iOS内购总结

内购类型有四种:

消耗型商品,非消耗型商品,非续期订阅,自动续期订阅. 其中最有难度的就是自动续期订阅的实现,开通自动续期订阅后,订阅会员的处理将会遇到如下难点问题:自动订阅的到期继续自动订阅的处理,订阅取消的处理,取消后又在App Store开启自动订阅的处理等一系列问题

异常情况说明提醒用户:
当用户使用appleid下买了一个月的应用会员,换个账号登录当前app,appleid不变,购买的时候提示我已经订阅过该项目,这种问题怎么处理

客户端开发者方面汇总

APP专用共享秘钥

App 专用共享密钥是用于接收此 App 自动续订订阅收据的唯一代码。如果您要将此 App 转让给其他开发者或不想公开主共享密钥,建议使用 App 专用共享密钥。

设置共享秘钥

注: 经过验证:购买过自动续期订阅后,验证内购时(即使是消耗型商品)必须带上password字段,测试模式没有提供用户取消订阅或者进行退款的测试

订阅群组

创建自动续订类型的时候,当你创建一个订阅产品时点击创建后会自动让你创建一个订阅群组,以向用户提供一系列内容供应、服务等级或时限。名字自己随便起,就是给自己看的,有代表意义就行,一个群组下可以有多个自动续订订阅。如果你要搞促销优惠,那么每个顾客可以享受每个订阅群组的一个推介促销优惠一次。

创建两个订阅组,一个开发、测试,一个生产使用订阅组可有效避免用户同时订阅多个项目

image.png

注意 一个订阅群组中的订阅是 互斥 的,这意味着用户只能一次订阅一个群组中的一个选项。如果你希望用户能够一次购买多个订阅,你可以将这些 App 内购买项目放在不同的订阅群组中。

App Store 服务器通知网址

您服务器上的网址 (URL),可接收有关 App 内购买项目的重要事件(例如订阅状态变更或 App 内购买项目退款)的相关 App Store 通知。更多请看https://help.apple.com/app-store-connect/#/dev0067a330b

image.png

代码配置

在APP启动时候要增加一下监听方法以便查询到订单信息:
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

订阅订单再次购买与首次购买所执行的方法不一(如下图):

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchasing: // 0
                break;
            case SKPaymentTransactionStatePurchased: // 1
                 //订阅特殊处理
                 if(transaction.originalTransaction){
                      //如果是自动续费的订单originalTransaction会有内容 
                 }else{
                      //普通购买,以及 第一次购买 自动订阅
                 }
                break;
            case SKPaymentTransactionStateFailed: // 2
                [self failTracker:transaction];
                break;
            case SKPaymentTransactionStateRestored: // 3
                [self restoreTransaction:transaction];
                
                break;
            default:

                break;
        }
    }
}

测试验证

自动续费测试时需注意:
一天最多测试6次自动续费,如需更多测试验证需要多申请一些沙盒账号,用户在订阅期间同一个群组产品无法重复订阅,如需允许多次订阅请创建多个群组将产品归纳在不同的群组中,以便可以重复订阅。

image.png
image.png

APP自动续费条款

自动续订订阅,一定要在 app 中有详细的使用条款相关,类似下图:


示例1
示例2

参考文案如下:

Once you confirm your purchase, you will be charged to your Apple ID account. If purchase the auto-renewing subscription, unless you cancel the order at least 24 hours before the end of the current billing cycle, the subscription will automatically renew, and your account will charge the renewal fee within 24 hours before the end of the current billing cycle. You can unsubscribe from account settings in the app store. \r\nPrivacy Policy & Terms of Use
确认购买后,将向您的Apple ID账户收款。购买连续包年项目,除非您在当前计费周期结束前至少24小时取消订单,否则项目会自动续订,您的账户将在当前计费周期结束前24小时内收取续订费用。您可在App Store的账户设置中取消订阅。\r\n隐私政策和使用条款

服务端方面汇总

  1. 自动续费产品进行验证时需要凭证参数(receipt-data)和共享秘钥(password),否则在验证返回"status": 21003(沙盒环境)或(21004 你提供的共享密钥和账户的共享密钥不一致),秘钥获取如图所示:

** 再次温馨提醒:购买过自动续期订阅后,验证内购时(即使是消耗型商品)必须带上password字段。

  1. 请求验证
    获取到票据以后我们通过App Store来验证票据是否真实
    沙盒状态下使用:https://sandbox.itunes.apple.com/verifyReceipt来验证
    生产环境下使用:https://buy.itunes.apple.com/verifyReceipt

2:服务端验证

经验实际验证得到以下结果:
in_app 与 latest_receipt_info
查看验证接口时发现,这两个字段的数值几乎相同,不过有几点需要注意:

