一、代码逻辑

关于iOS 订阅、自动订阅 本身功能开发很简单。跟正常的购买没什么大的差异。唯一需要特殊处理(自动订阅)的是,

在APP启动时候要增加侦听:

        [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; 

因为自动订阅,除了第一次购买行为是用户主动触发的。后续续费都是Apple自动完成的,一般在要过期的前24小时开始,苹果会尝试扣费,扣费成功的话 会在APP下次启动的时候主动推送给APP。所以,APP启动的时候一定要添加上面的那句话。

另外就是处理续费了:

- (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;
}
}
}

  上述代码片段对 transaction.originalTransaction 进行了判断,如果有内容一定为订阅类型的。为什么在这加个判断处理,是因为续费 是发生在APP启动的时候,这时候你登录流程等可能还没有走完,因为有的游戏在跟服务器进行 校验的时候会传一些userid等信息,或是加密的信息,视情况而定,是否要区分处理。

注意点:就是在沙箱环境测试时候,APP启动可能得到5次的 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions ;订单处理,算是要并发处理case SKPaymentTransactionStatePurchased: 这种case,这时候你得注意你得网络请求队列,不要最后一个订单请求覆盖了 前几个。(可以用信号量处理下,比较简单)

二、服务器验证receipt

服务器在校验receipt时候也就有一个坑:

1、那就是创建自动订阅的时候需要新建一个共享秘钥,就是一串字母。

2、服务器在向苹果服务器校验receipt时候,不仅需要传receipt,还需要传秘钥。

{
“receipt-data” : “(actual receipt bytes here)”
“password” : “(shared secret bytes here)”
}

3、介绍下receipt结构

 receipt通过base64解码可得:

{
"signature" = "dfreree...."; //也是base64
"purchase-info" = "ewoJIm9x....."; //也是base64,这个里面存放详细时间,流水号等
"environment" = "Sandbox";
"pod" = "100";
"signing-status" = "0";
}

"purchase-info"可以再次base64解码可得:

{
"original-purchase-date-pst" = "2017-08-29 23:52:45 America/Los_Angeles";
"purchase-date-ms" = "1504144439749";
"unique-identifier" = "a063c2c321dd885642a5cddd9160e0ad8291d978";
"original-transaction-id" = "1000000328915948";
"expires-date" = "1504144739749";
"transaction-id" = "1000000329310742";
"original-purchase-date-ms" = "1504075965000";
"web-order-line-item-id" = "1000000036091900";
"bvrs" = "1";
"unique-vendor-identifier" = "B78549AC-58D4-4750-8E6F-F4CCE6138A5A";
"expires-date-formatted-pst" = "2017-08-30 18:58:59 America/Los_Angeles";
"item-id" = "1276511095";
"expires-date-formatted" = "2017-08-31 01:58:59 Etc/GMT";
"product-id" = "lcm.denachina.pickle.38.1month";
"purchase-date" = "2017-08-31 01:53:59 Etc/GMT";
"original-purchase-date" = "2017-08-30 06:52:45 Etc/GMT";
"bid" = "com.denachina.pickle";
"purchase-date-pst" = "2017-08-30 18:53:59 America/Los_Angeles";
"quantity" = "1";
}

  你想要的东西,都可以获取到。客户端可以做这些事情,但是没有多大必要,还是服务器处理得好。(对于无服务器APP只能客户端处理了)

附上一个在线base64解码的:http://base64.xpcha.com/

 

三、自动续费测试

重点都不是上面的,重点是测试,如何测试?尤其自动续费怎么测?

先看下Apple原文档:

When testing auto-renewable subscriptions in the test environment, keep in mind that the duration times are compressed. Additionally, test subscriptions only auto-renew a maximum of six times. Table 3-1 lists the compressed duration times.

Actual duration

Test duration

1 week

3 minutes

1 month

5 minutes

2 months

10 minutes

3 months

15 minutes

6 months

30 minutes

1 year

1 hour

意思就是,沙箱环境 自动续费时间缩短了,一周 对应 三分钟,一月 对应 五分钟。。。

购买完一个一周 类型订阅,就不要在APP不退出的情况等待了,必须3分钟 或是 10分钟后重新登录,Apple才会主动告知你结果,也就是第一点提到的。

测试中会遇到几个问题:

1.沙箱环境自动续费是一定会自动续费的吗?

答案:不一定的,有时候会,有时候不会。所以要多测测,多建几个测试账号。

2.是否需要实现restoreCompletedTransactions ?

答案:视需求吧。有少量文章说2014年起苹果审核严格了,必须要有一个按钮实现restoreCompletedTransactions。另外,我听百度一位同学说,爱奇艺2015年因为这个被拒过。但是,目前来看很多使用了订阅的应用或是游戏,并没有这个功能。

我是感觉,看需求了。订阅 是跟着 userid 唯一呢? 还是跟着apple id 呢?在国内,一般都是前者。

四、讨论

1.自动订阅归属的问题:

a.  苹果设计自动订阅的初衷是 ,订阅一个服务, 这个服务需要跟着 Apple ID走。说白了,就是你A设备 用了Apple账号100001购买了,你换了B设备 用Apple账号100001登录app store,你同样能享受到服务。国外的一些音乐类型、杂志报刊等用的比较多,游戏类的少,苹果自己的Apple music也有自动订阅(首创)。

b.  目前国内的一些应用或是游戏,希望的是自动订阅 关联的是 APP的 user id ,而不是Apple ID。说白了,就是你购买了一个自动订阅服务,我不管你哪个apple id 支付的, 但是只能我一个 APP的 唯一用户可以享受服务。这时候就需要APP自身做处理了,就是记住首次购买的transaction-id,并且绑定某个用户。以后自动续费的话,都会有original-transaction-id,这个id 是第一次购买的transaction-id,根据这个服务器可以联系初始购买的服务。有点描述偏了,当transaction-id绑定了用户,再次收到其它用户transaction-id请求时候,视情况处理了。(你也可以根据unique-vendor-identifier处理)

2.同一个Apple ID购买完的自动订阅,可以再次点击购买吗(有效期内)?

答案:不可以,苹果自身会拦截,会出现这么个提示窗,如下图:

但是,sandbox测试环境,在第三大点的对应表格对应时间内,apple会拦截的,过了这个时间苹果是不会拦截的。

3.够买了自动订阅3个月的,可以换购 1年的  或是 1个月的吗?

答案:可以,苹果文档有提到,视为升级订阅套餐  或是 降级订阅套餐。

4.关于掉单的问题

答案:一定要在服务器校验完票据后,客户端收到服务器的反馈结果后再:

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];

5.关于普通消费商品,如何防止黑卡、掉单、外币等?

我有时间会再写一篇。

五、了解更多

https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/CreatingInAppPurchaseProducts.html#//apple_ref/doc/uid/TP40013727-CH3-SW10

https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html

https://stackoverflow.com/questions/8033673/ios-sandbox-environment-auto-renewal-subscription

http://www.jianshu.com/p/28fc3cc8c49f

http://www.cnblogs.com/zidong0822/p/4701839.html

http://blog.csdn.net/xiaoyuanzhiying/article/details/46708043

http://www.jianshu.com/p/e9e4dc3dc9ee

https://www.raywenderlich.com/154737/app-purchases-auto-renewable-subscriptions-tutorial

http://www.360doc.com/content/14/1118/16/12282510_426165722.shtml

关于验证:

https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html

https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW20

关于预防刷:

http://blog.csdn.net/skylin19840101/article/details/71757055

