Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2019/05/01 - iOS内购填坑之旅 #37

Open
AmyOne opened this issue May 9, 2019 · 1 comment
Open

2019/05/01 - iOS内购填坑之旅 #37

AmyOne opened this issue May 9, 2019 · 1 comment

Comments

@AmyOne
Copy link

AmyOne commented May 9, 2019

开发前准备

1.付费应用协议

因为内购都是和钱相关的,所以我们在开发之前,需要itunes Connect账号的owner,也就是所有者针对未配置过内购的新项目,签署 Paid Applications agreement(《付费应用程序协议》),这个页面一般我们也进不去。。。
image

  1. 配置内购项目

image

我们需要设置一个app secret(App专用共享秘钥),对于自动续期订阅的内购项目,我们需要这个秘钥去苹果做票据验证,其它模式不需要。

  1. Xcode工程配置

image

开启此选项App Store中APP的介绍界面显示内购的相关项目,关闭则不显示

项目应用

实现步骤主要包括三步:
  1. 首先在项目工程中加入StoreKit.framework
  2. 加入头文件#import <StoreKit/StoreKit.h>
  3. 遵守代理SKPaymentTransactionObserver,SKProductsRequestDelegate
App内请求内购项

在创建购买订单之前要向app store请求内购项,建议在购买之前完成,以减少购买时查询订单的时间

  1. 判断用户是否具备权限
if([SKPaymentQueue canMakePayments]) {
        self.progressHUD.label.text = @"购买中,请等待...";
        [self requestProductData];
    }else{
        self.progressHUD.label.text = @"您没有允许程序内付费...";
        [self.progressHUD hideAnimated:YES afterDelay:1];
    }
  1. 创建商品查询请求
(void)requestProductData{
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:
                                 [NSSet setWithObject:self.itemId]];
    
    request.delegate = self;
    
    [request start];
}

注意:这里的ProductIdentifiers可以查询多个商品,只需传一个product id的集合。

查询的结果我们可以在SKProductsRequestDelegate得到结果

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response

{
    NSArray *myProducts = response.products;
    
    if ([myProducts count] > 0) {
        SKProduct *selectedProduct = [myProducts objectAtIndex:0];
        SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:selectedProduct];
        
        [[SKPaymentQueue defaultQueue] addPayment:payment];
        
    }else{
        if (self.progressHUD) {
            self.progressHUD.label.text = @"无效的产品";
            [self.progressHUD hideAnimated:YES afterDelay:1];
        }
    }
}

请求完成和请求失败可以调用

- (void)requestDidFinish:(SKRequest *)request NS_AVAILABLE_IOS(3_0);
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error NS_AVAILABLE_IOS(3_0);

项目里面请求产品和支付还是放在一起的,可以在点击购买之前先完成商品信息的查询,然后保存下来,后面用户点击购买的时候可以节省一些请求时间,毕竟苹果的服务器确实是有些慢的。。 也可以缩短加载loading的时间。

构建支付请求
  1. 创建支付
SKPayment * payment = [SKPayment paymentWithProduct:product];

[[SKPaymentQueue defaultQueue] addPayment:payment];
  1. 添加支付交易的回调的SKPaymentTransactionObserver

这个是内购项目的最重要的一个方法,我们创建了支付项之后,剩下的就看苹果服务器的了,我们就靠这个observer来接收购买过程状态。

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions NS_AVAILABLE_IOS(3_0);

switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased://交易完成
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed://交易失败
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored://已经购买过该商品
                [self restoreTransaction:transaction];
                break;
            case SKPaymentTransactionStatePurchasing:      //商品添加进列表
                NSLog(@"商品添加进列表");
                break;
            default:
                break;
        }

注意:我们需要在适时的时候调用[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
否则苹果会以为这个事务一直没有完成,每次启动都会重复执行updatedTransactions方法。

对于自动订阅模式,我们需要给后端提供SKPaymentTransaction里面的一些信息,来做唯一标识,对于首次购买的用户SKPaymentTransaction里面的originalTransaction是空的,对于续费的用户才会有值,这样我们就可以区分首次购买和续费。

验证票据

我们app采用的服务器验证,获取票据信息后,通过base64编码以后上传至信任的服务器,由服务器完成与App Store的验证,
验证地址
沙盒环境 https://sandbox.itunes.apple.com/verifyReceipt;
正式环境 https://buy.itunes.apple.com/verifyReceipt;

NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
    
    NSString *encodingStr = [receiptData base64EncodedString];

沙盒账号是什么

iOS应用里面用到了苹果应用内付费(IAP)功能,在项目上线前一定要进行功能测试。测试肯定是需要的,何况这个跟money有关。。。不仅仅是要测试,我们还要进行大量的测试,这样才能发现内购存在的一些问题,比如漏单,支付失败等问题。开发完成了之后,如何进行测试呢?难道我测试个内购功能要自己掏钱?就算是公司掏钱,但是苹果要抽掉30%,想想点下购买的时候都会手抖。。。
苹果当然没这么坑了,测试内购,苹果提供了沙盒账号的方式。这个沙盒账号其实是虚拟的AppleID,在开发者账号后台的iTune Connect上配置了之后就能使用沙盒账号测试内购,有了沙盒账号,我们就可以任性挥霍啦。

image

IAP内购的一些坑

对于自动订阅模式,要在每次启动的时候都要添加observer [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
这主要是为了防止漏单的问题,在每次启动的时候会去检测是否有未完成的事务,然后再把相关信息上传到服务器。所以我们需要把orderId本地化,之后无论是首次购买还是续费,还有服务器续费未收到通知的情况,都可以防止漏单的问题。

@winterwd
Copy link

在每次启动的时候会去检测是否有未完成的事务 这里为什么是 每次启动APP检查,而不是 掉起支付前检查呢?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants