iOS应用内购买项目流程,大致分为三个部分:
第一部分:iTunes connect设置,包含创建新的发布版本号、创建购买项目(商品)、设置协议税务银行业务、添加沙盒技术测试账号;
第二部分:在项目Xcode中设置允许In-App Purchase,然后编写代码;代码部分可以使用原生态的,也可以使用第三方集成好的(IAPHelper),需要注意本地存储交易凭证,用于网络不佳或失败的情况下,重新提交。代码完成好之后,打包提交到APP Strore等待审核,提交的同时也把之前的内购项目勾选一并提交审核,待审核通过就可以测试了(可能审核通过之后需等待24小时,才能购买成功);
第三部分:测试是否成功购买,这里需要真机测试,并注销本机的Apple ID,然后在app中点击购买的时候,输入之前添加的沙盒测试账号。这部分可能会报各种错误,可能是前面一二部分没有设置完善,也有可能APP Store的问题,需要等待24小时自动就可以了,总之有问题就会有对应的解决方法,网上博客的解决方法也会帮助你的。
前面的设置部分按照开发文档设置,这里直接上代码:
//去苹果服务器请求商品
- (void)requestProductData:(NSString *)productId{
NSSet* dataSet = [[NSSet alloc] initWithArray:@[productId]];
[IAPShare sharedHelper].iap = [[IAPHelper alloc] initWithProductIdentifiers:dataSet];
#if DEBUG==1
[IAPShare sharedHelper].iap.production = NO;
#else
[IAPShare sharedHelper].iap.production = YES;
#endif
// 请求商品信息
[[IAPShare sharedHelper].iap requestProductsWithCompletion:^(SKProductsRequest* request,SKProductsResponse* response){
if(response.products.count > 0 ) {
SKProduct *product = response.products[0];
[[IAPShare sharedHelper].iap buyProduct:product onCompletion:^(SKPaymentTransaction* trans){
if(trans.error){
[self.hud hideAnimated:YES];
[MBProgressHUD showError:@"购买失败,请重试" toView:self.view];
}else if(trans.transactionState == SKPaymentTransactionStatePurchased) {
self.receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
[self justifyIAP:self.hud];
}else if(trans.transactionState == SKPaymentTransactionStateFailed) {
if (trans.error.code == SKErrorPaymentCancelled) {
[self.hud hideAnimated:YES];
}else{
[[[CommonUIAlert alloc] init] showCommonAlertView:self title:@"" message:@"购买失败,请重试" cancelButtonTitle:@"取消" otherButtonTitle:@"确定" cancle:^{
[self.hud hideAnimated:YES];
[self saveReceipt];
} confirm:^{
[self justifyIAP:self.hud];
}];
}
}
}];
}else{
[self.hud hideAnimated:YES];
[MBProgressHUD showError:@"购买失败,请重试" toView:self.view];
}
}];
}
#pragma mark - ios iap应用内支付二次验证
- (void)justifyIAP:(MBProgressHUD*)hud{
if(hud==nil){
hud = [MBProgressHUD showMessag:@"支付中..." toView:self.view];
}
NSString *receiptBase64 = [NSString base64StringFromData:self.receipt length:[self.receipt length]];
__weak typeof(self) weakSelf = self;
NSMutableDictionary *requestDict = [NSMutableDictionary dictionary];
[requestDict setObject:[DataModelInstance shareInstance].userModel.userId forKey:@"userid"];
[requestDict setObject:[CommonMethod paramStringIsNull:receiptBase64] forKey:@"data"];
[self requstType:RequestType_Post apiName:API_NAME_USER_POST_USER_IAPSECONDVALID paramDict:requestDict hud:hud success:^(AFHTTPRequestOperation *operation, id responseObject, MBProgressHUD *hud) {
if([CommonMethod isHttpResponseSuccess:responseObject] == HttpResponseTypeSuccess){
weakSelf.receipt = nil;
[hud hideAnimated:YES];
[weakSelf.navigationController popViewControllerAnimated:YES];
}else{
[[[CommonUIAlert alloc] init] showCommonAlertView:self title:@"" message:@"购买失败,请重试" cancelButtonTitle:@"取消" otherButtonTitle:@"确定" cancle:^{
[hud hideAnimated:YES];
[weakSelf saveReceipt];
} confirm:^{
[weakSelf justifyIAP:hud];
}];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error, MBProgressHUD *hud) {
[[[CommonUIAlert alloc] init] showCommonAlertView:self title:@"" message:@"购买失败,请重试" cancelButtonTitle:@"取消" otherButtonTitle:@"确定" cancle:^{
[hud hideAnimated:YES];
[weakSelf saveReceipt];
} confirm:^{
[weakSelf justifyIAP:hud];
}];
}];
}
//持久化存储用户购买凭证(这里最好还要存储当前日期,用户id等信息,用于区分不同的凭证)
- (void)saveReceipt{
if(self.receipt && self.receipt.length){
NSString *fileName = [NSString genUUID];
NSString *savedPath = [NSString stringWithFormat:@"%@%@.plist", AppStoreInfoLocalFilePath, fileName];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys: self.receipt, IAP_RECEIPT,[DataModelInstance shareInstance].userModel.userId,IAP_USER_ID,nil];
BOOL result = [dic writeToFile:savedPath atomically:YES];
NSLog(@"%d",result);
self.receipt = nil;
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//从服务器验证receipt失败之后,在程序再次启动的时候,使用保存的receipt再次到服务器验证
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:AppStoreInfoLocalFilePath]) {//如果在改路下不存在文件,说明就没有保存验证失败后的购买凭证,也就是说发送凭证成功。
[fileManager createDirectoryAtPath:AppStoreInfoLocalFilePath//创建目录
withIntermediateDirectories:YES
attributes:nil
error:nil];
}else{//存在购买凭证,说明发送凭证失败,再次发起验证
[self sendFailedIapFiles];
}
return YES;
}
//验证receipt失败,App启动后再次验证
- (void)sendFailedIapFiles{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
//搜索该目录下的所有文件和目录
NSArray *cacheFileNameArray = [fileManager contentsOfDirectoryAtPath:AppStoreInfoLocalFilePath error:&error];
if (error == nil){
for (NSString *name in cacheFileNameArray){
if ([name hasSuffix:@".plist"]){//如果有plist后缀的文件,说明就是存储的购买凭证
NSString *filePath = [NSString stringWithFormat:@"%@/%@", AppStoreInfoLocalFilePath, name];
[self sendAppStoreRequestBuyPlist:filePath];
}
}
}
}
- (void)sendAppStoreRequestBuyPlist:(NSString *)plistPath{
NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:plistPath];
//这里的参数请根据自己公司后台服务器接口定制,但是必须发送的是持久化保存购买凭证
NSData *receipt = [dic objectForKey:IAP_RECEIPT];
if(receipt==nil){
[self sendAppStoreRequestSucceededWithData:plistPath];
return;
}
NSString *receiptBase64 = [NSString base64StringFromData:receipt length:[receipt length]];
NSMutableDictionary *requestDict = [NSMutableDictionary dictionary];
[requestDict setObject:[dic objectForKey:IAP_USER_ID] forKey:@"userid"];
[requestDict setObject:[CommonMethod paramStringIsNull:receiptBase64] forKey:@"data"];
[[[UIViewController alloc] init] requstType:RequestType_Post apiName:API_NAME_USER_POST_USER_IAPSECONDVALID paramDict:requestDict hud:nil success:^(AFHTTPRequestOperation *operation, id responseObject, MBProgressHUD *hud) {
if([CommonMethod isHttpResponseSuccess:responseObject] == HttpResponseTypeSuccess){
[self sendAppStoreRequestSucceededWithData:plistPath];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error, MBProgressHUD *hud) {
}];
}
//验证成功就从plist中移除凭证
- (void)sendAppStoreRequestSucceededWithData:(NSString *)plistPath{
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:plistPath]){
[fileManager removeItemAtPath:plistPath error:nil];
}
}