In-App Purchase(iap)快速指南

iap简介 
在应用中内嵌Store,在iOS应用中使用Store Kit framework来实现In-App Purchase。Store Kit会连接App Store,代替应用安全地完成用户支付。Store Kit提示用户授权支付,然后通知应用“用户已经完成支付”,这样应用就可以提供用户购买的东西。
使用iap可以为应用的增强功能,或额外内容进行收费,如以下场景:
基础版本的应用,购买额外的特性 
书籍阅读器应用,允许用户购买和下载新的书籍 
游戏,提供新的环境(场景、关卡、等级等) 
在线游戏,允许玩家购买虚拟财产(金币、道具、装备等) 
数字杂志或newsletter订阅



Products
可在iTunes Connect中定义,iap支持四种类型的product: 
Content(内容):包括数字图书、杂志、照片、封面、游戏等级、游戏角色、以及其它应用可交付的数字内容 
Functionality(功能):解锁或扩展应用当前已交付的特性。例如游戏中的多个小游戏,用户可单独购买 
Service(服务):允许应用提供收费的一次性服务,每次使用服务都需要单独购买 
Subscription(订阅):以扩展的方式,提供用户对内容或服务的访问。如应用提供每月财报或在线门户网站访问
所有的Product都必须先在App Store中通过iTunes Connect注册,你需要提供一个唯一的product identifier,以及许多其它信息
应用使用Store Kit与App Store通信时;以及用户购买后,应用处理这个购买并提供product,都使用这个ID进行标识
product还可以分为以下类型:
消耗型,每次用户需要该产品时都需要购买。一次性服务通常都是消耗型 
非消耗型,每个用户只需要购买一次,一旦购买,用户账号相关联的所有设备都可以使用。Store Kit提供内建的支持,可在多个设备中使用非消耗型产品 
自动再生订阅,和非消耗型一样递送至用户的所有设备。但是自动再生订阅在iTunes Connect中需指定订阅的持续时间。App Store在过期后自动更新该订阅。如果用户选择不允许订阅自动更新,在订阅过期后将自动取消,用户不能再访问该产品。应用负责验证某个订阅当前是否有效,并且可以获得最近交易的receipt 
免费订阅,在Newsstand中提供免费订阅。用户一旦注册了免费订阅,账号相关的所有设备都可以访问该订阅的内容。免费订阅不会过期,而且只能在启用Newsstand的应用中提供 
非自动再生订阅,创建受限持续时间订阅的旧机制,应使用自动再生订阅代替该机制。与自动再生订阅的区别有三点: 
iTunes Connect中不指定订阅协议,应用负责提供这个信息给用户 
可以被购买多次,App Store不会自动更新该订阅。应用负责实现订阅更新,检测过期,提示用户重新购买。 
应用必须将非自动再生订阅递送至用户的所有设备。Store Kit不会进行自动同步。



如何交付已购买的iap特性
应用需要设计和实现提供iap产品给用户的交付机制。目前主要有两种:内建模型、服务器模型。
在这两种模型中,你都必须跟踪Store中已提供的products列表,并交付给用户使用。
内建模型
所有需要交付的products都已经包含在应用中。这个模型主要用于应用解锁相关的功能。也可以使用这个模型来交付应用Bundle中已经提供的内容。
内建模型的主要优点是可以快速地交付products给用户,多数内建型Products都应该是非消耗型的
应用可以在Application Bundle中存储Product Identifier,从而标识出相应的iap product。苹果推荐使用plist来记录所有内建特性的product ID。基于内容的应用可以使用这个技术来增加新的内容,而不需要修改应用的代码。
在用户成功购买product之后,应用必须解锁该特性,并且交付给用户使用。解锁特性最简单的方法是修改应用参数。应用参数在用户备份iOS设备时会自动备份,应用在用户购买product之后可以考虑推荐用户进行备份操作。
内建模型示意图:


