iOS苹果内购(详细步骤)

打印 上一主题 下一主题

主题 813|帖子 813|积分 2441

留意 apple Pay 不是苹果内购
1.第一步,先到 App Store Connect 中 的 协议部分,去填写信息,(这部分一般法务去解决)
(1)查看付费同意条款

(2)设置付费协议,添加账户,
(3)选择报税表等
(4)填写联系人

2.添加内购项目 (这个需要开发加入)
(1)添加内购项目
此处的产物ID 为唯一ID:  一般的规则:  bundleID.xxx    ;
(2)设置内购项目范例

斲丧型和非斲丧型的区别是:
斲丧型:可以重复多次购买
非斲丧型:只能购买一次,不会逾期,例如:永世会员等;
(3).设置名称,可以自定义;
(4)按照要求设置贩卖范围,代价,税务类别
(5)添加App Store 本地化版本;(只要是设置语言)
(6)App Store 推广
此处推广图也要是唯一的,不能和别的的商品推广图一致;(不能包含alpha通道)
如果设计给的图不合规,可以实验把图片用预览打开后 导出 ,取消alpha ,导出即可使用;
(7)考核信息就是,商品在App 中的页面位置截图,以及描述
(8)存储即可
此时我们已经创建了这个商品,接下来就是在项目中获取商品信息,并付出
3.在项目中的代码操作:
逻辑:  先通过后端的接口获取到商品的产物ID,然后在页面中发起购买请求,这个请求是向App Store发出的,整个过程不消与后端交互,等结果返回后,将付出根据发给后端即可.
具体的代码,可以参考:
  1. #import <Foundation/Foundation.h>
  2. #import <StoreKit/StoreKit.h>
  3. typedef enum : NSUInteger {
  4.     ApplePayManagerTypeSuccess,         // 购买成功
  5.     ApplePayManagerTypeFailed,          // 购买失败
  6.     ApplePayManagerTypeCancle,          // 取消购买
  7.     ApplePayManagerTypeVerFailed,       // 订单校验失败
  8.     ApplePayManagerTypeVerSuccess,      // 订单校验成功
  9.     ApplePayManagerTypeNotArrow,        // 不允许内购
  10. } ApplePayManagerType;
  11. typedef void (^IAPCompletionHandle)( ApplePayManagerType type,NSData * _Nullable data);
  12. NS_ASSUME_NONNULL_BEGIN
  13. @interface ApplePayManager : NSObject
  14. @property (nonatomic, strong)UIView * targetView;
  15. + (instancetype)shareAPManager;
  16. //开始内购
  17. - (void)startPurchWithDict:(NSDictionary *)purch completeHandle:(IAPCompletionHandle)handle;
