[iOS]应用内支付(内购)的个人开发过程及坑!

ios内购支付-支付宝APP支付提现 这里用的是thinkphp5,但是支付这个其他框架或版本也可以参考,只需要改下写法即可。 阅读详情

本文会给大家详细介绍iOS内购,这是本人16年5月底的开发过程,希望对看完此篇文章的人有所帮助。
本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统。
部分地方直接摘自网络,基本上是我的逻辑,省时省心省力。

一. 创建测试App

首先你需要登录 App的ItunesConnection,你会看到如下界面
这里写图片描述

简单的介绍一下这几个选项
1.我的App主要用于管理自己的App应用,例如编辑资料,上架,下架等。
2.销售和趋势主要是来查看App在各个平台的下载量,收入等方面数据,里面有曲线图等图文结合的方式给我们参考。
3.付款和财务报告显示的是你的收入以及付款等相关信息。
4.iAd主要是跟广告有关,开发者可以登录到Workbench,通过iAd对应用的广告进行控制。
5.用户和职能用于生成相应账号,例如苹果沙河测试账号。
6.协议,税务和银行业务则是你银行相关账户的信息设置。
在这里我们选择第一个选项,我的App, 然后点击左上角的加号,新建一个用来测试用的App。
这里写图片描述

点新建 App,会出现新建窗口;
这里写图片描述

在这里有几个需要填写的地方,名称自己取,平台IOS,语言选择了简体中文,套装ID也就是你的Bundle Identifier,需要你在Certificates页面 申请BundleID,SKU可以理解为用户看一看到的唯一标示,会体现在你的app的App Store的链接中。

二.添加内购

App创建好之后,我们打开创建的App,在左上角选择功能,会看到左侧的App 内购买项目。我们点击右下角的加号,为App添加内购项目。
这里写图片描述

之后我们会看到类型的选项,如下图

这里写图片描述

官方的注释写的很清楚了,只在这里简单的说下前两种:
- 消耗型项目 就像你玩游戏需要买金币,买钻石等,只要花钱就可以无限次的购买
- 非消耗型项目 就像你在App Store购买App,买了一次之后就不用再买第二次,你拥有永久使用权。
在我们的app中,是充值会员,所以选择的是第一种,可以无限次购买。

这里写图片描述

这里有几个选项,需要填写商品名称,产品ID以及价格等级,简单说明一下
1. 商品名称根据你的消费道具的实际意义来说明,比如“100颗宝石”,“100金币”等。
2. 产品ID是比较重要的,由项目自定义,只要唯一即可,因为测试,我在这里随便填写的123,在实际应用中,一定要认真填写。
3. 价格等级的话“查看价格表”中有对应的说明,可以对照着表中每个国家的货币价格与等级来选择
接下来是语言选择,和上传快照如下图

这里写图片描述

点击添加语言,填写名称和描述,这里我们依然选择简体中文,如下

这里写图片描述

审核备注,根据实际情况填写,可以不填。而下面的屏幕快照,则是商品图片,以像素为单位,最低尺寸为321,390,尺寸需求如下图,上传即可。

这里写图片描述

到这里为止, 我们的内购项目则添加完成。接下来则是测试阶段了。

三.申请沙盒测试账号(用来测试购买项目)

这个账号,是利用苹果的沙盒测试环境来模拟AppStore的购买流程,你肯定不会想要用真实RMB去购买测试吧?
首先我们回到iTunes Connect中,在这里我们选择用户和职能。

这里写图片描述

然后在上面的第三个选项沙箱技术测试员中点击加号,添加测试员。

这里写图片描述

在信息填写页面只简单说两句。
所有信息都可以随意填写,不用管是否真实。
App Store地区选择,一定要选对,它对应的是你创建的App的地区, 你App是中国的话, 在这里我们依然选择中国。
此账号只能用来测试,不要在正式的appstore上使用
填写完毕,点击保存后,我们则生成一个测试账号,当然这个账号是可以随时删除和添加的。

这里写图片描述

之后终于到了写代码的时候了,点开你的Xcode创建你的项目!
大部分代码都可以在.m文件中实现。

<code class="hljs objectivec has-numbering"><span class="hljs-preprocessor">#import <span class="hljs-title">"ViewController.h"</span></span>
<span class="hljs-preprocessor">#import <span class="hljs-title"><StoreKit/StoreKit.h></span></span>
<span class="hljs-preprocessor">#import <span class="hljs-title">"SVProgressHUD.h"</span></span>