应用从Bundle中获取product ID列表 
应用向App Store发送请求,以获取products的信息 
App Store返回Products信息 
应用使用这些信息来显示一个Store给用户 
用户从显示的Store中选择一项 
应用向App Store发送payment请求 
App Store处理这个payment,并返回一个完成的transaction 
应用读取transaction信息,并交付用户已购买的内容
服务器模型
在服务器模型中,我们提供一个独立的服务器,来交付Products给iOS应用。服务器交付适合于订阅、服务、内容,因为这些Products可以按数据来交付,而不需要修改iOS应用bundle。游戏也可以使用服务器来交付新的游戏环境(谜题、等级、或关卡)。Store Kit不管你的服务器与iOS应用之间如何交互;Store Kit也不提供标识特定用户的机制。你需要自己提供一个用户机制,来标识你iOS应用的用户(例如你提供订阅服务,需要与用户相关联,就需要自己实现用户机制)。
服务器模型示意图:



应用发送请求到服务器,获取所有的Products ID列表 
服务器返回Products ID列表 
应用发送请求至App Store,获取Products的信息 
App Store返回Product信息 
应用使用这些信息,向用户显示一个Store界面 
用户从Store中选择一项 
应用向App Store发送payment请求 
App Store处理该payment,并返回完成的transaction 
应用从transaction中获取receipt数据,并将其发送给服务器 
服务器记录receipt数据,并建立一个audit trail(审查跟踪) 
服务器发送receipt数据到App Store,以验证是否合法的transaction 
App Store解析receipt数据,并返回receipt,以及验证结果(是否合法) 
服务器读取返回的receipt数据,并确定哪个用户已经完成购买 
服务器交付已购买的内容至iOS应用
应用最好通过你的服务器来获取Products标识,而不是直接存放在plist文件中。这样可以增加新Products,而无需更新应用。
在服务器模型中,应用获取transaction相关联的签名receipt,并且发送给服务器。服务器然后验证该receipt并对其进行解码,以确定需要向应用交付什么内容。
非消耗型products可以使用Store Kit内建的功能恢复,但是非自动再生订阅必须由你的服务器来还原。你负责记录非自动再生订阅的信息,并且还原给用户使用。消耗型Products也可以在服务器中记录,例如允许用户在多个设备上获取服务的结果。



获取Product信息
应用在显示Store给用户时,必须在界面上显示App Store中获取的信息。
向App Store发送请求
Store Kit提供通用的机制,向App Store发送请求。应用创建和初始化请求对象,指定一个delegate,就可以发起请求。App Store处理请求后,会异步地调用delegate来通知应用请求结果。如下图所示:



SKRequest
SKRequest是一个抽象基类,用于向Store发送请求
SKRequestDelegate
SKRequestDelegate是一个protocol,应用实现该接口,来处理Store成功或失败的结果
获取Products的信息
应用使用Products请求来获取本地化的product信息。应用创建一个请求,包含products ID字符串的列表。发起请求后,products ID会传输至App Store。App Store会返回你之前在iTunes Connect中注册的本地化的products信息。应用使用这些信息来显示Store


允许用户购买products之前,必须先使用该product ID向App Store查询详细的信息,这样才能确保product ID合法,而且在iTunes Connect中标记为可以销售。
SKProductsRequest
SKProductsRequest对象使用一组product ID来创建,这些是你想要显示给用户的product列表
SKProductsRequestDelegate
SKProductsRequestDelegate协议需要应用来实现,用于获取Store的响应信息。请求成功处理后,会异步地接收App Store的响应信息
SKProductsResponse
SKProductsResponse对象是返回的结果信息,请求列表中的每个合法的product ID都有一个相应的SKProduct对象;同时还包含Store不能识别的一组product ID列表。不能识别的原因有许多:ID拼写错误、标记为不可销售、iTunes Connect中的修改还没有传送到其它App Store服务器等
SKProduct
SKProduct对象提供你在App Store中注册的product的详细本地化信息



Purchase(购买)
当用户准备好购买product时,应用请求App Store来完成支付。App Store会创建一个持久化的transaction,即使用户退出和重新启动应用,也会继续地处理该支付交易。App Store将未决交易列表同步给应用,并且在任何交易状态变化时,递送更新信息给应用。
收集支付
应用创建一个payment对象,并将其添加到payment队列中,如下图所示:



payment添加到队列中时,会创建一个持久化的交易来保存它。在payment处理完之后,交易会更新为收集支付之后的状态信息。应用实现observer来接收交易更新信息。observer负责提供已购买内容给用户,并且从payment队列中移除该交易
SKPayment 
使用payment对象来收集支付。payment对象包含一个product ID,以及一个可选的购买数量。你可以把同一个payment对象排队多次,每排队一次都将导致一次单独的请求。
用户可以在"设置"中禁用应用中购买。在排队一个购买请求之前,应用需要首先确认payment能够被处理,调用payment队列的canMakePayments方法。
SKPaymentQueue
payment队列用来与App Store通信。当payment添加到队列中时,Store Kit传输这个请求到App Store。Store Kit会显示对话框询问用户授权支付。完成的transaction会返回到应用的observer,以便应用处理。
SKPaymentTransaction
每个payment被添加到队列时,都会创建一个相应的transaction。每个transaction都有一些属性,允许应用确定该交易的状态。当支付收集完成后,transaction对象还包含额外的交易成功详细信息。
虽然应用可以从payment队列中获取未决交易的列表,更通用的做法是应用等待,直到payment队列主动通知observer,并传递更新交易的列表。
SKPaymentTransactionObserver
应用的某个对象实现SKPaymentTransactionObserver协议,并把该对象添加为payment队列的observer。observer的主要责任是检查已完成的交易;递送已经成功购买的product;然后将这些完成交易从payment队列中移除。
应用应该在启动时就关联observer到payment队列,而不是等到用户试图购买时才关联。transaction在应用终止后并不会丢失,下次应用启动时,Store Kit会继续处理这些交易。在应用初始化阶段添加observer,可确保所有交易都成功传递到应用。
还原交易
一旦交易处理完成,并从队列中移除,应用通常不会再看到它。但是如果应用支持的product类型必须可还原,你就必须包含一个接口,允许用户还原这些购买。这个接口允许用户把已购买的product添加到其它设备,或者原始设备重置后,也需还原交易。
Store Kit提供内建的机制,用于还原非消耗型、自动再生订阅、免费订阅等product的交易。应用调用payment队列的restoreCompletedTransactions方法,payment队列就会发送一个请求到App Store来还原交易。而App Store则对所有之前已经完成的交易生成一个新的还原交易。还原交易对象的originalTransaction属性保存了原始的交易。应用对还原交易进行处理,从中获取原始交易,然后使用这个原始交易对象来解锁用户已购买的内容。在Store Kit还原了之前完成的交易时,会通知payment队列的observer对象,并调用它的paymentQueueRestoreCompletedTransactionsFinished: 方法。
如果用户试图购买一个可还原的product(而不是使用你实现的还原接口),应用会接收到一个正常的交易,而不是可还原交易。但是用户不需要再次为这个product支付。应用应该把这些交易当作原始交易一样来处理。
非自动再生订阅和可消耗型product不会被Store Kit自动还原。但是非自动再生订阅又必须可还原,因此在购买这些product时,你必须在自己的服务器上记录这些交易,并提供自己的机制来还原这些交易给用户的所有设备。



