iOS应用内购买项目(IAP)

Posted Eric博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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应用内购买项目(IAP)的主要内容,如果未能解决你的问题,请参考以下文章

iOS 中的 IAP 停止工作

应用内购买后,viewWillAppear 会多次加载购买的商品

iOS 上 PhoneGap 2.0 的应用内购买 (IAP) 插件?

IAP(应用内购买)入门价格我如何管理它 iOS?

应用获准销售后何时激活应用内购买

应用内购买测试帮助 IAP