1、内购流程
1、在 AppStore 中创建相应的物品,创建内购沙盒测试账号 2、客户端从后台获取相应的物品 ID (当然也可以再客户端写死,但后期扩展性就受限制了) 3、依据相应的物品 ID 请求商品的相关信息 4、依据商品信息创建订单请求交易 5、依据返回的订单状态处理交易结果 6、请求后台再次验证订单状态 7、依据后台返回结果处理相关逻辑
2、创建内购物品以及沙盒测试账号
3、客户端编写相关代码
再这里我把和支付相关的逻辑都抽取到了一个单例中,在最后贴上个人梳理的相关代码大家一起学习
4、做内购过程中遇到的坑
1、内购沙盒测试 账号在支付成功后,再次购买相同 ID 的物品,会提示如下内容的弹窗。
您已购买此 App 内购买项目。此项目将免费恢复 。
解决方法:在使用
[[SKPaymentQueue defaultQueue] addPayment:payment];
将支付信息添加进苹果的支付队列后,苹果会自动完成后续的购买请求,在用户购买成功或者点击取消购买的选项后回调
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction;
方法返回响应的结果信息,在该方法内除了得到响应的支付信息编写自身的业务的代码外还要记得调用
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
方法通知苹果的支付队列该交易已经完成,否者就会已发起相同 ID 的商品购买就会有此项目将免费恢复的提示。
2、每次启动一个新的内购支付流程,刚发起的时候系统就会调用- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction;
这个方法,结果扰乱一部分的支付业务逻辑
在 SKPaymentQueue 被启动并且添加了 addObserver之后,如果其判断到有未完成的交易,会主动调用paymentQueue updatedTransactions 这个方法来继续完成相关的交易流程,所以如果在上面那种情况下得到结果后不去调用 finish 接口,下次重新开启支付流程就会检查未完成的支付并调用该接口。
解决方法:
1.在得到支付结果后及时调用 finish 方法
2.添加一个是否是新发起的支付流程的条件,在条件符合的情况下才触发应用的相关逻辑的代码
(PS:在拿到苹果的支付结果凭据的时候最好在客户端做一份持久化的数据备份,等待后台验证完成后再清除掉,避免出现验证中间出现问题导致用户支付成功但后台相关的增值处理没有完成导致用户金钱损失的问题)
3、如何区分购买物品的是 沙盒测试 账号 还是 真实账号
后台再验证支付凭据的时候要区分是沙盒测试账号购买的还是用户真实账号购买的,所以在传凭据的时候还需要告诉后台当前购买的账号性质。
解决方法:通过在配置文件中定义相关的宏定义并结合 Debug 与 Release 的编译环境确定相关的参数
#define APPSTORE_ASK_TO_BUY_IN_SANDBOX 1 NSNumber *sandbox; #if (defined(APPSTORE_ASK_TO_BUY_IN_SANDBOX) && defined(DEBUG)) sandbox = @(0 ); #else sandbox = @(1 ); #endif
个人没有找到相关的方法可以打完包后动态的检测购买物品的账号性质,希望知道的朋友分享一下,感谢 ^_^
5、iOS7 客户端验证的订单状态
6、内购验证凭据返回结果状态码说明
苹果反馈的状态码:
2 1 0 0 0 App Store无法读取你提供的JSON数据 2 1 0 0 2 收据数据不符合格式 2 1 0 0 3 收据无法被验证 2 1 0 0 4 你提供的共享密钥和账户的共享密钥不一致 2 1 0 0 5 收据服务器当前不可用 2 1 0 0 6 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中 2 1 0 0 7 收据信息是测试用(sandbox),但却被发送到产品环境中验证 2 1 0 0 8 收据信息是产品环境中使用,但却被发送到测试环境中验证
更为详细的信息请参考
ios 应用内支付(In-App Purchase,沙盒测试,后台验证)
【IOS一气呵成】之IAP集成:内购和内购恢复
另附:苹果官网内购 API 链接
7、如何恢复购买
备注:
相关代码:
XYPayManager.h
#import <Foundation/Foundation.h> #define APPSTORE_ASK_TO_BUY_IN_SANDBOX 1 typedef void (^payCompleteBlock)( NSDictionary *resultDic, BOOL isSuccess); @interface XYPayManager : NSObject + (instancetype)sharedPayManager; - (void )requestAppleStoreProductDataWithString:( NSString *)productIdentifier payComplete :(payCompleteBlock)payCompletionBlock; - (void )checkAppStorePayResultWithBase 6 4 String:( NSString *)base 6 4 String; @end
XYPayManager.m
#import "XYPayManager.h" #import <StoreKit/StoreKit.h> @interface XYPayManager() <SKPaymentTransactionObserver, SKProductsRequestDelegate> @property ( nonatomic , copy ) NSString *appleProductIdentifier; @property ( nonatomic , copy ) payCompleteBlock payComplete; @end @implementation XYPayManager + (instancetype)sharedPayManager { static XYPayManager *payManager; static dispatch_once_t once = 0 ; dispatch_once(&once, ^{ payManager = [[XYPayManager alloc ] init ]; [[SKPaymentQueue defaultQueue ] addTransactionObserver :payManager]; }); return payManager; } - (void )dealloc { [[SKPaymentQueue defaultQueue ] removeTransactionObserver : self ]; } #pragma mark - 苹果支付充值 - (void )requestAppleStoreProductDataWithString:( NSString *)productIdentifier payComplete :(payCompleteBlock)payCompletionBlock { if (![SKPaymentQueue canMakePayments ]) { NSLog(@"不允许程序内付费" ); return ; } NSLog(@"-------------请求对应的产品信息----------------" ); self .startBuyAppleProduct = YES ; self .payComplete = payCompletionBlock; self .appleProductIdentifier = productIdentifier; NSLog(@"生成产品信息" ); NSArray *product = [[NSArray alloc ] initWithObjects :productIdentifier, nil nil ]; NSSet *nsset = [NSSet setWithArray :product]; SKProductsRequest *request = [[SKProductsRequest alloc ] initWithProductIdentifiers :nsset]; request.delegate = self ; [request start ]; } - (void )productsRequest:( SKProductsRequest *)request didReceiveResponse :( SKProductsResponse *)response{ NSLog(@"--------------收到产品反馈消息---------------------" ); NSArray *productArray = response .products ; if ([productArray count ] == 0 ){ NSLog(@"--------------没有商品------------------" ); return ; } NSLog(@"productID:%@" , response .invalidProductIdentifiers ); NSLog(@"产品付费数量:%lu" ,(unsigned long )[productArray count ]); SKProduct *product = nil ; for ( SKProduct *pro in productArray) { NSLog(@"%@" , [pro description ]); NSLog(@"%@" , [pro localizedTitle ]); NSLog(@"%@" , [pro localizedDescription ]); NSLog(@"%@" , [pro price ]); NSLog(@"%@" , [pro productIdentifier ]); if ([pro .productIdentifier isEqualToString : self .appleProductIdentifier ]){ product = pro; } } SKPayment *payment = [SKPayment paymentWithProduct :product]; NSLog(@"发送购买请求" ); [[SKPaymentQueue defaultQueue ] addPayment :payment]; } - (void )request:( SKRequest *)request didFailWithError :( NSError *)error{ NSLog(@"------------------错误-----------------:%@" , error); } - (void )requestDidFinish:( SKRequest *)request{ NSLog(@"------------反馈信息结束-----------------" ); } - (void )paymentQueue:( SKPaymentQueue *)queue updatedTransactions :( NSArray *)transaction { for ( SKPaymentTransaction *paymentTransactionp in transaction){ switch (paymentTransactionp .transactionState ) { case SKPaymentTransactionStatePurchased : { NSLog(@"交易完成-restoreCompletedTransactions" ); [self buyAppleStoreProductSucceedWithPaymentTransactionp :paymentTransactionp]; [self completeTransaction :paymentTransactionp]; } break ; case SKPaymentTransactionStatePurchasing : NSLog(@"商品添加进列表" ); break ; case SKPaymentTransactionStateRestored : NSLog(@"已经购买过商品" ); break ; case SKPaymentTransactionStateFailed : { NSLog(@"交易失败" ); [self completeTransaction :paymentTransactionp]; } break ; } } } - (void )buyAppleStoreProductSucceedWithPaymentTransactionp:( SKPaymentTransaction *)paymentTransactionp { NSString *base 6 4 Str = [paymentTransactionp .transactionReceipt base64EncodedStringWithOptions :NSDataBase 6 4 EncodingEndLineWithLineFeed]; NSLog(@"苹果内购凭据号\n\n\n\n\n\n%@\n\n\n\n\n\n" ,base 6 4 Str); [self checkAppStorePayResultWithBase64String :base 6 4 Str]; } - (void )checkAppStorePayResultWithBase 6 4 String:( NSString *)base 6 4 String { NSNumber *sandbox; #if (defined(APPSTORE_ASK_TO_BUY_IN_SANDBOX) && defined(DEBUG)) sandbox = @(0 ); #else sandbox = @(1 ); #endif NSMutableDictionary *prgam = [[NSMutableDictionary alloc ] init ];; [prgam setValue :sandbox forKey : @"sandbox" ]; [prgam setValue :base 6 4 String forKey : @"reciept" ]; } - (void )completeTransaction:( SKPaymentTransaction *)transaction{ NSLog(@"交易结束" ); [[SKPaymentQueue defaultQueue ] finishTransaction :transaction]; } @end
原文地址:http://blog.csdn.NET /jiisd/article/details/50527426