转自:http://hi.baidu.com/sz9808/item/9e975abcdf1c6ef362388e93

下面介绍在购买过程中用到的几个类:
SKPayment
要收集支付信息,先要了解一下支付对象。 支付对象包含了商品的标识(identifier)和要购买商品的数量(quantity)(数量可选)。你可以把同一个支付对象重复放入支付队列,,每一次这样的动作都相当于一次独立的支付请求。

用户可以在Settings程序中禁用购买的功能。 因此在请求支付之前,程序应该首先检查支付是否可以被处理。 调用SKPaymentQueue的canMakePayments方法来检查。

SKPaymentQueue
支付队列用以和App Store之间进行通信。 当新的支付对象被添加到队列中的时候, Store Kit向App Store发送请求。 Store Kit将会弹出对话框询问用户是否确定购买。 完成的交易将会返回给程序的observer对象。

SKPaymentTransaction
transaction对象在每次添加新的payment到队列中的时候被创建。 transaction对象包含了一些属性,可以让程序确定当前的交易状态。

程序可以从支付队列那里得到一份审核中的交易列表,但更常用的做法还是等待支付队列告知交易状态的更新。

SKPaymentTransactionObserver
在程序中实现SKPaymentTransactionObserver的协议,然后把它作为SKPaymentQueue对象的观察者。该观察者的主要职责是:检查完成的交易,交付购买的内容,和把完成后的交易对象从队列中移除。

在程序一启动,就应该为支付队列指定对应的观察者对象,而不是等到用户想要购买商品的时候。 Transaction对象在程序退出时不会丢失。程序重启时, Store Kit继续执行未完成的交易。 在程序初始化的时候添加观察者对象,可以保证所有的交易都被程序接收(也就时说,如果有未完成的transaction,如果程序重启,就重新开始了,如果稍候再添加观察者,就可能会漏掉部分交易的信息)。


恢复交易信息(Transactions)
当transaction被处理并从队列移除之后,正常情况下,程序就再也看不到它们了。 如果你的程序提供的是非消耗性的或是订阅类的商品,就必须提供restore的功能,使用户可以在其他设备上重新存储购买信息。

Store Kit提供内建的功能来重新存储非消耗商品的交易信息。 调用SKPaymentQueue的restoreCompletedTransactions的方法来重新存储。对于那些之前已经完成交易的非消耗性商品,Apple Store生成新的,用于恢复的交易信息。 它包含了原始的交易信息。你的程序可以拿到这个信息,然后继续为购买的功能解锁。 当之前所有的交易都被恢复时, 就会调用观察者对象的paymentQueueRestoreCompletedTransactionsFinished方法。

如果用户试图购买已经买过的非消耗性商品,程序会收到一个常规的交易信息,而不是恢复的交易信息。但是用户不会被再次收费。程序 应把这类交易和原始的交易同等对待。

订阅类服务和消耗类商品不会被Store Kit自动恢复。 要恢复这些商品,你必须在用户购买这些商品时,在你自己的服务器上记录这些交易信息, 并且为用户的设备提供恢复交易信息的机制。
在程序中添加Store功能
本章为添加购买功能的指导

详细流程:

准备工作当然是添加StoreKit.framework了。
然后是具体的步骤:

1. 决定在程序内出售的商品的类型。
之前提到过,程序内可以出售的新feature类型是有限制的。 Store Kit不允许我们下载新的代码。 你的商品要么可以通过当前的代码工作(bundle类型),要么可以通过服务器下载(当然,这里下载的为数据文件,代码是不可以的)。 如果要修改源代码,就只能老实的升级了。

2. 通过iTunes Connect注册商品
每次添加新商品的时候都需要执行这一步骤。 每个商品都需要一个唯一的商品标识。 App Store通过这个标识来查找商品信息并处理支付流程。 注册商品标识的方法和注册程序的方法类似。

要了解如何创建和注册商品信息,请参考“iTunes Connect Developer Guide”文档。   

3. 检测是否可以进行支付
用户可以禁用在程序内部支付的功能。在发送支付请求之前,程序应该检查该功能是否被开启。程序可在显示商店界面之前就检查该设置(没启用就不显示商店界面了),也可以在用户发送支付请求前再检查,这样用户就可以看到可购买的商品列表了。

例子:
if([SKPaymentQueue canMakePayments])
{
    ...//Display a store to the user
}
else
{
    ...//Warn the user that purchases are disabled.
}

