iOS应用内购买项目(IAP)

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];
    }
}




你可能感兴趣的:(iOS技术应用)