复制代码

  1. #import "ApplePayManager.h"
  2. #import "MineMemberInAppVerifyReceiptRequest.h"
  3. @interface ApplePayManager ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
  4. @property (nonatomic,copy) NSString * purchID;
  5. @property (nonatomic,copy) IAPCompletionHandle handle;
  6. @property (nonatomic,strong) NSDictionary * parmas;
  7. @end
  8. @implementation ApplePayManager
  9. +(instancetype)shareAPManager{
  10.     static ApplePayManager *IAPManager = nil;
  11.     static dispatch_once_t onceToken;
  12.     dispatch_once(&onceToken,^{
  13.         IAPManager = [[ApplePayManager alloc] init];
  14.     });
  15.     return IAPManager;
  16. }
  17. - (instancetype)init{
  18.     self = [super init];
  19.     if (self) {
  20.         // 购买监听写在程序入口,程序挂起时移除监听,这样如果有未完成的订单将会自动执行并回调 paymentQueue:updatedTransactions:方法
  21.         [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
  22.     }
  23.     return self;
  24. }
  25. - (void)dealloc{
  26.     [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
  27. }
  28. #pragma mark - public  去苹果服务器请求产品信息
  29. -(void)startPurchWithDict:(NSDictionary *)purch completeHandle:(IAPCompletionHandle)handle{
  30.     if (purch.allKeys.count > 0) {
  31.         self.parmas = purch;
  32.         if ([purch objectForKey:@"productId"]) {
  33.             if ([SKPaymentQueue canMakePayments]) {
  34.                 // 开始购买服务
  35.                 self.purchID = [purch objectForKey:@"productId"];
  36.                 self.handle = handle;
  37.                 NSSet *nsset = [NSSet setWithArray:@[self.purchID]];
  38.                 SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
  39.                 request.delegate = self;
  40.                 [request start];
  41.                 dispatch_async(dispatch_get_main_queue(), ^{
  42.                     [self.targetView makeToastActivity:CSToastPositionCenter];
  43.                 });
  44.             }else{
  45.                 [self handleActionWithType:ApplePayManagerTypeNotArrow data:nil];
  46.             }
  47.         }
  48.     }
  49. }
  50. #pragma mark --收到产品返回信息 SKProductsRequestDelegate
  51. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
  52.     NSArray *product = response.products;
  53.     if([product count] <= 0){
  54.         NSLog(@"--------------没有商品------------------");
  55.         return;
  56.     }
  57.    
  58.     SKProduct *p = nil;
  59.     for(SKProduct *pro in product){
  60.         if([pro.productIdentifier isEqualToString:self.purchID]){
  61.             p = pro;
  62.             break;
  63.         }
  64.     }
  65.    
  66.     NSLog(@"productID:%@", response.invalidProductIdentifiers);
  67.     NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
  68.     NSLog(@"%@",[p description]);
  69.     NSLog(@"%@",[p localizedTitle]);
  70.     NSLog(@"%@",[p localizedDescription]);
  71.     NSLog(@"%@",[p price]);
  72.     NSLog(@"%@",[p productIdentifier]);
  73.     NSLog(@"发送购买请求");
  74.     //发送内购请求
  75.     SKPayment *payment = [SKPayment paymentWithProduct:p];
  76.     [[SKPaymentQueue defaultQueue] addPayment:payment];
  77. }
  78. //请求失败
  79. - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
  80. //    [self.targetView hideToastActivity];
  81.     NSLog(@"------------------错误-----------------:%@", error);
  82. }
  83. - (void)requestDidFinish:(SKRequest *)request{
  84. //    [self.targetView hideToastActivity];
  85.     NSLog(@"------------反馈信息结束-----------------");
  86. }
  87. #pragma mark - SKPaymentTransactionObserver // 监听购买结果
  88. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
  89.     [self.targetView hideToastActivity];
  90.     for (SKPaymentTransaction *tran in transactions) {
  91.         NSLog(@"%ld====",(long)tran.transactionState);
  92.         switch (tran.transactionState) {
  93.             case SKPaymentTransactionStatePurchased: //交易完成
  94.                 //订阅特殊处理
  95.                 if(tran.originalTransaction){
  96.                     //如果是自动续费的订单originalTransaction会有内容
  97.                     NSLog(@"自动续费的订单,originalTransaction = %@",tran.originalTransaction.transactionIdentifier);
  98.                     //tran.originalTransaction.transactionIdentifier
  99.                     //SKPaymentTransaction
  100.                     [self completeTransaction:tran isAutomatically:YES];
  101.                     //[self verifyPurchaseWithPaymentTransaction:tran isTestServer:NO];
  102.                 }else{
  103.                     //普通购买,以及 第一次购买 自动订阅
  104.                    // NSLog(@"%@-------",tran.transactionIdentifier);
  105.                     [self completeTransaction:tran isAutomatically:NO];
  106.                 }
  107.                
  108.                 break;
  109.             case SKPaymentTransactionStatePurchasing://商品添加进列表
  110.                 //NSLog(@"%ld====",tran.error.code);
  111.                 //NSLog(@"%@====",[[NSString alloc]initWithData:tran.payment.requestData encoding:NSUTF8StringEncoding]);
  112.                 //[TDGAVirtualCurrency onChargeRequst:@"" iapId:@"" currencyAmount:0 currencyType:@"" virtualCurrencyAmount:0 paymentType:@""];
  113.                 break;
  114.             case SKPaymentTransactionStateRestored://购买过
  115.                 NSLog(@"已经购买过商品");
  116.                 // 消耗型不支持恢复购买
  117.                 //[[SKPaymentQueue defaultQueue] finishTransaction:tran];
  118.                 break;
  119.             case SKPaymentTransactionStateFailed://交易失败
  120.                   NSLog(@"%@====",tran.error);
  121.                 //SKErrorUnknown
  122.                 [self failedTransaction:tran];
  123.                
  124.                 break;
  125.             default:
  126.                 break;
  127.         }
  128.     }
  129. }
  130. /// AppStore 从促销点击购买处理
  131. - (BOOL)paymentQueue:(SKPaymentQueue *)queue shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)product {
  132.     // 直接返回 NO,不交给苹果处理
  133.     NSString *productID = product.productIdentifier;
  134.     NSLog(@"AppStore 促销点击购买: productIdentifier - %@", productID);
  135.    
  136.     return YES;
  137. }
  138. #pragma mark - delegate
  139. // 交易结束
  140. - (void)completeTransaction:(SKPaymentTransaction *)transaction isAutomatically:(BOOL)isAutomatically{
  141.    
  142.     NSLog(@"%@", transaction.description);
  143.     // Your application should implement these two methods.
  144.     //    票据的校验是保证内购安全完成的非常关键的一步,一般有三种方式:
  145.     //    1、服务器验证,获取票据信息后上传至信任的服务器,由服务器完成与App Store的验证(提倡使用此方法,比较安全)
  146.     //    2、本地票据校验
  147.     //    3、本地App Store请求验证
  148.    
  149.     NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
  150.     NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
  151.     // 向自己的服务器验证购买凭证
  152.     //NSError *error;
  153.     //转化为base64字符串
  154.     NSString *receiptString=[receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
  155.    
  156.     NSLog(@"receipt ==== \n%@", receiptString);
  157.     //最终将这个receipt3的发送给服务器去验证就没问题啦!
  158.     //自动订阅(自动续费月卡)需要多加一个参数
  159.    
  160.     NSString * product_id = transaction.payment.productIdentifier;
  161.     NSString * transaction_id = transaction.transactionIdentifier;
  162.    
  163.    
  164.     NSMutableDictionary * requestContents = [[NSMutableDictionary alloc]init];
  165.     //订阅特殊处理
  166.     if (isAutomatically) {
  167.          //如果是自动续费的订单originalTransaction会有内容
  168.         NSString * transaction_id2 = transaction.originalTransaction.transactionIdentifier;
  169.         NSString * transaction_id = transaction.transactionIdentifier;
  170. //        [requestContents addEntriesFromDictionary:@{@"receipt": receipt3,@"password":secretKey,@"product_id":product_id,@"transaction_id":transaction_id,@"originalTransaction":transaction_id2}];
  171.     }else{
  172.         if (self.parmas.allKeys.count > 0) {
  173. //            [requestContents addEntriesFromDictionary:@{@"receipt": receipt3,@"uid":self.parmas[@"uid"],@"amount":self.parmas[@"amount"],@"actorid":self.parmas[@"userRoleId"],@"server":self.parmas[@"serverId"],@"order_no":self.parmas[@"cpOrderNo"],@"password":secretKey,@"product_id":product_id,@"transaction_id":transaction_id}];
  174.         }
  175.     }
  176.     [self.targetView makeToastActivity:CSToastPositionCenter];
  177.     NSDictionary * dic = @{@"productId": product_id, @"transactionId": transaction_id, @"receipt": receiptString, @"isSandbox": @0};
  178.     [MineMemberInAppVerifyReceiptRequest requestWithDic:dic requestFinish:^(MineMemberInAppVerifyReceiptRequest * request) {
  179.         [self.targetView hideToastActivity];
  180.         int code = [[request.responseJson objectForKey:@"code"] intValue];
  181.         NSString * stateString = [request.responseJson objectForKey:@"msg"];
  182.         if (code == 200) {
  183.             [self handleActionWithType:ApplePayManagerTypeVerSuccess data:nil];
  184.         }else {
  185.             [self.targetView makeToast:@"校验失败" duration:2 position:CSToastPositionCenter];
  186.         }
  187.     } requestFailed:^(MineMemberInAppVerifyReceiptRequest *request) {
  188.         [self.targetView hideToastActivity];
  189.         [self.targetView makeToast:@"请求失败" duration:2 position:CSToastPositionCenter];
  190.     }];
  191.    
  192.     //本地像苹果app store验证,上面是像自己的服务器验证
  193.     [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
  194.     // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
  195.     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
  196.     //[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
  197. }
  198. // 交易失败
  199. - (void)failedTransaction:(SKPaymentTransaction *)transaction{
  200.     if (transaction.error.code != SKErrorPaymentCancelled) {
  201.         [self handleActionWithType:ApplePayManagerTypeFailed data:nil];
  202.     }else{
  203.         [self handleActionWithType:ApplePayManagerTypeCancle data:nil];
  204.     }
  205.    
  206.     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
  207. }
  208. - (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag{
  209.     //交易验证
  210.     NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
  211.     NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
  212.    
  213.     if(!receipt){
  214.         // 交易凭证为空验证失败
  215.         [self handleActionWithType:ApplePayManagerTypeVerFailed data:nil];
  216.         return;
  217.     }
  218.     // 购买成功将交易凭证发送给服务端进行再次校验
  219.     // [self handleActionWithType:ApplePayManagerTypeSuccess data:receipt];
  220.    
  221.     NSError *error;
  222.     NSDictionary *requestContents = @{
  223.                                       @"receipt-data": [receipt base64EncodedStringWithOptions:0]
  224.                                       };
  225.     NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
  226.                                                           options:0
  227.                                                             error:&error];
  228.    
  229.     if (!requestData) { // 交易凭证为空验证失败
  230.         [self handleActionWithType:ApplePayManagerTypeVerFailed data:nil];
  231.         return;
  232.     }
  233.    
  234.     //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
  235.     //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
  236.    
  237.     NSString *serverString = @"https://buy.itunes.apple.com/verifyReceipt";
  238.     if (flag) {
  239.         serverString = @"https://sandbox.itunes.apple.com/verifyReceipt";
  240.     }
  241.     NSURL *storeURL = [NSURL URLWithString:serverString];
  242.     NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
  243.     [storeRequest setHTTPMethod:@"POST"];
  244.     [storeRequest setHTTPBody:requestData];
  245.    
  246.     NSURLSession *session = [NSURLSession sharedSession];
  247.     NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:storeRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  248.         if (error) {
  249.             // 无法连接服务器,购买校验失败
  250.             [self handleActionWithType:ApplePayManagerTypeVerFailed data:nil];
  251.         } else {
  252.             NSError *error;
  253.             NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  254.             if (!jsonResponse) {
  255.                 // 苹果服务器校验数据返回为空校验失败
  256.                 [self handleActionWithType:ApplePayManagerTypeVerFailed data:nil];
  257.             }
  258.             // 先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器
  259.             NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]];
  260.             if (status && [status isEqualToString:@"21007"]) {
  261. //                [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
  262.             }else if(status && [status isEqualToString:@"0"]){
  263. //                [self handleActionWithType:ApplePayManagerTypeVerSuccess data:nil];
  264.             }
  265.         }
  266.     }];
  267.     [dataTask resume];
  268.    
  269.     // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
  270.     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
  271. }
  272. #pragma mark - private
  273. - (void)handleActionWithType:(ApplePayManagerType)type data:(NSData *)data{
  274.     switch (type) {
  275.         case ApplePayManagerTypeSuccess:
  276.             NSLog(@"购买成功");
  277.             break;
  278.         case ApplePayManagerTypeFailed:
  279.             NSLog(@"购买失败");
  280.             break;
  281.         case ApplePayManagerTypeCancle:
  282.             NSLog(@"用户取消购买");
  283.             [self.targetView makeToast:@"用户取消购买" duration:2 position:CSToastPositionCenter];
  284.             break;
  285.         case ApplePayManagerTypeVerFailed:
  286.             [self.targetView makeToast:@"订单校验失败" duration:2 position:CSToastPositionCenter];
  287.             NSLog(@"订单校验失败");
  288.             break;
  289.         case ApplePayManagerTypeVerSuccess:
  290.             NSLog(@"订单校验成功");
  291.             break;
  292.         case ApplePayManagerTypeNotArrow:
  293.             NSLog(@"不允许程序内付费");
  294.             break;
  295.         default:
  296.             break;
  297.     }
  298.     if(_handle){
  299.         _handle(type,data);
  300.     }
  301. }
复制代码
上报完成即可
在App 提交考核的时候,要附带着添加指定的内购产物,
留意: 在未考核通过之前,这些商品,均是沙盒测试情况下的,付出并不会涉及金额交易,而且后端可以查询到付出信息


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

刘俊凯

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表