iOS开发支付篇——内购(IAP)详解

1
<em>内购所需要的资料整理总结,史上最完整的,哈哈哈哈哈哈</em>

思维导图

分享图片

重点总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
1.获取内购列表(从App内读取或从自己服务器读取)
2.App Store请求可用的内购列表
3.向用户展示内购列表
4.用户选择了内购列表,再发个购买请求,收到购买完成的回调(购买完成后会把钱打给申请内购的银行卡内)
5.购买流程结束后, 向服务器发起验证凭证以及支付结果的请求
6.自己的服务器将支付结果信息返回给前端并发放虚拟产品
7.服务端的工作比较简单,分4步:
  7.1.接收ios端发过来的购买凭证。
  7.2.判断凭证是否已经存在或验证过,然后存储该凭证。
  7.3.将该凭证发送到苹果的服务器验证,并将验证结果返回给客户端。
    7.4.如果需要,修改用户相应的会员权限。
    7.5.考虑到网络异常情况,服务器的验证应该是一个可恢复的队列,如果网络失败了,应该进行重试。
简单来说就是将该购买凭证用Base64编码,然后POST给苹果的验证服务器,苹果将验证结果以JSON形式返回。

一、使用注意事项及遇到的坑

  1.使用注意

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

  2.获取不到商品信息

1
2
3
4
5
6
1.确定配置环节正确。
2.确定是真机测试且手机没有越狱。
3.确定内购商品添加到了需要内购功能的App中。
4.确定当前运行的App的Bundle ID和后台配置的App的Bundle ID是一致的。
5.可以尝试先删除旧App,再重新编译生成新的,避免新App未覆盖错误。
6.这里要提一点,沙盒的测试账号和你请求商品信息没有关系。请求商品信息的流程是,你在后台配置好了内购商品,并且将其添加到了需要集成内购功能的App中,然后你请求商品。请求到商品后的流程是这样的,苹果系统会自动弹出登录框让你登录账号。然后根据提示操作进行购买,这里的账号就是你配置的沙盒测试账号。

二、为什么要使用内购?(why)和内购是什么?(what)

1
2
3
4
  1.如果你购买的商品,是在本app中使用和消耗的,就一定要用内购,否则会被拒绝上线,例如:游戏币,在线书籍
app中使用的道具等。本例中,就是直播中你用来打赏用的金币,那东西可就属于消耗型的。
  2.如果是直接购买商城之类的快递包邮的那些东东,那就直接调用支付宝,微信啦,之类的三方支付就好了,淘宝,京东都玩过哈!
比较坑的一点就是,内购的话,还要和苹果3/7分成,那就可以说,充值相同的钱,相对来说,iOS是比安卓亏的!

三、怎样使用内购?(how)

1
2
3
4
5
6
7
8
9
10
  1.使用内购需要哪些资料<br>     1张visa银行卡,appid,1张银行卡与苹果三七分打钱用
    (1)协议、税务和银行业务
       联系人信息:(appid账号人)姓名,邮箱,电话号码,地址(城市、具体街道分行写)
       visa银行卡信息:开户行,开户行所在地址,开户行的邮政编码,开户行持有人卡号,开户行持有人姓名
       税务信息:1.会问你是不是美国居民选择NO.  2. 有没有在美国从事商业性活动,选择NO. 之后填写个人或组织名称,所在国家,受益方式(独立开发者选择个人),居住地址,邮寄地址,声明人,头衔
        (2)内购产品id的配置 (开发人员配置)
            如果是金币或其它消耗品的产品的话用消耗性型项目,参考名称(内购项目,比如金币100),产品id,定价信息,使用内购的快照,显示名称,描述。
    (3)用户职能
      测试员:添加水箱测试员及沙箱账号,水箱测试账号不能是正常使用的appid账号,直接使用一个没有注册过的邮箱账号即可。
姓名,测试账号密码,appstore地区(必须填对)。

四、操作流程图解与代码

  1.创建app后填写用户信息

分享图片

1
2
3
4
5
6
7
8
9
10
11
功能简介 :
     1.我的App主要用于管理自己的App应用,例如编辑资料,上架,下架等。
     2.销售和趋势主要是来查看App在各个平台的下载量,收入等方面数据,里面有曲线图等图文结合的方式给我们参考。
     3.付款和财务报告显示的是你的收入以及付款等相关信息。
     4.iAd主要是跟广告有关,开发者可以登录到Workbench,通过iAd对应用的广告进行控制。
     5.用户和职能用于生成相应账号,例如苹果沙盒测试账号。
     6.协议,税务和银行业务则是你银行相关账户的信息设置。