<span class="hljs-class"><span class="hljs-keyword">@interface</span> <span class="hljs-title">ViewController</span> ()<<span class="hljs-title">SKPaymentTransactionObserver</span>,<span class="hljs-title">SKProductsRequestDelegate</span>></span>
<span class="hljs-keyword">@property</span> (<span class="hljs-keyword">nonatomic</span>,copy) <span class="hljs-built_in">NSString</span> *currentProId;

<span class="hljs-keyword">@end</span>

<span class="hljs-class"><span class="hljs-keyword">@implementation</span> <span class="hljs-title">ViewController</span></span>

- (<span class="hljs-keyword">void</span>)viewDidLoad {
    [<span class="hljs-keyword">super</span> viewDidLoad];

    <span class="hljs-built_in">UIButton</span> *button = [<span class="hljs-built_in">UIButton</span> buttonWithType:UIButtonTypeCustom];
    button<span class="hljs-variable">.frame</span> = CGRectMake(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-number">100</span>);
    button<span class="hljs-variable">.backgroundColor</span> = [<span class="hljs-built_in">UIColor</span> greenColor];
    [button setTitle:@<span class="hljs-string">"6元"</span> forState:UIControlStateNormal];
    [button addTarget:<span class="hljs-keyword">self</span> action:<span class="hljs-keyword">@selector</span>(btnClick:) forControlEvents:UIControlEventTouchDown];
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.view</span> addSubview:button];
}


- (<span class="hljs-keyword">void</span>)btnClick:(<span class="hljs-built_in">UIButton</span> *)button
{
    [[SKPaymentQueue defaultQueue] addTransactionObserver:<span class="hljs-keyword">self</span>];
    _currentProId = @<span class="hljs-string">"123"</span>;
    <span class="hljs-keyword">if</span>([SKPaymentQueue canMakePayments]){
        [<span class="hljs-keyword">self</span> requestProductData:product];
    }<span class="hljs-keyword">else</span>{
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"不允许程序内付费"</span>);
    }
}

<span class="hljs-comment">//去苹果服务器请求商品</span>
- (<span class="hljs-keyword">void</span>)requestProductData:(<span class="hljs-built_in">NSString</span> *)type{
    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"-------------请求对应的产品信息----------------"</span>);

    [SVProgressHUD showWithStatus:<span class="hljs-literal">nil</span> maskType:SVProgressHUDMaskTypeBlack];

    <span class="hljs-built_in">NSArray</span> *product = [[<span class="hljs-built_in">NSArray</span> alloc] initWithObjects:type,<span class="hljs-literal">nil</span>];

    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request<span class="hljs-variable">.delegate</span> = <span class="hljs-keyword">self</span>;
    [request start];

}

<span class="hljs-comment">//收到产品返回信息</span>
- (<span class="hljs-keyword">void</span>)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"--------------收到产品反馈消息---------------------"</span>);
    <span class="hljs-built_in">NSArray</span> *product = response<span class="hljs-variable">.products</span>;
    <span class="hljs-keyword">if</span>([product count] == <span class="hljs-number">0</span>){
        [SVProgressHUD dismiss];
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"--------------没有商品------------------"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"productID:%@"</span>, response<span class="hljs-variable">.invalidProductIdentifiers</span>);
    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"产品付费数量:%lu"</span>,(<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span>)[product count]);

    SKProduct *p = <span class="hljs-literal">nil</span>;
    <span class="hljs-keyword">for</span> (SKProduct *pro in product) {
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"%@"</span>, [pro description]);
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"%@"</span>, [pro localizedTitle]);
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"%@"</span>, [pro localizedDescription]);
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"%@"</span>, [pro price]);
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"%@"</span>, [pro productIdentifier]);

        <span class="hljs-keyword">if</span>([pro<span class="hljs-variable">.productIdentifier</span> isEqualToString:_currentProId]){
            p = pro;
        }
    }

    SKPayment *payment = [SKPayment paymentWithProduct:p];

    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"发送购买请求"</span>);
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

<span class="hljs-comment">//请求失败</span>
- (<span class="hljs-keyword">void</span>)request:(SKRequest *)request didFailWithError:(<span class="hljs-built_in">NSError</span> *)error{
    [SVProgressHUD showErrorWithStatus:@<span class="hljs-string">"支付失败"</span>];
    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"------------------错误-----------------:%@"</span>, error);
}

