iOS 内付费(in-app purchase)--非消耗品的购买与恢复

这里写图片描述


iOS内付费的功能对于一个app来说是非常重要的,如果在这一环节出了一些致命的问题,那就很可能会影响app的推广和公司的利益了。

我在很早之前写过一篇关于iOS内付费的文章(文章地址),在那篇博客中讲述了如何在苹果后台生成iOS内付费商品,以及在我们的app工程中如何去添加相应的内付费代码。但是,在后来的日子里面我发现网友在关于内购功能上遇到的问题比我想象的还要多,其中很大一部分都是一些很简单的问题,比如说签名使用的不正确,内购商品ID不正确,bundleID没有和签名一致,苹果账号没有补充完整银行税务信息等导致的,只要简单的修改一下就能解决以上问题。

另外,很多app项目创建的内购商品都是以“消耗品“ ,这种类型的商品为主,不过有的网友也遇到了其他的问题,他在app中创建了几个非消耗品的内购商品,然后提交苹果审核的时候,却被苹果打回了,原因是苹果要求开发者对这种非消耗品的商品增加一个“恢复“的按钮。

Apple 打回的原文如下:

Business - 3.1.1


We found that your app offers In-App Purchase(s) that can be restored but does not include a "Restore Purchases" feature to allow users to restore the previously purchased In-App Purchase(s), as specified in the "Restoring Purchase Products" section of the In-App Purchase Programming Guide

"Users restore transactions to maintain access to content they've already purchased. For example, when they upgrade to a new phone, they don't lose all of the items they purchased on the old phone. Include some mechanism in your app to let the user restore their purchases, such as a Restore Purchases button."

To restore previously purchased In-App Purchase products, it would be appropriate to provide a "Restore" button and initiate the restore process when the "Restore" button is tapped by the user.

Before You Submit

今天,我在这篇文章中就来针对上述的苹果打回的问题做出解决方案。


“非消耗品的购买和恢复” 该如何操作

创建非消耗品(non-consumable)

在 iTunesconnect 后台中创建一个非消耗品,根据内购商品所需要的内容编辑完整。创建的选项如下图所示:

这里写图片描述

内购流程梳理

非消耗品第一次购买的逻辑和消耗品是一样,我们先来理一遍内购的流程,首先在代码中我们一定要在内购初始化的地方加上这句代码:

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

不然购买成功和失败的任何回调你都收不到。在初始化之后,要去苹果服务器那获取我们所要提供给用户的详细商品信息,代码逻辑如下:

- (void) requestProductData{
    if(!self.mRequestProductDataObjects || self.mRequestProductDataObjects.count == 0){
        return;
    }

    if ([SKPaymentQueue canMakePayments])
    {
        NSArray *product = nil;
        product = [NSArray arrayWithArray:_mRequestProductDataObjects];
        NSSet *nsset = [NSSet setWithArray:product];
        SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
        request.delegate = self;
        [request start];
    }

    ...
}

在保证商品id正确的情况下,我们的程序会把获取到的商品信息回调到此函数中:

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response NS_AVAILABLE_IOS(3_0);

解析商品信息的逻辑,我这边就不细说了,要看详细的代码,就去我这篇文章中 iOS应用程序内购/内付费看吧!

在获取到商品信息之后,我们就可以开始进行购买了,购买的逻辑函数如下:

- (void) purchase:(NSString*) productId
{
    if(productId.length == 0 || !productId){
        return;
    }

    SKProduct * _payProduct  =nil;

    if ([SKPaymentQueue canMakePayments])
    {
        for (SKProduct * payment in self.purchasableObjects) {
            if ([productId isEqualToString:payment.productIdentifier] )
            {
                _payProduct = payment;
                break;
            }
        }
    }

    if(!_payProduct){
        return;
    }

    SKMutablePayment * payment = [SKMutablePayment paymentWithProduct:_payProduct];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

上述函数的内部逻辑走完以后,就会回调此方法了:

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions){
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                NSLog(@"支付成功");
                break;

            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                NSLog(@"支付失败");
                break;

            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
                NSLog(@"交易恢复处理");
            default:
                break;
        }
    }
}

在这个回调函数的内部逻辑里面,分别有对购买成功,购买失败,恢复交易的处理。好了,经过一遍简单的梳理以后,想必你已经在心中有了一个大概,接下来就是我们的重头戏了。

恢复按钮怎么添加?

正常情况下,非消耗品的内购商品在购买成功后,应该把 “购买” 字样变成 “恢复” 字样,当用户完成一次购买以后,就可以永久使用啦!那在什么时机去修改这个按钮呢。

当非消耗品完成一次购买以后,再次去点击购买的话,系统会提示你已经购买了此项目,如图:

这里写图片描述

为了确保我们的app在每次运行起来后,内购商品列表能正确显示,哪些非消耗商品是已经购买过的,我们得在内购功能初始化的地方(就是添加监听函数下面)调用该函数,来获取哪些商品是已经买过的:

[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

该函数调用后会恢复之前的交易,程序会直接进入到函数updatedTransactions的回调:

 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
   case SKPaymentTransactionStateRestored:
                .....

}

然后再走到此回调函数:

- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue

我们可以给上面函数中添加如下逻辑,用一个NSMutableArray来存储苹果回调过来给我们已经购买过的非消耗品的商品信息:

- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
    purchasedItemIDs = [[NSMutableArray alloc] init];
    NSLog(@"received restored transactions: %i", queue.transactions.count);
    for (SKPaymentTransaction *transaction in queue.transactions)
    {
        NSString *productID = transaction.payment.productIdentifier;
        [purchasedItemIDs addObject:productID];
        NSLog(@"%@",purchasedItemIDs);
    }
}

在这里我们已经获取到了所有已经购买过的非消耗品的 transactions,那最后一步就是回调app界面去修改按钮名称咯。那有些人会问,如果我换了设备怎么办,其实也很简单,iOS内购商品是跟着你的Apple ID走的,你换到新设备后登录你的Apple ID帐号,启动app就会走到restoreCompletedTransactions函数,苹果还是会给你推送过来你购买过的非消耗品信息,然后再去修改app按钮是显示 购买 还是 恢复 。

最后关于在app内购买非消耗品 ,道具应该怎么下发,我做一下自己的理解说明吧!非消耗品不管是第一次购买,还是恢复购买,苹果验证票据返回过来的信息都是一致的,如图所示:

这里写图片描述

所以,我们在第一次购买非消耗品的时候,待苹果服务器票据验证成功后,服务器要将玩家的账号信息(非Apple ID账号,再说你app也获取不到)和 该商品ID保存在数据库中;玩家若换设备或者卸载了app以后,再次想要获取该道具,我们就可以直接根据玩家的账号信息和商品ID去数据库中查找,若已经购买过了,就可以直接下发道具。

总结

关于iOS内购的功能其实代码就那么些,但是如果你真的要做到内购功能很健壮,例如购买东西时不丢单,游戏充值到账等,确实要花很多精力去研究,这不单单是客户端的任务,还包括服务器如何保存票据,丢单的时候如何去补单这些操作。好了,写了这么些,不早了也该歇歇了,若有什么问题的话,请加以下公众号联系我。


好了。祝大家生活愉快。多多收获友谊和爱情。如果想获取更多的讯息,请扫描下方二维码关注我的微信公众号:
这里写图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章