流程
  1.注册app,填写协议、税务和银行业务
    注册app,需要设置Bundle identifier,此个步骤这里就不在写了
   填写协议、税务和银行业务

  分享图片

1
2
3
4
5
6
7
8
<strong>选择申请合同类型</strong>
页面内容:
Request Contracts(申请合同)
Contracts In Effect(已生效合同)。
合同类型:
iOS Free Application(免费应用合同)
iOS Paid Application(付费应用合同)
iAd App NetNetwork(广告合同)

  1.申请iOS Paid Application合同

分享图片

分享图片

 2. 设置协议税务、银行卡信息

1
2
当我们点击申请iOS Paid Application合同后,该合同的状态会变成如下的样子,我们可以看到其中Status为Contact, Bank, Pending Tax,
意思是联系方式、银行和税务信息没有填写。

 分享图片

 2.1设置联系人信息

1
2
3
4
5
6
7
如果你没有添加过联系人,你需要通过Add New Contact按钮来添加一个新的联系人。然后指定联系人的职务,职务如下:
Senior Management:高管
Financial:财务
Technical:技术支持
Legal:法务
Marketing:市场推广
如果你是独立开发者,可以全部填你自己一个人。

  新增联系人

分享图片

  通过新增或之前增加的联系人设置高管等信息

  分享图片

待完成后点击Done,返回后状态会变成Edit状态

 2.2设置银行卡信息(可以通过银行名称和地址直接上网查询CNAPS Code号,不要问我上那查)

 

分享图片

分享图片  

确认银行卡信息

分享图片

分享图片

 

 2.3设置税务信息(1.是美国税务,只需要这个就行,后面的澳大利亚和日本的和我们没的关系)

分享图片

1
选择U.S Tax Forms,选择后会问你两个问题,第一个问题如下:询问你是否是美国居民,有没有美国伙伴关系或者美国公司,如果没有直接选择No。

分享图片

1
接下来第二个问题如下:询问你有没有在美国的商业性活动,没有也直接选No。

分享图片

然后填写税务信息

1
2
3
4
5
6
7
8
9
10
然后填写你的税务信息,包括以下几点:
 
Individual or Organization Name:个人或者组织名称
Country of incorporation: 所在国家
Type of Beneficial Owner:受益方式,独立开发者选个人
Permanent Residence:居住地址
Mailing address:邮寄地址
Name of Person Making  this  Declaration:声明人
Title:头衔
当你填写完所有资料后,合同状态就会变成Processing,笔者凌晨1点左右提交,下午就通过了。

  具体填写见下图(以下是确认税务信息图)

分享图片

  填写完成后效果

分享图片

 3.配置内购产品ID

1
2
完成以上操作,并且苹果审核完毕之后,就可以配置内购产品了。
登录 iTunesConnect -->我的App 模块找到需要内购的App,最后找到页面如下:

  分享图片

分享图片

分享图片

分享图片

1
2
3
4
5
填写沙箱测试员和添加内购产品注意事项
1、邮箱必须是没有注册或者说关联过appstore的邮箱。
2、密码必须有一个是大写字母有一个是小写字母(苹果规定的,理解)。
3、内购屏幕截图规格必须是312*290,且最低分辨率是72ppi。
4、内购的价格是苹果规定的不能自定义(坑啊)。

4.增加内购测试账号

     4.1 内购测试之前准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1、什么是内购测试账号(what)及为什么使用内购测试账号(why)?
     iOS应用里面用到了苹果应用内付费(IAP)功能,在项目上线前一定要进行功能测试。测试肯定是需要的,何况这个跟money有关。。。开发完成了之后,如何进行测试呢?难道我测试个内购功能要自己掏钱?就算是也是公司掏钱,但是苹果要吃掉3成的啊,想想如果是99刀的商品,点下购买的时候心里都有点发慌。。。
苹果当然没这么坑了,测试内购,苹果提供了沙盒账号(也叫沙箱账号)的方式。这个沙箱账号其实是虚拟的AppleID,在开发者账号后台的iTune Connect上配置了之后就能使用沙盒账号测试内购,有了沙盒账号,就能体验一把土豪的感觉了,游戏钻石什么的随便充,反正不用我的钱。
     注意:你可以把沙盒账号看做是一个虚拟的AppleID,这个AppleID只有进行内购测试的功能。重要,重要,重要,这个虚拟的账号只能在自己的测试号中使用,如果在其它地方如appstore使用的话会提示账号无效之类的话。   
 
2、如何使用内购测试账号(how)?
     2.1作用内购账号的前提
1)内购的商品ID,价格等相关信息已经录入到开发者后台了(不然那你买什么)
2)开发者后台已经创建好沙盒测试账号了(下面我们会将如何创建)
3)你要有一部真机(iPhone或iPad都行,别用模拟器就好。而且不能是越狱机)
4)bundleID别搞错了,开发者账号、证书、bundleID要一致
5)如果你是第一次在这个开发者账号上集成内购功能,
请先将iTune Connect上的税务协议都填写好,否则内购时会发现商品ID无效。
  重要,如果不添加税务协议会报错,找不到商品。

  选择用户和职能就是在协议、税务和银行业务左侧

分享图片

分享图片

   4.2内购测试开始

1
2
3
4
5
6
7
8
1.在iPhone上安装测试包(必须是打包签名证书或者develop签名证书打的包,不能是从App Store上下载的)
2.退出iPhone的App Store账号(因为我们需要使用沙盒账号登录)。
操作方法一:打开App Store应用首页滑到最下方--选中AppleID--注销
操作方法二:设置--iTunes Store与App Store--选中AppleID--注销
3.不能用沙盒测试帐号来登录appstore官网或去其它已上线平台去支付详见图4.21
4.运行下面代码的demo,传入你创建的产品id(就是在app iTunes Connect ->我的app ->功能 ->app内购买项目添加的商品),点击充值跳转开始购买详见图4.22
5.再次购买时需要输入测试沙盒账号密码(在用户和职能->沙箱技术测试员创建的测试账号)详见图4.23
6.购买成功反馈详见图4.24

  4.21 图

分享图片

  4.22 图

分享图片

  4.23 图

分享图片

  4.24 图

 

5.代码及业务逻辑

   业务逻辑

  1. 获取内购列表(从App内读取或从自己服务器读取)
  2. App Store请求可用的内购列表
  3. 向用户展示内购列表
  4. 用户选择了内购列表,再发个购买请求,收到购买完成的回调(购买完成后会把钱打给申请内购的银行卡内)
  5. 购买流程结束后, 向服务器发起验证凭证以及支付结果的请求
  6. 自己的服务器将支付结果信息返回给前端并发放虚拟产品
  7. 服务端的工作比较简单,分4步:

    1. 接收ios端发过来的购买凭证。
    2. 判断凭证是否已经存在或验证过,然后存储该凭证。
    3. 将该凭证发送到苹果的服务器验证,并将验证结果返回给客户端。
    4. 如果需要,修改用户相应的会员权限。

    考虑到网络异常情况,服务器的验证应该是一个可恢复的队列,如果网络失败了,应该进行重试。

    简单来说就是将该购买凭证用Base64编码,然后POST给苹果的验证服务器,苹果将验证结果以JSON形式返回。

分享图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
 代码如下 :
/*注意事项:
1.沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。
2.请务必使用真机来测试,一切以真机为准。
3.项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。
4.如果是你自己的设备上已经绑定了自己的AppleID账号请先注销掉,否则你哭爹喊娘都不知道是怎么回事。
5.订单校验 苹果审核app时,仍然在沙盒环境下测试,所以需要先进行正式环境验证,如果发现是沙盒环境则转到沙盒验证。
识别沙盒环境订单方法:
  1.根据字段 environment = sandbox。
  2.根据验证接口返回的状态码,如果status=21007,则表示当前为沙盒环境。
  苹果反馈的状态码:
  21000App Store无法读取你提供的JSON数据
  21002 订单数据不符合格式
  21003 订单无法被验证
  21004 你提供的共享密钥和账户的共享密钥不一致
  21005 订单服务器当前不可用
  21006 订单是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
  21007 订单信息是测试用(sandbox),但却被发送到产品环境中验证
  21008 订单信息是产品环境中使用,但却被发送到测试环境中验证
  */
 
#import <Foundation/Foundation.h>
 
typedef  enum  {
     SIAPPurchSuccess = 0,        // 购买成功
     SIAPPurchFailed = 1,         // 购买失败
     SIAPPurchCancle = 2,         // 取消购买
     SIAPPurchVerFailed = 3,      // 订单校验失败
     SIAPPurchVerSuccess = 4,     // 订单校验成功
     SIAPPurchNotArrow = 5,       // 不允许内购
}SIAPPurchType;
 
typedef  void  (^IAPCompletionHandle)(SIAPPurchType type,NSData *data);
 
 
@ interface  STRIAPManager : NSObject
+ (instancetype)shareSIAPManager;
//开始内购
- ( void )startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle;
@end
.m
#import "STRIAPManager.h"
#import <StoreKit/StoreKit.h>
@ interface  STRIAPManager()<SKPaymentTransactionObserver,SKProductsRequestDelegate>{
    NSString           *_purchID;
    IAPCompletionHandle _handle;
}
@end
@implementation STRIAPManager
 
#pragma mark - ??life cycle
+ (instancetype)shareSIAPManager{
     
     static  STRIAPManager *IAPManager = nil;
     static  dispatch_once_t onceToken;
     dispatch_once(&onceToken,^{
         IAPManager = [[STRIAPManager alloc] init];
     });
     return  IAPManager;
}
- (instancetype)init{
     self = [super init];
     if  (self) {
         // 购买监听写在程序入口,程序挂起时移除监听,这样如果有未完成的订单将会自动执行并回调 paymentQueue:updatedTransactions:方法
         [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
     }
     return  self;
}
 
- ( void )dealloc{
     [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
 
 
#pragma mark - ??public
- ( void )startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle{
     if  (purchID) {
         if  ([SKPaymentQueue canMakePayments]) {
             // 开始购买服务
             _purchID = purchID;
             _handle = handle;
             NSSet *nsset = [NSSet setWithArray:@[purchID]];
             SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
             request. delegate  = self;
             [request start];
         } else {
             [self handleActionWithType:SIAPPurchNotArrow data:nil];
         }
     }
}
#pragma mark - ??private
- ( void )handleActionWithType:(SIAPPurchType)type data:(NSData *)data{
#if DEBUG
     switch  (type) {
         case  SIAPPurchSuccess:
             NSLog( @"购买成功" );
             break ;
         case  SIAPPurchFailed:
             NSLog( @"购买失败" );
             break ;
         case  SIAPPurchCancle:
             NSLog( @"用户取消购买" );
             break ;
         case  SIAPPurchVerFailed:
             NSLog( @"订单校验失败" );
             break ;
         case  SIAPPurchVerSuccess:
             NSLog( @"订单校验成功" );
             break ;
         case  SIAPPurchNotArrow:
             NSLog( @"不允许程序内付费" );
             break ;
         default :
             break ;
     }
#endif
     if (_handle){
         _handle(type,data);
     }
}
#pragma mark - ??delegate
// 交易结束
- ( void )completeTransaction:(SKPaymentTransaction *)transaction{
   // Your application should implement these two methods.
     NSString * productIdentifier = transaction.payment.productIdentifier;
     NSString * receipt = [transaction.transactionReceipt base64EncodedString];
     if  ([productIdentifier length] > 0) {
         // 向自己的服务器验证购买凭证
     }
 
     [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}
 
// 交易失败
- ( void )failedTransaction:(SKPaymentTransaction *)transaction{
     if  (transaction.error.code != SKErrorPaymentCancelled) {
         [self handleActionWithType:SIAPPurchFailed data:nil];
     } else {
         [self handleActionWithType:SIAPPurchCancle data:nil];
     }
     
     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
 
- ( void )verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag{
     //交易验证
     NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
     NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
     
     if (!receipt){
         // 交易凭证为空验证失败
         [self handleActionWithType:SIAPPurchVerFailed data:nil];
         return ;
     }
     // 购买成功将交易凭证发送给服务端进行再次校验
     [self handleActionWithType:SIAPPurchSuccess data:receipt];
     
     NSError *error;
     NSDictionary *requestContents = @{
                                       @"receipt-data" : [receipt base64EncodedStringWithOptions:0]
                                       };
     NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                           options:0
                                                             error:&error];
     
     if  (!requestData) {  // 交易凭证为空验证失败
         [self handleActionWithType:SIAPPurchVerFailed data:nil];
         return ;
     }
     
     //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
     //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
     
     NSString *serverString =  @"https://buy.itunes.apple.com/verifyReceipt" ;
     if  (flag) {
         serverString =  @"https://sandbox.itunes.apple.com/verifyReceipt" ;
     }
     NSURL *storeURL = [NSURL URLWithString:serverString];
     NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
     [storeRequest setHTTPMethod: @"POST" ];
     [storeRequest setHTTPBody:requestData];
     
     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
     [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                            completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                                if  (connectionError) {
                                    // 无法连接服务器,购买校验失败
                                    [self handleActionWithType:SIAPPurchVerFailed data:nil];
                                else  {
                                    NSError *error;
                                    NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                                    if  (!jsonResponse) {
                                        // 苹果服务器校验数据返回为空校验失败
                                        [self handleActionWithType:SIAPPurchVerFailed data:nil];
                                    }
                                    
                                    // 先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器
                                    NSString *status = [NSString stringWithFormat: @"%@" ,jsonResponse[ @"status" ]];
                                    if  (status && [status isEqualToString: @"21007" ]) {
                                        [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
                                    } else  if (status && [status isEqualToString: @"0" ]){
                                        [self handleActionWithType:SIAPPurchVerSuccess data:nil];
                                    }
#if DEBUG
                                    NSLog( @"----验证结果 %@" ,jsonResponse);
#endif
                                }
                            }];
     
     
     // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
 
#pragma mark - SKProductsRequestDelegate
- ( void )productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
     NSArray *product = response.products;
     if ([product count] <= 0){
#if DEBUG
         NSLog( @"--------------没有商品------------------" );
#endif
         return ;
     }
     
     SKProduct *p = nil;
     for (SKProduct *pro  in  product){
         if ([pro.productIdentifier isEqualToString:_purchID]){
             p = pro;
             break ;
         }
     }
     
#if DEBUG
     NSLog( @"productID:%@" , response.invalidProductIdentifiers);
     NSLog( @"产品付费数量:%lu" ,(unsigned  long )[product count]);
     NSLog( @"%@" ,[p description]);
     NSLog( @"%@" ,[p localizedTitle]);
     NSLog( @"%@" ,[p localizedDescription]);
     NSLog( @"%@" ,[p price]);
     NSLog( @"%@" ,[p productIdentifier]);
     NSLog( @"发送购买请求" );
#endif
     
     SKPayment *payment = [SKPayment paymentWithProduct:p];
     [[SKPaymentQueue defaultQueue] addPayment:payment];
}
 
//请求失败
- ( void )request:(SKRequest *)request didFailWithError:(NSError *)error{
#if DEBUG
     NSLog( @"------------------错误-----------------:%@" , error);
#endif
}
 
- ( void )requestDidFinish:(SKRequest *)request{
#if DEBUG
     NSLog( @"------------反馈信息结束-----------------" );
#endif
}
 
#pragma mark - SKPaymentTransactionObserver
- ( void )paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
     for  (SKPaymentTransaction *tran  in  transactions) {
         switch  (tran.transactionState) {
             case  SKPaymentTransactionStatePurchased:
                 [self completeTransaction:tran];
                 break ;
             case  SKPaymentTransactionStatePurchasing:
#if DEBUG
                 NSLog( @"商品添加进列表" );
#endif
                 break ;
             case  SKPaymentTransactionStateRestored:
#if DEBUG
                 NSLog( @"已经购买过商品" );
#endif
                 // 消耗型不支持恢复购买
                 [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                 break ;
             case  SKPaymentTransactionStateFailed:
                 [self failedTransaction:tran];
                 break ;
             default :
                 break ;
         }
     }
}
@end
在控制器中调用,导入头文件
调用方法
- ( void )purchaseAction{
     
     if  (!_IAPManager) {
         _IAPManager = [STRIAPManager shareSIAPManager];
     }
     // iTunesConnect 苹果后台配置的产品ID
     [_IAPManager startPurchWithID: @"com.bb.helper_advisory"  completeHandle:^(SIAPPurchType type,NSData *data) {
//请求事务回调类型,返回的数据
         
     }];
}
经典文章参考: 1.  http: //yimouleng.com/2015/12/17/ios-AppStore/  内购流程
        2.   http: //www.jianshu.com/p/b199a4672608   完成交易后和服务器交互
              3.   http: //www.jianshu.com/p/1ef61a785508 沙盒账号测试
本站公众号
   欢迎关注本站公众号,获取更多程序园信息
开发小院