4. 获得商品的信息
程序创建SKProductsRequest对象,用想要出售的商品的标识来初始化, 然后附加上对应的委托对象。 该请求的响应包含了可用商品的本地化信息。

//这里发送请求
- (void)requestProductData
{
    SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:
    [NSSet setWithObject: kMyFeatureIdentifier]];
    
    request.delegate = self;
    [request start];
}

//这个是响应的delegate方法
- (void)productsRequest: (SKProductsRequest *)request
didReceiveResponse: (SKProductsResponse *)response
{
    NSArray *myProduct = response.products;

    //生成商店的UI
    [request autorelease];
}

5. 添加一个展示商品的界面
Store Kit不提供界面的类。 这个界面需要我们自己来设计并实现。

6. 为支付队列(payment queue)注册一个观察者对象
你的程序需要初始化一个transaction observer对象并把它指定为payment queue的观察者。

上代码:

MyStoreObserver *observer = [[MyStoreObserver alloc]init];
[[SKPaymentQueue defaultQueue]addTransactionObserver: observer];

应该在程序启动的时候就添加好观察者,原因前面说过,重启后程序会继续上次未完的交易,这时就添加观察者对象就不会漏掉之前的交易信息。

7. 在MyStoreObserver类中执行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. 观察者对象在用户成功购买一件商品时,提供相应的内容,以下是在交易成功后调用的方法
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
    //你的程序需要实现这两个方法
    [self recordTransaction: transaction];
    [self provideContent: transaction.payment.productIdentifier];
    
    //将完成后的交易信息移出队列
    [[SKPaymentQueue defaultQueue]finishTransaction: transaction];
}

交易成功的信息包含transactionIdentifier和transactionReceipt的属性。其中,transactionReceipt记录了支付的详细信息,这个信息可以帮助你跟踪、审(我们的)查交易,如果你的程序是用服务器来交付内容,transactionReceipt可以被传送到服务器,然后通过App Store验证交易。(之前提到的server模式,可以参考以前的图)

9. 如果交易是恢复过来的(restore),我们用这个方法来处理:
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
    [self recordTransaction: transaction];
    [self provideContent: transaction.payment.productIdentifier];

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
这个过程完成购买的过程类似。 恢复的购买内容提供一个新的交易信息,这个信息包含了新的transaction的标识和receipt数据。 如果需要的话,你可以把这些信息单独保存下来,供追溯审(我们的)查之用。但更多的情况下,在交易完成时,你可能需要覆盖原始的transaction数据,并使用其中的商品标识。

