BooheeFE / weekly

📝 薄荷前端周刊 Boohee Front End Team Weekly
760 stars 40 forks source link

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

Open AmyOne opened 5 years ago

AmyOne commented 5 years ago

开发前准备

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 commented 4 years ago

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