- (<span class="hljs-keyword">void</span>)requestDidFinish:(SKRequest *)request{
    [SVProgressHUD dismiss];
    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"------------反馈信息结束-----------------"</span>);
}
<span class="hljs-comment">//沙盒测试环境验证</span>
<span class="hljs-preprocessor">#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"</span>
<span class="hljs-comment">//正式环境验证</span>
<span class="hljs-preprocessor">#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"</span>
<span class="hljs-comment">/**
 *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题
 *
 */</span>
-(<span class="hljs-keyword">void</span>)verifyPurchaseWithPaymentTransaction{
    <span class="hljs-comment">//从沙盒中获取交易凭证并且拼接成请求体数据</span>
    <span class="hljs-built_in">NSURL</span> *receiptUrl=[[<span class="hljs-built_in">NSBundle</span> mainBundle] appStoreReceiptURL];
    NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];

    <span class="hljs-built_in">NSString</span> *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];<span class="hljs-comment">//转化为base64字符串</span>

    <span class="hljs-built_in">NSString</span> *bodyString = [<span class="hljs-built_in">NSString</span> stringWithFormat:@<span class="hljs-string">"{\"receipt-data\" : \"%@\"}"</span>, receiptString];<span class="hljs-comment">//拼接请求数据</span>
    NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];


    <span class="hljs-comment">//创建请求到苹果官方进行购买验证</span>
    <span class="hljs-built_in">NSURL</span> *url=[<span class="hljs-built_in">NSURL</span> URLWithString:SANDBOX];
    NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
    requestM<span class="hljs-variable">.HTTPBody</span>=bodyData;
    requestM<span class="hljs-variable">.HTTPMethod</span>=@<span class="hljs-string">"POST"</span>;
    <span class="hljs-comment">//创建连接并发送同步请求</span>
    <span class="hljs-built_in">NSError</span> *error=<span class="hljs-literal">nil</span>;
    NSData *responseData=[<span class="hljs-built_in">NSURLConnection</span> sendSynchronousRequest:requestM returningResponse:<span class="hljs-literal">nil</span> error:&error];
    <span class="hljs-keyword">if</span> (error) {
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"验证购买过程中发生错误,错误信息:%@"</span>,error<span class="hljs-variable">.localizedDescription</span>);
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-built_in">NSDictionary</span> *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:<span class="hljs-literal">nil</span>];
    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"%@"</span>,dic);
    <span class="hljs-keyword">if</span>([dic[@<span class="hljs-string">"status"</span>] intValue]==<span class="hljs-number">0</span>){
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"购买成功!"</span>);
        <span class="hljs-built_in">NSDictionary</span> *dicReceipt= dic[@<span class="hljs-string">"receipt"</span>];
        <span class="hljs-built_in">NSDictionary</span> *dicInApp=[dicReceipt[@<span class="hljs-string">"in_app"</span>] firstObject];
        <span class="hljs-built_in">NSString</span> *productIdentifier= dicInApp[@<span class="hljs-string">"product_id"</span>];<span class="hljs-comment">//读取产品标识</span>
        <span class="hljs-comment">//如果是消耗品则记录购买数量,非消耗品则记录是否购买过</span>
        <span class="hljs-built_in">NSUserDefaults</span> *defaults=[<span class="hljs-built_in">NSUserDefaults</span> standardUserDefaults];
        <span class="hljs-keyword">if</span> ([productIdentifier isEqualToString:@<span class="hljs-string">"123"</span>]) {
            <span class="hljs-keyword">int</span> purchasedCount=[defaults integerForKey:productIdentifier];<span class="hljs-comment">//已购买数量</span>
            [[<span class="hljs-built_in">NSUserDefaults</span> standardUserDefaults] setInteger:(purchasedCount+<span class="hljs-number">1</span>) forKey:productIdentifier];
        }<span class="hljs-keyword">else</span>{
            [defaults setBool:<span class="hljs-literal">YES</span> forKey:productIdentifier];
        }
        <span class="hljs-comment">//在此处对购买记录进行存储,可以存储到开发商的服务器端</span>
    }<span class="hljs-keyword">else</span>{
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"购买失败,未通过验证!"</span>);
    }
}
<span class="hljs-comment">//监听购买结果</span>
- (<span class="hljs-keyword">void</span>)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(<span class="hljs-built_in">NSArray</span> *)transaction{
    <span class="hljs-keyword">for</span>(SKPaymentTransaction *tran in transaction){
        <span class="hljs-keyword">switch</span> (tran<span class="hljs-variable">.transactionState</span>) {
            <span class="hljs-keyword">case</span> SKPaymentTransactionStatePurchased:{
                <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"交易完成"</span>);
                <span class="hljs-comment">// 发送到苹果服务器验证凭证</span>
                [<span class="hljs-keyword">self</span> verifyPurchaseWithPaymentTransaction];
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];

            }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> SKPaymentTransactionStatePurchasing:
                <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"商品添加进列表"</span>);

                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> SKPaymentTransactionStateRestored:{
                <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"已经购买过商品"</span>);

                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
            }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> SKPaymentTransactionStateFailed:{
                <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"交易失败"</span>);
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                [SVProgressHUD showErrorWithStatus:@<span class="hljs-string">"购买失败"</span>];
            }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">default</span>:
                <span class="hljs-keyword">break</span>;
        }
    }
}