10. 交易过程失败的话,我们调用如下的方法:
- (void)failedTransaction: (SKPaymentTransaction *)transaction
{
    if(transaction.error.code != SKErrorPaymentCancelled)
    {
        //在这类显示除用户取消之外的错误信息
    }

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

通常情况下,交易失败的原因是取消购买商品的流程。 程序可以从error中读出交易失败的详细信息。

显示错误信息不是必须的,但在上面的处理方法中,需要将失败的交易从支付队列中移除。 一般来说,我们用一个对话框来显示错误信息,这时就应避免将用户取消购买这个error显示出来。

11. 组织好程序内“商店”的UI。当用户选择一件商品时, 创建一个支付对象,并放到队列中。
SKPayment *payment = [SKPayment paymentWithProductIdentifier: kMyFeatureIdentifier];
[[SKPaymentQueue defaultQueue] addPayment: payment];

如果你的商店支持选择同一件商品的数量,你可以设置支付对象的quantity属性
SKMutablePayment *payment = [SKMutablePayment paymentWithProductIdentifier: kMyFeatureIdentifier];
payment.quantity = 3;
[[SKPaymentQueue defaultQueue] addPayment: payment];


下一步:
本章中所示代码可用于内置型商品模式(Built-in)。 如果你的程序要使用服务器来发布商品,你需要负责设计和执行iPhone程序和你的服务器之间的通信。服务器应该验证数据并为程序提供内容。

验证store的收据

使用服务器来交付内容,我们还需要做些额外的工作来验证从Store Kit发送的收据信息。

重要信息:来自Store的收据信息的格式是专用的。 你的程序不应直接解析这类数据。可使用如下的机制来取出其中的信息。

验证App Store返回的收据信息
当交易完成时,Store Kit告知payment observer这个消息,并返回完成的transaction。 SKPaymentTransaction的transactionReceipt属性就包含了一个经过签名的收据信息,其中记录了交易的关键信息。你的服务器要负责提交收据信息来确定其有效性,并保证它未经过篡改。 这个过程中,信息被以JSON数据格式发送给App Store,App Store也以JSON的格式返回数据。
(大家可以先了解一下JSON的格式)

验证收据的过程:

1. 从transaction的transactionReceipt属性中得到收据的数据,并以base64方式编码。
2. 创建JSON对象,字典格式,单键值对,键名为"receipt-data", 值为上一步编码后的数据。效果为:
{
    "receipt-data"    : "(编码后的数据)"
}

3. 发送HTTP POST的请求,将数据发送到App Store,其地址为:
https://buy.itunes.apple.com/verfyReceipt

4. App Store的返回值也是一个JSON格式的对象,包含两个键值对, status和receipt:
{
    "status"    : 0,
    "receipt"    : { … }
}

如果status的值为0, 就说明该receipt为有效的。 否则就是无效的。

App Store的收据
发送给App Store的收据数据是通过对transaction中对应的信息编码而创建的。 当App Store验证收据时, 将从其中解码出数据,并以"receipt"的键返回。 返回的响应信息是JSON格式,被包含在SKPaymentTransaction的对象中(transactionReceipt属性)。Server可通过这些值来了解交易的详细信息。 Apple建议只发送receipt数据到服务器并使用receipt数据验证和获得交易详情。 因为App Store可验证收据信息,返回信息,保证信息不被篡改,这种方式比同时提交receipt和transaction的数据要安全。(这段得再看看)

表5-1为交易信息的所有键,很多的键都对应SKPaymentTransaction的属性。
备注:一些键取决于你的程序是链接到App Store还是测试用的Sandbox环境。更多关于sandbox的信息,请查看"Testing a Store"一章。

Table 5-1 购买信息的键:

键名        描述
quantity     购买商品的数量。对应SKPayment对象中的quantity属性
product_id    商品的标识,对应SKPayment对象的productIdentifier属性。
transaction_id        交易的标识,对应SKPaymentTransaction的transactionIdentifier属性
purchase_date    交易的日期,对应SKPaymentTransaction的transactionDate属性
original_-transaction_id    对于恢复的transaction对象,该键对应了原始的transaction标识
original_purchase_-date    对于恢复的transaction对象,该键对应了原始的交易日期
app_item_id    App Store用来标识程序的字符串。一个服务器可能需要支持多个server的支付功能,可以用这个标识来区分程序。链接sandbox用来测试的程序的不到这个值,因此该键不存在。
version_external_-identifier    用来标识程序修订数。该键在sandbox环境下不存在
bid    iPhone程序的bundle标识
bvrs    iPhone程序的版本号

测试Store功能
开发过程中,我们需要测试支付功能以保证其工作正常。然而,我们不希望在测试时对用户收费。 Apple提供了sandbox的环境供我们测试。

备注:Store Kit在模拟器上无法运行。 当在模拟器上运行Store Kit的时候,访问payment queue的动作会打出一条警告的log。测试store功能必须在真机上进行。

Sandbox环境
使用Sandbox环境的话,Store Kit并没有链接到真实的App Store,而是链接到专门的Sandbox环境。 SandBox的内容和App Store一致,只是它不执行真实的支付动作。 它会返回交易成功的信息。 Sandbox使用专门的iTunes Connect测试 账户。不能使用正式的iTunes Connect账户来测试。

要测试程序,需要创建一个专门的测试账户。你至少需要为程序的每个区域创建至少一个测试账户。详细信息,请查看iTunes Connect Developer Guide文档。

在Sandbox环境中测试
步骤:
1. 在测试的iPhone上退出iTunes账户
Settings中可能会记录之前登录的账户,进入并退出。

重要信息:不能在Settings 程序中通过测试账户登录。

2. 运行程序
当你在程序的store中购买商品后,Store kit提示你去验证交易。用测试账户登录,并批准支付。 这样虚拟的交易就完成了。

在Sandbox中验证收据
验证的URL不同了:
NSURL *sandboxStoreURL = [[NSURL alloc]initWithString: 
@"https://sandbox.itunes.apple.com/verifyReceipt"];

posted on 2012-09-13 11:43  YanLiu  阅读(652)  评论(0)    收藏  举报