1、自动续订订阅类型,在到期后会再生成一条购买记录,这条记录会出现在last_receipt_info里,但不会出现在in_app里
2、自动续订订阅类型可以配置试用,试用记录只有在latest_receipt_info里,is_trial_period字段才是true
3、消耗型购买记录未出现在latest_receipt_info,需要检查in_app来确保校验正确
4、消耗型产品是无法查看到取消购买或者退款状态
5、当订单信息中有 Cancellation Date 字段说明该订单被取消,不管该订阅的过期日期是什么,该订单都可以设为无效订单未进行扣款成功或者发起了退款
6、因为自动订阅可能重视的就是到期时间,所以应该关注的是“ expires_date”、“ expires_date_ms”、“ expires_date_pst”这三个字段,expires_date表示过期时间,expires_date_ms 表示过期时间毫秒数值,expires_date_pst表示的是太平洋时间
7、由于 expires_date 表示的是美国时间,所以一般都采用expires_date_ms数值来进行验证有效期
8、判断是否有自动续费产品,推荐循环 latest_receipt_info订单列表判断是否有大于当前时间日期,同时判断是否有 Cancellation Date 字段;
9、transaction.originalTransaction是 一组自动续订类型,购买过,里面originalTransactionId会一样。然后transaction.originalTransaction.transactionIdentifier这个会多单,每一单对应的就是用户的购买记录。周期到期,一般就expires_date时间到期,拿receipt去苹果服务端验证,用户是否续订了。

客户端上报

apple每期自动扣款后, 会生成一笔新的receipt, 客户端获取后发送给server校验, 成功后开通下一期会员权益

状态变更通知

用于自动续订订阅的服务器到服务器通知服务, 可以在苹果后台配置通知地址, 状态变更时, server会收到通知, 下面是摘自苹果官方的状态描述

image.png

说明:

1.扣款失败导致过期,不会有任何通知
2.正常扣款续订,不会有任何通知
3.服务端最好处理CANCEL类型。因为IAP存在黑产:比如买了一年会员,然后打电话给苹果客服退款,如果服务端不处理,这一年会员是生效的。(退款通知只有订阅和非订阅类型才有,消耗型无退款通知)

官方文档 https://developer.apple.com/documentation/storekit/#//apple_ref/doc/uid/TP40008267-CH7-SW13

会员状态轮询

server轮询 自动续订类型的收据, 每一期的latest_receipt_info中都会记录所有的交易(包含历史和新增), 可以轮询上一期(任意一期都可以)receipt, 通过latest_receipt_info 解析出用户最新的订阅状态.

关于用户取消和用户续订:

cancellation_date_ms

此字段表示 Apple 客户支持取消交易的时间和日期,或自动续订订阅计划升级的时间和日期,采用 UNIX 纪元时间格式,以毫秒为单位。此字段仅适用于 Apple 退还给用户的购买。使用此时间格式处理日期。
取消的应用内购买无限期保留在收据中。仅当退款用于非消耗性产品、自动续订订阅或非续订订阅时,才会出现此值。
此字段仅在生产中可用,不会出现在沙盒收据中。

您可以使用此值:
确定是否停止提供与购买相关的内容。

检查任何最新的续订交易,这可能表明用户重新开始或升级了他们的订阅,以进行自动续订订阅购买。

如用户取消,这个凭证里面有 cancellation_date_ms 字段判断。 检索订单如到期就不用管。 如果到期自动续订了,就要客户端重新传凭证给服务端,服务端去苹果验证订单。

因此,在判断订阅是否过期的时候,cancellation_date_ms 和 expires_date_ms 都需要判断。

苹果官方文档https://developer.apple.com/documentation/appstorereceipts/cancellation_date_ms

苹果验证结果说明

下面是凭证验证返回的详细结果:

当订阅产品验证后,新增返回了 latest_receipt_info 订阅与非订阅订单信息列表

image.png

非订阅和消耗品订单如下图:
in_app 返回结果

image.png

订阅和非订阅订单数据内容如下:
latest_receipt_info 返回结果

image.png

关于订阅订单参数做一个详细说明(参数后面有注释):

{
    "quantity": "1",
    "product_id": "产品Id",         /// 产品Id
    "transaction_id": "1000000850764418",
    "original_transaction_id": "1000000850764418",         /// 此id值不变
    "purchase_date": "2021-07-30 03:13:27 Etc/GMT",     ///最新的购买时间
    "purchase_date_ms": "1627614807000",             ///最新的购买时间毫秒
    "purchase_date_pst": "2021-07-29 20:13:27 America/Los_Angeles",     ///最新的购买时间(太平洋的时间)
    "original_purchase_date": "2021-07-30 03:13:29 Etc/GMT",             ///最初的购买时间
    "original_purchase_date_ms": "1627614809000",                 ///最初的购买时间毫秒
    "original_purchase_date_pst": "2021-07-29 20:13:29 America/Los_Angeles",     /// 最初的购买时间(太平洋的时间)
    "expires_date": "2021-07-30 03:18:27 Etc/GMT",              /// 时间到期
    "expires_date_ms": "1627615107000",                         ///到期时间毫秒
    "expires_date_pst": "2021-07-29 20:18:27 America/Los_Angeles", /        //到期时间(太平洋的时间)
    "web_order_line_item_id": "1000000064562233",
    "is_trial_period": "false",                                 ///  首次购买
    "is_in_intro_offer_period": "false",                         ///  是否是否是在试用期
    "in_app_ownership_type": "PURCHASED",
    "subscription_group_identifier": "20857364"
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容