<span class="hljs-comment">//交易结束</span>
- (<span class="hljs-keyword">void</span>)completeTransaction:(SKPaymentTransaction *)transaction{
    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"交易结束"</span>);

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


- (<span class="hljs-keyword">void</span>)dealloc{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:<span class="hljs-keyword">self</span>];
}

- (<span class="hljs-keyword">void</span>)didReceiveMemoryWarning {
    [<span class="hljs-keyword">super</span> didReceiveMemoryWarning];
    <span class="hljs-comment">// Dispose of any resources that can be recreated.</span>
}

<span class="hljs-keyword">@end</span></code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li><li>123</li><li>124</li><li>125</li><li>126</li><li>127</li><li>128</li><li>129</li><li>130</li><li>131</li><li>132</li><li>133</li><li>134</li><li>135</li><li>136</li><li>137</li><li>138</li><li>139</li><li>140</li><li>141</li><li>142</li><li>143</li><li>144</li><li>145</li><li>146</li><li>147</li><li>148</li><li>149</li><li>150</li><li>151</li><li>152</li><li>153</li><li>154</li><li>155</li><li>156</li><li>157</li><li>158</li><li>159</li><li>160</li><li>161</li><li>162</li><li>163</li><li>164</li><li>165</li><li>166</li><li>167</li><li>168</li><li>169</li><li>170</li><li>171</li><li>172</li><li>173</li><li>174</li><li>175</li><li>176</li><li>177</li><li>178</li><li>179</li><li>180</li><li>181</li><li>182</li><li>183</li><li>184</li><li>185</li><li>186</li><li>187</li><li>188</li><li>189</li><li>190</li><li>191</li><li>192</li><li>193</li><li>194</li><li>195</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li><li>123</li><li>124</li><li>125</li><li>126</li><li>127</li><li>128</li><li>129</li><li>130</li><li>131</li><li>132</li><li>133</li><li>134</li><li>135</li><li>136</li><li>137</li><li>138</li><li>139</li><li>140</li><li>141</li><li>142</li><li>143</li><li>144</li><li>145</li><li>146</li><li>147</li><li>148</li><li>149</li><li>150</li><li>151</li><li>152</li><li>153</li><li>154</li><li>155</li><li>156</li><li>157</li><li>158</li><li>159</li><li>160</li><li>161</li><li>162</li><li>163</li><li>164</li><li>165</li><li>166</li><li>167</li><li>168</li><li>169</li><li>170</li><li>171</li><li>172</li><li>173</li><li>174</li><li>175</li><li>176</li><li>177</li><li>178</li><li>179</li><li>180</li><li>181</li><li>182</li><li>183</li><li>184</li><li>185</li><li>186</li><li>187</li><li>188</li><li>189</li><li>190</li><li>191</li><li>192</li><li>193</li><li>194</li><li>195</li></ul>

在这里需要注意几点,
1. 代码中的_currentProId所填写的是你的购买项目的的ID,这个和第二步创建的内购的productID要一致;本例中是 123。
2. 在监听购买结果后,一定要调用[[SKPaymentQueue defaultQueue] finishTransaction:tran];来允许你从支付队列中移除交易。
3. 沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。
4. 请务必使用真机来测试,一切以真机为准。
5. 项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。
6. 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号
7. 二次验证,请注意区分宏, 测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的时候需要判断返回Status Code决定是否去沙盒进行二次验证,为了线上用户的使用,验证的顺序肯定是先验证正式环境,此时若返回值为21007,就需要去沙盒二次验证,因为此购买的是在沙盒进行的。

附:苹果支付错误目录

Status CodeDescription
21000The App Store could not read the JSON object you provided.
21002The data in the receipt-data property was malformed or missing.
21003The receipt could not be authenticated.
21004The shared secret you provided does not match the shared secret on file for your account.Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
21005The receipt server is not currently available.
21006This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response.Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
21007This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.
21008This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.

参考:
1. iOS开发之内购-AppStore;
2. 苹果官方文档

文章原地址:http://blog.youkuaiyun.com/darling_shadow/article/details/51538267

Objective-C iOS StoreKit 原生内购订阅封装,含Demo Objective-C iOS StoreKit 原生内购订阅代码封装,语言是Objective-C,内含demo,可以二次封装给其他开发工具使用. 支持Unity3D,cocos,xamarin...等开发工具进行二次封装使用. 立即下载

相关推荐

ios 内购代码 In App Rage objective c代码。最新版的已是swift版本。

ios 内购代码 测试的代码。

iOS开发:Objective-C精确的货币计算

在iOS开发中,和货币价格计算相关的,需要注意计算精度的问题。即使只是两位小数,也会出现误差。使用float类型运算,是完全不够的。经过一番测试,最后选择使用系统提供的API的NSDecimalNumber来进行更好的解决。 作为一个对外的库,鉴于版本延续,我们保留对外的flaot的类型,不改变接口,选择进行内部适配。 以下是一些基本的测试, 原始数据 float a =

随记 5019

搭建Java集成开发环境IntelliJ IDEA

package net.huawei.p01.t02;/** * 功能:输出一条信息 * 作者:华卫 * 日期:2022年03月24日 */ public class HelloWorld { public static void main(String[] args) { // arguments System.out.println("Hello Java World~"); } }

qq_67308063的博客 475

【IAP支付之三】苹果IAP安全支付与防范 receipt收据验证

这里网上的朋友已经介绍的很详细了,具体的链接已经无法找到了。 这里主要说几点本人在开发中遇到的问题: 1.漏单必须要处理,玩家花RMB购买的东西却丢失了,是绝对不能容忍的。所谓的漏单就是玩家已经正常付费,却没有拿到该拿的道具。 解决:只要购买成功,便将购买记录(receipt等账单信息)保存下来,然后将账单信息传送给我们游戏服务器,游戏服务器获得账单后,和苹果服务器验证,账单...

weixin_30625691的博客 443

FUEL算法解读(一)——框架解读,思维导图可以很清晰看懂算法启动框架,已更新完,图太大放不上来链接在文末

一图看懂FUEL算法节点框架!!!

weixin_43793717的博客 2785

CHB-MIT波士顿儿童医院癫痫EEG脑电数据处理(一)

CHB-MIT癫痫EEG脑电数据处理CHB-MIT头皮脑电图数据介绍CHB-MIT头皮脑电图数据下载CHB-MIT头皮脑电图数据处理提取原始数据滤波(0.1~50hz)数据分块(时间窗口) CHB-MIT头皮脑电图数据介绍 该数据库收集自波士顿儿童医院,其中包括患有难治性癫痫发作的儿科患者的脑电图记录。停用抗癫痫药物后最多持续几天对受试者进行监测,以表征其癫痫发作并评估其对手术干预的候选资格。 从22位受试者中收集了23个病例的记录(5例男性,3至22岁; 17例女性,1.5至19岁)。 (案例chb21是

AI_DOG的博客 1万+

[ObjectC]分享到支付宝iOS接入

定义前言引入SDK头文件: #import "APOpenAPI.h" //支付宝 #define ZFB_APP_ID @"2017111509948730" //继承用于回调 @interface AppController () @end官方文档说明重新函数- (BOOL)application:(UIApplication *)application didFinishLaunch

超越 1524

Open3D——RANSAC 三维点云空间直线拟合【2025最新版】

使用RANSAC算法在点云数据中拟合三维空间直线的python代码实现。博客长期更新,本文最新更新时间为:2025年3月23日。

点云侠的博客 7354

苹果支付的实现

由于app经常需要用到支付功能,然而ios用户,是无法用支付宝、微信进行支付,这时候只能用到苹果支付。苹果支付是苹果公司推出的一种在线支付方式,用户可以通过苹果支付购买应用、内购道具等等。

浮游的博客 4851

基于YOLOv8的建筑材料分类系统:从数据集构建到UI界面实现

建筑材料分类数据集需要包含各种常见建筑材料的图像,并对每种材料进行标注。砖块(Brick)混凝土(Concrete)钢筋(Rebar)木材(Wood)瓷砖(Tile)玻璃(Glass)钢材(Steel)塑料(Plastic)石膏板(Drywall)绝缘材料(Insulation)如果没有找到合适的公开数据集,可以构建自己的数据集。使用智能手机或专业相机拍摄各种建筑材料的照片确保在不同光照条件、角度和背景下拍摄每类材料至少收集300-500张图像。

YOLO 489

手把手教你学AI GPU的UMD驱动软件开发专栏简介

通过本专栏的学习,您将掌握UMD的核心概念、开发环境搭建、API实现、内存管理和同步技术,以及在实际AI场景中的应用。通过本专栏的学习,您将不仅掌握UMD驱动开发的核心技术,还将能够将这些技术应用到实际的AI应用场景中,为您的AI应用提供更高效的硬件加速支持。无论您是想开发自己的AI加速框架,还是希望深入理解现有AI加速库的工作原理,本专栏都将为您提供宝贵的知识和实践经验。:本专栏不仅讲解UMD驱动的核心概念,还通过精心设计的实战案例,引导您从零开始构建UMD驱动,真正掌握AI GPU驱动开发的技能。

MHD0815的博客 113

iOS应用内支付(内购)的个人开发过程及坑!

本文会给大家详细介绍iOS内购,这是本人16年5月底的开发过程,希望对看完此篇文章的人有所帮助。  本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统。  部分地方直接摘自网络,基本上是我的逻辑,省时省心省力。 一. 创建测试App 首先你需要登录 App的ItunesConnection,你会看到如下界面  简单的介绍一下这

chenyxh2005的专栏 2669

GPU 资源池化云平台解决方案​

定义与特点Kubernetes是一个开源的容器编排和管理平台,通过提供自动化和声明式的方式来部署、扩展、维护和运行应用程序。它支持跨多个主机和集群部署容器,确保应用程序的高可用性和可扩展性。Kubernetes提供了丰富的功能和插件,允许用户根据自己的需求进行定制和扩展。架构与组件控制平面(Control Plane):负责集群的决策(如调度)和集群状态的检测与响应。

ZhangJiqun&191cm&85kg 1161

iOS应用内支付(IAP)的那些坑

iOS应用内支付(IAP)的那些坑,注意事项

新博客地址:blog.songchunmin.com 2696

YOLOv11涨点改进 | 全网独家创新,注意力改进篇 | TGRS 2025 | 引入APCM自适应像素级协同机制,动态增强目标区域、抑制背景噪声,助力YOLOv11做红外小目标检测有效涨点

本文提出了一种基于APCM模块改进的YOLOv11网络模型,通过像素级局部-全局协同建模增强目标特征并抑制背景噪声。APCM模块采用轻量化设计,无需复杂参数,能有效提升小目标检测性能。文章详细介绍了APCM的原理、优势及其在红外小目标检测中的应用,包括模块结构、核心代码实现和YAML配置文件修改方法。实验结果表明,该方法在NUAA-SIRST和IRSTD-1K数据集上优于现有技术,显著提高了复杂场景下的检测精度与鲁棒性。

Ai缝合怪、助力小伙伴高效发论文 116

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

iOS 内购 分享

要识别用户,首先就是要选择一个标识符,通过这个标识符来识别这个用户的设备(而不是用户),这个标识符要能够保证一个设备上返回的值是一样的,并且在其他设备上不会出现相同的值。 在iOS7之前,曾经有过很多方法来识别用户的设备,从最原始的设备udid、mac地址,到被各广告统计平台广泛使用的开源方案OpenUDID。 但随着AppStore开始拒绝接受使用udid的应用,到mac地址在iOS

u013321328的专栏 2458

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

iOS中 支付宝钱包详解/第三方支付 韩俊强的博客

一、在app中成功完成支付宝支付的过程 1、申请支付宝合作。参考网址: https://b.alipay.com/order/productDetail.htm?productId=2012120700377310 这是使用支付宝支付的第一步,合作申请通过之后,会集成一个开发文件。即使不申请也可以下载,只是demo中会缺少几个必须的参数: 在demo中的 PartnerConfig.h 文件中可以看到 2、生成RSA 这个需要在windows系统下生成,开

韩俊强的博客 6582

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

【iOS内购支付,无需插件】Uniapp拉起苹果内购支付注意事项、实现步骤以及踩过的坑(手把手教程)

苹果内购支付,通过uniapp结合vue2进行实现

菜菜的博客 1万+
上一篇: CGContextTranslateCTM
下一篇: UIScrollerView的scrollRectToVisible方法
光速之年
博客等级 码龄12年 31粉丝 97原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值