应用中添加Store(内建模型)
首先确保项目链接了StoreKit.framework,应用中添加Store的详细步骤如下:
1. 定义应用需要递送的products。 
Store Kit对products有一些限制,不允许应用对自己打补丁,或者下载额外的代码。products要么已经在应用的现有代码中,要么从远程服务器下载数据文件来实现。如果应用增加特性需要修改现有代码,必须发布一个新版本的应用。
2. 在iTunes Connect中为每个product注册详细信息
每次应用Store要增加一个新的product,都需要先在iTunes Connect中进行注册。每个product都需要一个唯一的ID字符串。App Store使用这个字符串来查找product信息以及处理支付请求。product ID特定于iTunes Connect账号,注册的方式与注册应用类似。
3. 确定系统能够处理支付
用户可以禁止应用内购买,因此你的应用需要先检查当前是否支持应用内购买。应用可以在显示Store给用户之前,或者在实际发起购买请求之前,进行这项检查。后者允许用户查看能够购买的products,即使应用内购买当前被禁止。
if ([SKPaymentQueue canMakePayments])
{
   ... // 向用户显示Store
}
else
{
   ... // 警告用户当前禁止应用内购买
}
4. 获取products的信息
应用创建一个SKProductsRequest对象,并初始化为一组你想要销售的product ID,添加一个delegate处理请求返回结果,然后就可以发起这个请求。响应结果保存了所有合法的products的本地化信息。应用必须首先获得product的信息,然后才能创建payment请求。
- (void) requestProductData
{
   SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObject: kMyFeatureIdentifier]];
   request.delegate = self;
   [request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    NSArray *myProduct = response.products;
    // 把信息显示到Store界面
    [request autorelease];
}
5. 增加一个用户界面,显示products给用户
Store Kit不提供用户界面类,如何显示Store给用户是应用的事情。
6. 注册一个transaction observer到payment队列
应用实例化一个transaction observer,并将其注册到payment队列。
MyStoreObserver *observer = [[MyStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
如前所述,应用最好在启动时注册observer。交易完成之前应用退出,App Store也仍然记得这些交易。启动时注册observer确保所有之前排队交易的结果都能够被应用接收到。
7. 在应用的MyStoreObserver对象中实现paymentQueue:updatedTransactions: 方法
observer的paymentQueue:updatedTransactions: 方法在新交易被创建或更新时都会被调用
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
            default:
                break;
        }
    }
}
8. observer在用户成功购买后提供相应的product
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
// 应用需要实现这两个方法:记录交易、提供内容
    [self recordTransaction: transaction];
    [self provideContent: transaction.payment.productIdentifier];