iOS 自动订阅开发的更多相关文章

  1. 从C#到Objective-C,循序渐进学习苹果开发(5)--利用XCode来进行IOS的程序开发

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.前面几篇随笔主要介绍C#和O ...

  2. 利用XCode来进行IOS的程序开发

    利用XCode来进行IOS的程序开发 本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换 ...

  3. Sagit.Framework For IOS 开发框架入门开发教程2:一行代码实现引导页

    前言: 开篇比较简单:Sagit.Framework For IOS 开发框架入门开发教程1:框架下载与环境配置 第二篇教程之前写了一半,感觉不太好写,而且内容单纯介绍API,要说的很多,又枯燥乏味. ...

  4. iOS项目——项目开发环境搭建

    在开发项目之前,我们需要做一些准备工作,了解iOS扩展--Objective-C开发编程规范是进行开发的必备基础,学习iOS学习--Xcode9上传项目到GitHub是我们进行版本控制和代码管理的选择 ...

  5. ios自动打包-fastlane 安装、使用、更新和卸载

    ios自动打包使用fastlane 1.首先安装xcode 首先检查是否已经安装 Xcode 命令行工具,fastlane 使用 xcodebuild 命令进行打包,运行 xcode-select - ...

  6. 《React Native 精解与实战》书籍连载「配置 iOS 与 Android 开发环境」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  7. 《iOS 7 应用开发实战详解》

    <iOS 7 应用开发实战详解> 基本信息 作者: 朱元波    管蕾 出版社:人民邮电出版社 ISBN:9787115343697 上架时间:2014-4-25 出版日期:2014 年5 ...

  8. iOS 9应用开发教程之多行读写文本ios9文本视图

    iOS 9应用开发教程之多行读写文本ios9文本视图 多行读写文本——ios9文本视图 文本视图也是输入控件,与文本框不同的是,文本视图可以让用户输入多行,如图2.23所示.在此图中字符串“说点什么吧 ...

  9. iOS 9应用开发教程之显示编辑文本标签文本框

    iOS 9应用开发教程之显示编辑文本标签文本框 ios9显示.编辑文本 在iOS,经常会看到一些文本的显示.文字就是这些不会说话的设备的嘴巴.通过这些文字,可以很清楚的指定这些设备要表达的信息.本节将 ...

随机推荐

  1. Hadoop单机安装配置过程:

    1. 首先安装JDK,必须是sun公司的jdk,最好1.6版本以上. 最后java –version 查看成功与否. 注意配置/etc/profile文件,在其后面加上下面几句: export JAV ...

  2. SQL Server计算列

    计算列由可以使用同一表中的其他列的表达式计算得来.表达式可以是非计算列的列名.常量.函数,也可以是用一个或多个运算符连接的上述元素的任意组合.表达式不能为子查询. 例如,在 AdventureWork ...

  3. JSP小例子——以Model1的思想实现用户登录小例子(不涉及DB操作)

    Model1简介现在比较流行的就是Model1和Model2,这里介绍Model1.在Model1模型出现前,整个Web应用的情况是:几乎全部由JSP页面组成,JSP页面接受处理客户端请求,对请求处理 ...

  4. Erstudio8.0怎么用?Erstudio8.0汉化版详细使用教程

    Erstudio8.0使用教程 打开ERstudio,点击新建出现如图对话框: 选择第一个,表示创建一个新的关系型 数据库模型 这里提一点数据库模型分为relational(关系)和dimension ...

  5. vue ios自带拼音全键输入法模糊查询兼容性问题

    ios的自带拼音全键会在输入框中输入拼音,直接在输入框用@keyup="autoInput()"的话,在监听输入事件的时候安卓显示正常, ios就会出现输入显示数据不灵敏 解决办法 ...

  6. 160519、Oracle中将查询出的多条记录的某个字段拼接成一个字符串的方法

    with temp as( select 'China' nation ,'Guangzhou' city from dual union all select 'China' nation ,'Sh ...

  7. java 常用资源

    java高手真经:http://pan.baidu.com/share/link?uk=2100475681&shareid=2381645927#path=%252F%255Bwww.jav ...

  8. Codeforces Round #426 (Div. 2)A题&&B题&&C题

    A. The Useless Toy:http://codeforces.com/contest/834/problem/A 题目意思:给你两个字符,还有一个n,问你旋转n次以后从字符a变成b,是顺时 ...

  9. echarts学习心得1---模块化单文件引入和标签式单文件引入

    一.模块化单文件引入 1. 为ECharts准备一个具备大小(宽高)的Dom(当然可以是动态生成的) <div id="main" style="height:40 ...

  10. SSH整合中,使用父action重构子类action类.(在父类中获取子类中的泛型对象)

    import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import com.opensymphony.x ...