In App Purchase(程序内支付),简称IAP,开发人员可以通过在程序中添加IAP利用自己的程序实现赢利。
虽然Apple官方有大量文档介绍IAP相关知识,但是实际运用中总是不可避免会出现一些错误,远不像文档上介绍的那么顺利。
下面是对实际项目中IAP运用的一些总结和介绍:
一,前期准备
在开始动手之前,需要先根据项目要求,确定合适的商品支付类型,了解相应的支付处理流程,以决定程序的构架。
建议参考文档:In App Purchase(Store Kit)
相关链接:[WWW] http://wenku.baidu.com/view/71ba9c1eb7360b4c2e3f6455.html
官方定义的支付类型,主要有以下2种:
内置产品类型
使用这种模型,需要交付的产品已经在程序内部。
这种方式通常用在一些被锁定的功能上。也可以用来交付在程序束(AppBundle)中的内容。 该方式的一个重要的优点是你可以及时的给客户交付产品,大多数的内置产品应为非消耗性商品。
服务器类型 使用这终方式,要提供另外的服务器将产品发送给程序。
服务器交付适用于订阅、内容类商品和服务,因为商品可以作为数据发送,而不需改动程序束。
二,实现流程
在程序中实现IAP的主要流程如下:
创建唯一的App ID
生成及安装新的provisioning profile文件
在Xcode中更新 bundle ID 及 code signing profile
如果还没做的话,请在iTunes Connect中提交有关你程序的 metadata
如果还没做的话,请在iTunes Connect中提交你程序的二进制码
为IAP添加新产品,取得产品的Product ID(产品ID)
等待Apple处理产品相关信息和测试环境
以上1~5、7项不作多余说明,第6项需注意根据项目需求选择合适的产品类型,主要有以下3种:
Non-consumable(非消耗品): 仅需付费一次 (例如你希望将出现从免费版升级为专业版)
Consumable(消耗品): 每次下载都需要付费
Subscription(预订): 循环消费,主要有auto-renewable-subscription(自动订阅)和non-renewable-subscription(非自动订阅)两种,在iOS6开始又追加了一种free-subscriptions(免费订阅)。
注意:non-consumable、auto-renewable-subscription、free-subscriptions这3种商品可以采用restore的支付操作。
三,实装代码
IAP功能的实装需要使用StoreKit framework,所以请先在framework中追加。
另,StoreKit无法在模拟器上工作。模拟器上只会收到如下警告信息:WARNING: SKPaymentQueue does not work in the simulator
添加StoreObserver类,用于处理返回信息
做成TestStoreObserverDelegate.h文件,定义以下2个回调函数:
// 支付成功 - (void) didCompleteTransaction: (SKPaymentTransaction *)transaction;
// 支付失败 - (void) didFailedTransaction: (SKPaymentTransaction *)transaction;
做成TestStoreObserver.h文件,声明以下3个方法:
#import
#import
#import "TestStoreObserverDelegate.h"
@interface TestStoreObserver : NSObject {
id delegate;
}
@property (assign) id delegate;
- (void) paymentQueue: (SKPaymentQueue *)queue updatedTransactions: (NSArray *)transactions;
- (void) completeTransaction: (SKPaymentTransaction *)transaction;
- (void) failedTransaction: (SKPaymentTransaction *)transaction;
@end
TestStoreObserver.m文件,实装以下3个方法:
1 #import "TestStoreObserver.h"
2
3 @implementation GolfStoreObserver
4 @synthesize delegate;
5
6 - (void)paymentQueue: (SKPaymentQueue *)queue updatedTransactions: (NSArray *)transactions {
7 for (SKPaymentTransaction* transaction in transactions) {
8 switch (transaction.transactionState) {
9 case SKPaymentTransactionStatePurchased: // 支付结束
10 [self completeTransaction: transaction];
11 break;
12
13 case SKPaymentTransactionStateFailed: // 支付失败
14 [self failedTransaction: transaction];
15 break;
16
17 case SKPaymentTransactionStateRestored:
18 break;
19
20 default:
21 break;
22 }
23 }
24 }
25
26 - (void)completeTransaction: (SKPaymentTransaction *)transaction
27 {
28 // 支付成功的回调函数
29 if (self.delegate != nil && [self.delegate respondsToSelector: @selector(didCompleteTransaction:)]) {
30 [self.delegate didCompleteTransaction: transaction];
31 }
32 }
33
34 - (void)failedTransaction: (SKPaymentTransaction *)transaction
35 {
36 [[NSNotificationCenter defaultCenter] postNotificationName: @"faliedTransaction" object:nil];
37 [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
38 // 支付失败的回调函数
39 if (self.delegate != nil && [self.delegate respondsToSelector: @selector(didFailedTransaction:)]) {
40 [self.delegate didFailedTransaction: transaction];
41 }
42 }
43
44 -(void) dealloc {
45 [super dealloc];
46 }
47
48 @end
在controller类,添加SKProductsRequestDelegate协议,支付开始的处理中,添加以下代码,用来从APPLE获取商品信息:
request = [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObject: entity.productId]];
request.delegate = self;
[request start];
在controller类,追加以下回调函数,当接收到商品信息的时候,向支付队列里添加一个支付对象:
- (void)productsRequest: (SKProductsRequest *)request didReceiveResponse: (SKProductsResponse *)response {
SKPayment *payment = [SKPayment paymentWithProductIdentifier: entity.productId];
[[SKPaymentQueue defaultQueue] addPayment: payment];
}
另外添加- (void)request:(SKRequest *)request didFailWithError: (NSError *)error函数用于处理产品信息接收异常。
在controller类,追加以- (void) didCompleteTransaction: (SKPaymentTransaction *)transaction函数,处理支付返回信息:
[[SKPaymentQueue defaultQueue] finishTransaction: transaction]; // 将支付对象移出支付队列
APPLE的支付返回信息是,JSON格式的字符串,可以直接根据其中的status的值是否为0来判断成功与否(0为成功)
如果是内置产品类型的商品,进行到这里,再进行如下的确认处理,正常处理就已经完成了。
1 NSString *json = [NSString stringWithFormat: @"{\"receipt-data\":\"%@\"}", [transaction.transactionReceipt base64Encoding]]; // 需要将返回结果进行base64加密处理
2 NSString *urlsting = @“https://sandbox.itunes.apple.com/verifyReceipt”;
3 NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: urlsting]];
4 [urlRequest setHTTPMethod: @"POST"];
5 [urlRequest setHTTPBody: [json dataUsingEncoding: NSUTF8StringEncoding]];
6 NSError *error;
7 NSURLResponse *response;
8 NSData *result = [NSURLConnection sendSynchronousRequest: urlRequest returningResponse: &response error:&error];
9 NSString *resultString = [[NSString alloc] initWithData: result encoding: NSUTF8StringEncoding];
10 NSDictionary *dict = (NSDictionary*) [resultString JSONValue];
11
12 //支払成功フラグ
13 BOOL paymentSuccessFlg = NO;
14
15 //支払結果を確認する
16 if (dict != nil) {
17 NSNumber *status = [dict objectForKey: @"status"];
18 if ([status isEqual: [NSNumber numberWithInt: 0]] == YES) {
19 paymentSuccessFlg = YES;
20 }
21 }
如果是服务器类型的商品,还需要进行服务器端验证,下面是准备将APPLE的支付返回信息提交到程序服务器端的处理。服务器的环境是struts2+sesear2,数据采用JSON格式。
1 NSString *json = [NSString stringWithFormat: @"{\"receipt-data\":\"%@\"}", [transaction.transactionReceipt base64Encoding]]; // 需要将返回结果进行base64加密处理
2 NSData *postData = [json dataUsingEncoding: NSUTF8StringEncoding];
将postData作为参数传递个服务器端后,由服务器端把这个支付结果提交APPLE确认即可。