// 从payment队列中删除交易
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
成功的交易包含一个transactionIdentifier属性和一个transactionReceipt属性,记录了已处理支付的详细信息。应用不需要对这些信息做任何处理。当然你可能希望记录这些信息并为交易建立一个审查跟踪(audit trail)。如果使用服务器来递送内容,应用可以把receipt发送到服务器,然后由服务器向App Store验证该交易。
一旦你完成交付product给用户,应用必须调用finishTransaction: 来完成交易,交易将从payment队列中移除。为了确保products不会丢失,应用应该在调用finishTransaction: 之前交付内容。
9. 处理还原购买,结束交易
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
    [self recordTransaction: transaction];
    [self provideContent: transaction.originalTransaction.payment.productIdentifier];
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
这个方法类似于上面的购买。还原购买是一个新的交易,拥有不同的transaction ID和receipt。你可以单独保存这些信息,并建立审查跟踪。但是当完成交易时,你还是要还原原始的交易,那里面保存了实际的payment对象和product ID。
10. 处理失败购买,结束交易
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
    if (transaction.error.code != SKErrorPaymentCancelled)
    {
        // 可以显示一个错误(可选的)
    }
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
通常交易失败都是用户决定不购买。应用可以读取失败交易的error域,从而了解为何交易失败。
对于失败的交易,应用唯一需要做的是从队列中移除它。如果应用在交易失败后显示一个对话框告诉用户交易出错,应该避免在用户主动取消时也显示该错误。
11. 做完上面所有以后,你就可以显示用户界面。当用户在Store中选择一项时,应用就创建一个payment对象,并将其添加到payment队列中。
SKPayment *payment = [SKPayment paymentWithProductIdentifier:kMyFeatureIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
如果Store提供一次购买多个product的功能,你可以创建一个payment对象,并指定quantity属性
SKMutablePayment *payment = [SKMutablePayment paymentWithProductIdentifier:kMyFeatureIdentifier];
payment.quantity = 3;
[[SKPaymentQueue defaultQueue] addPayment:payment];
以上的代码示例主要用于内建product模型。如果你的应用使用服务器来交付内容,你需要设计和实现iOS应用与服务器之间通信的协议。你的服务器还需要在交付products给应用之前,先验证receipts。



验证Store receipt
应该接收到Apple Store Kit的receipt后,应该执行额外的receipt检查,来验证交易的合法性。如果应用依赖于独立的服务器来提供订阅、服务、可下载内容,验证receipt就非常重要。服务器验证receipt可确保iOS应用向服务器的请求是合法的。
Store receipt的内容和格式是私有的,并且有可能变化。应用不能试图直接解析receipt数据。使用下面描述的机制来验证receipt,并获得receipt中存储的信息。
向App Store验证一个Receipt
当Store Kit返回一个完成的购买到payment队列的observer时,交易的transactionReceipt属性包含一个签名的receipt,记录了所有关键的交易信息。服务器可以post这个receipt到App Store,来验证receipt是合法的,没有被篡改。向App Store查询使用JSON dictionary直接发送和接收,由RFC 4627定义。
执行以下步骤,来验证receipt:
1. 从transaction的transactionReceipt属性获取receipt数据,并使用Base64对其进行编码
2. 创建一个JSON对象,只有一个名为"receipt-data"的键,它的值是步骤1创建的字符串。JSON对象如下:
{
    "receipt-data" : "(actual receipt bytes here)"
}
3. 使用HTTP POST请求,Post这个JSON对象到App Store,URL为:https://buy.itunes.apple.com/verifyReceipt
4. 从App Store接收到的响应也是一个JSON对象,有两个键:status和receipt。如下所示:
{
    "status" : 0,
    "receipt" : { ... }
}
如果status的值为0,表示是一个合法的receipt。否则receipt非法。
Store Receipt
你发送给App Store的receipt数据编码了交易的信息。当App Store验证receipt时,会先解码receipt数据,并在响应中返回。receipt响应是一个JSON dictionary,包含了应用中SKPaymentTransaction对象的所有信息。因此服务器可以查询这些JSON域,来获取用户购买的详细信息。苹果推荐iOS应用只发送receipt数据给服务器,不发送交易数据给服务器,然后服务器再到App Store去验证receipt。App Store会验证receipt数据没有被篡改。服务器从App Store响应的receipt数据中获取交易信息,而不是由iOS应用直接发送交易信息给服务器,会更加安全。
下表列出了你可以从响应receipt中获取的信息,许多键直接对应于SKPaymentTransaction类的属性。表中没有指定的键都被苹果保留,不得使用。

描述
quantity购买的数量,对应于transaction.payment.quantity属性
product_idproduct ID标识,对应于transaction.payment.productIdentifier属性
transaction_idtransaction ID标识,对应于transaction.transactionIdentifier属性
purchase_date交易发生的日期和时间,对应于transaction.transactionDate属性
original_transaction_id对于还原交易,这个值保存了原始交易ID
original_purchase_date对于还原交易,这个值保存了原始交易日期
app_item_id字符串,App Store用来唯一地标识一个创建了支付交易的iOS应用。如果你的服务器支持多个iOS应用,你可以使用这个值来区分不同的应用。在sandbox中运行的应用没有app_item_id,因此这个键也不存在
version_external_identifier唯一标识你的应用修订版本的任意数值。sandbox应用没有这个键
bidiOS应用的Bundle ID
bvrsiOS应用的版本号



测试Store
在开发过程中,你需要测试应用Store来确保正常工作。但是又不能进行实际的用户支付,因此需要使用苹果提供的sandbox Store测试。Store Kit不能在iOS模拟器中使用,测试Store必须在真机上进行。
Sandbox环境
从Xcode启动应用时,Store Kit不会连接到App Store。相反会连接到特殊的Sandbox Store环境。Sandbox环境使用了App Store的基础架构,但是不会发生实际的支付。Sandbox环境返回成功的支付,就好像实际上购买了一样。Sandbox环境使用特殊的iTunes Connect账号,只能用于测试In-App Purchase。不能使用普通的iTunes Connect账号在Sandbox中测试。
你需要在iTunes Connect中创建一个或多个特殊的测试账号,至少每个本地化区域需要一个单独的测试账号。详细的信息可参考iTunes Connect Developer Guide.
Testing in the Sandbox
按以下步骤,在Sandbox中进行测试:
1. 在测试的iOS设备中退出当前iTunes账号
在测试应用之前,必须首先退出普通的iTunes账号。iOS 3.0在设置程序中包含了一个Store类别,你可以在里面退出当前账号
切记:不要在设置中登录你的测试账号,否则测试账号将失效
2. 运行你的应用
一旦你退出了普通账号,就可以在Xcode中运行你的应用。在应用中提交支付时,Store Kit会提示你授权交易。使用测试账号登录并同意支付。这时候不会发生扣款,但是交易会正常完成。
在Sandbox中验证Receipts
你也可以在Sandbox中验证receipt,执行验证的代码和普通App Store是一样的,但是Sandbox环境的URL不一样:
NSURL *sandboxStoreURL = [[NSURL alloc] initWithString: @"https://sandbox.itunes.apple.com/verifyReceipt"];



Auto-Renewable订阅
In-App Purchase提供标准化的方式,来实现自动再生订阅。自动再生订阅拥有以下特性:
1. 在iTunes Connect中配置自动再生订阅时,需要指定订阅的持续时间,以及其它选项。
2. Auto-renewable订阅会自动还原,而且和非消耗型product一样,都使用相同的Store Kit函数。原始交易,和每次的再生交易都会发送到应用。
3. 服务器向App Store验证receipt时,如果订阅活跃,并且被App Store自动再生过,App Store会返回一个更新的receipt
添加自动再生订阅到Store
按以下步骤实现auto-renewable订阅:
1. 连接到iTunes Connect,并创建一个新的"shared secret"。shared secret是一个密码,服务器在验证auto-renewable订阅的receipt时必须提供此密码。这样在与App Store之间就增加了一层额外的安全保护。
2. 在iTunes Connect中新增并配置一项新的auto-renewable订阅类型
3. 修改服务器的receipt验证代码,在发送到App Store的receipt JSON数据中增加shared secret。服务器的验证代码必须解析App Store的响应数据,以确定订阅是否过期。如果订阅被用户更新,App Store会返回最新的receipt给你的服务器。
设计你的iOS客户端应用
多数情况下,iOS应用只需要很小的修改,就能支持auto-renewable订阅。实际上,客户端应用现在变得更加简单,因为你可以使用相同的代码还原auto-renewable订阅,做法和还原非消耗型product一样:应用在订阅更新时,会接收到独立的transaction,应用只需单独验证每个receipt即可。
验证Auto-renewable订阅Receipt
和前面验证Store Receipts几乎相同。创建JSON对象,并POST到App Store。唯一的区别是增加第二个域:shared secret,也就是你在iTunes Connect创建的那个
{
    "receipt-data" : "(actual receipt bytes here)"
    "password"     : "(shared secret bytes here)"
}
响应包含一个status域,表示receipt是否通过验证
{
    "status" : 0,
    "receipt" : { ... }
    "latest_receipt" : "(base-64 encoded receipt)"
    "latest_receipt_info" : { ... }
}
如果用户的receipt合法,而且是活跃的订阅,status域为0,receipt域包含解码后的receipt数据。如果服务器接收到的status为非0值,可对应以下错误代码,来解析相应的错误。

Status描述
21000App Store不能读取你提供的JSON对象
21002receipt-data域的数据有问题
21003receipt无法通过验证
21004提供的shared secret不匹配你账号中的shared secret
21005receipt服务器当前不可用
21006receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送
21007receipt是Sandbox receipt,但却发送至生产系统的验证服务
21008receipt是生产receipt,但却发送至Sandbox环境的验证服务

注意:这里的状态码只能用于Auto-renewable订阅,其它类型的product不能使用这些状态码进行判断
App Store返回给你的服务器的JSON对象的receipt数据中,除了前面说过的几个键,增加或修改了以下几个键:
描述
expires_date订阅receipt的过期日期,GMT至今的秒数,还原交易时不包含这个键
original_transaction_id最初购买时的transaction ID,随后的订阅更新和还原都共享这个ID
original_purchase_date最初购买的日期,表示订阅的起始日期
purchase_date交易支付发生的日期,对于renewable订阅,这也可能是最近一次订阅更新的日期。

另外JSON还包含两个额外的域:latest_receipt、latest_expired_receipt_info,这两个都是receipt对象,你的服务器可以使用这两个域来记录最近的订阅更新。
还原Auto-Renewable订阅
App Store在每次更新订阅时都会创建独立的transaction。你的应用还原之前的购买时,Store Kit会把所有transaction都传递给应用。因此应用需要组合每个transaction的购买日期与订阅长度,然后确定该receipt是否合法。



上面内容基本来自iOS Developer Library的《In-App Purchase Programming Guide》


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值