in app purchase 教程中的restoreTransaction是怎么回事

  最近正在做一个iphone游戏内购买的项目,所以了解了一些In App Purchase相关的技术。  根据Apple官方文档,In App Purchase(IAP)有两种模型:内建模型(Built-in Model)和服务器模型(Server Model)。由于我做的项目需要用自己的服务器管理虚拟货币,因此自然就选择了服务器模型。  由于IAP的整个流程比较复杂,一篇博客的篇幅无法完全介绍清楚,所以我将用几篇博客的篇幅来做介绍。在这篇博客里就仅仅介绍IAP购买完成后如何通过将客户端收到的transactionReceipt发送到我自己的服务器(我用的Google App Engine,Python环境),再由我的服务器验证用户的购买行为是否已完成。  如果想要全面了解IAP流程的,请移步至apple文档:  以下是验证receipt的详细流程:  IAP在购买流程中,会给每一次购买行为创建一个SKPaymentTransaction,这个transaction会记录用户购买行为的状态,如正在购买(SKPaymentTransactionStatePurchasing),已购买(SKPaymentTransactionStatePurchased),购买失败(SKPaymentTransactionStateFailed)等。而当transaction状态是 SKPaymentTransactionStatePurchased的时候,客户端就能得到一个transaction.transactionReceipt。我的目的就是要从客户端发送这个receipt,然后服务器收到receipt后,通过POST方式发送receipt到app store,app store会验证receipt并返回验证结果,服务器收到结果后再判断验证是否成功。  第一步:发送receipt  首先在SKPaymentTransaction的状态改变的时候,会调用一个delegate:- (void)paymentQueue:(SKPaymentQueue&*)queue updatedTransactions:(NSArray&*)。我可以通过transactions数组得到里面的每一个transaction。再判断它的transactionState,如果是等于SKPaymentTransactionStatePurchased,就调用另外一个函数:completeTransaction,这个函数的具体实现:- (void)completeTransaction:(SKPaymentTransaction *)transaction {
// truely purchase IAP product for user
NSString* jsonObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes
length:transaction.transactionReceipt.length];
NSString* varStr = [[NSString alloc] initWithFormat:
@"your_url?receipt=%@",
jsonObjectString];
VMRequest* request = [[VMRequest alloc] initWithVariableStr:varStr
requestType:PURCHASED_IAP_PRODUCT
delegate:self];
[[VMRequestQueue sharedQueue] addRequest:request
toQueue:[VMRequestQueue sharedQueue].requestSetInfoQueue];
[mIAPRequestDict setObject:request forKey:transaction.transactionIdentifier];
[varStr release];
[request release];}  这个函数首先是执行了一段encode操作。这段操作是对transactionReceipt做了一次base64的编码(根据apple文档的要求)。编码的代码是我从网上找到的,代码如下:- (NSString *)encode:(const uint8_t *)input length:(NSInteger)length {
static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/=";
NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t *output = (uint8_t *)data.mutableB
for (NSInteger i = 0; i & i += 3) {
NSInteger value = 0;
for (NSInteger j = j & (i + 3); j++) {
value &&= 8;
if (j & length) {
value |= (0xFF & input[j]);
NSInteger index = (i / 3) * 4;
output[index + 0] =
table[(value && 18) & 0x3F];
output[index + 1] =
table[(value && 12) & 0x3F];
output[index + 2] = (i + 1) & length ? table[(value && 6)
& 0x3F] : '=';
output[index + 3] = (i + 2) & length ? table[(value && 0)
& 0x3F] : '=';
return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];}原文链接:  encode完成后,就开始将编码过的receipt发送到我的服务器。这一段很简单,只要先创建一段url格式的字符串,再创建url,然后在初始化request的时候添加这个url,最后创建connection就行了。其中VMRequest是我写的一个继承自NSMutableURLRequest的类,这里可以直接用NSMutableURLRequest代替。需要注意的是这里的request最好使用POST方式,因为GET方式很可能因为发送的receipt数据量过大而导致数据丢失。VMRequestQueue是VMRequest的队列,这个队列会自动按顺序执行队列中的request,这部分可以改成直接创建NSURLConnection。总之,只要创建好需要访问的URL,再用POST方式发送到自己的服务器,这第一步就完成了。  第二步:向app store验证  这一步中所做的事全部是在服务器端完成。我的服务器使用的是Google App Engine(GAE)的Python环境。  首先,服务器收到receipt。因为这段receipt是已经经过编码的,所以不需要再度编码,唯一要做的就是生成一个json格式的数据,发送到app store并获取返回的数据。具体代码如下:import httplib&# have to import simplejson because google app engine uses python 2.5. When it upgrades, can import json directlyimport simplejson as jsonjsonStr = json.dumps({"receipt-data": receipt})#connect = httplib.HTTPSConnection("buy.")# sandboxconnect = httplib.HTTPSConnection("sandbox.")headers = {"Content-type": "application/json"}connect.request("POST", "/verifyReceipt", jsonStr)result = connect.getresponse()data = result.read()connect.close()decodedJson = json.loads(data)status = decodedJson[u'status']if status == 0:
return decodedJsonelse:
return False  simplejson是python2.5支持的一个json库,使用simplejson的原因是GAE只支持到python2.5。如果GAE版本更新到2.6及以上了,那就可以直接import json。  json对象的格式有点类似于一个dictionary,一个key对应于一个value。  json.dumps是将一个json对象序列化成一段字符串,相对的json.loads就是将一段字符串反序列化成一个json对象。  httplib是一个python自带的库,用来做http连接。根据apple文档,代码中的request类型必须是POST,这一点需要注意。  最后,在读取json对象的数据的时候,必须要在key的前面加&u&。  结果:服务器收到解码的receipt  服务器收到的解码的receipt格式是:  {u'status': &&status&, u'receipt': {u'product_id': u'&product_id&', u'original_transaction_id': u'&&original_transaction_id&', u'bid': u'&bid&', u'original_purchase_date': u'&&original_purchase_date&', u'bvrs': u'&&bvrs&', u'purchase_date': u'&&purchase_date&', u'item_id': u'&item_id&', u'transaction_id': u'&&transaction_id&', u'quantity': u'&&quantity&'}}  这其中,status就表示receipt是否通过验证;receipt后又是一个json对象,里面product_id就是用户购买的IAP产品的id。最后一步,判断status是否等于0。如果等于0,就执行用户购买后应该实现的操作,最终再将结果反映到客户端上。In App Purchases(IAP 应用程序內购买): 完全攻略 (转) - wenxp2006 - 博客园
概况IAP能正常工作的秘诀:分成两个步骤:创建及提取产品描述购买产品第一个步骤是你可能遇到问题的部分。一旦你在代码中成功地获取了产品描述,编写购买产品的代码不过是小菜一碟。我们先看看步骤1。创建及提取产品描述下面是有关创建产品及提取其描述的非常粗略的步骤:创建唯一的App ID生成及安装新的provisioning profile文件在Xcode中更新 bundle ID 及 code signing profile如果还没做的话,请在iTunes Connect中提交有关你程序的 metadata如果还没做的话,请在iTunes Connect中提交你程序的二进制码为IAP添加新产品编写提取产品描述的代码等待几小时提取产品描述的代码非常简单,但其他步骤则很容易错。注意: 为提取产品描述,你并不需要在iTunes Connect中创建IAP测试用户。1. 创建唯一的App ID为支持IAP,你的App ID不能包括通配符(&*&)。为确定你的App Id是否包括通配符,请登录,在 iPhone Developer Program Portal中选择左边菜单中的 &App IDs&检查你的 App ID。下面是一个唯一的App ID:<.runm*****ter.runm*****terfree下面不是一个唯一的 App ID:<.runm*****ter.*如果你还没有一个唯一的App ID,按如下步骤创建一个:在developer portal中的 App IDs 部分,选择&New App ID&填写下列信息:Display name(显示名): 选取一个不同的App ID的名称。你不能编辑或删除旧的App ID,所以你必须为你的App ID提供一个新名称以避免溷淆。Prefix(前缀): 生成一个新的前缀,或者如果你的程序是通过Keychain Services API分享数据的系列程序中之一的话,则选用已存在的前缀。Suffix(后缀): panyname.appname (这是通用格式 & 注意没有使用通配符)。按 &Save&按 App ID旁的&Configure& 链接选取 &Enable In App Purchase&选择框按&Done&2. 创建一个新的Provisioning Profile文件在创建了新的App ID后,你需要生成一个指向这个App ID的新provisioning profile。下面就是令人痛苦的生成和安装新provisioning profile的详细步骤:在 iPhone Developer Portal中, 选择左边的Provisioning部分确保你处于Development 标籤下, 按下右上角的 &New Profile&填入所需信息并指向你刚创建的唯一的App ID如果你在Acti*****条目下看到 &Pending&,那麽请按下&Development&标籤标题进行刷新点击 &Download& 下载新的profile文件将profile文件拖入到Dock中Xcode图标上进行安装如果你想在硬盘上保存provisioning profile,那麽你可以按如下步骤手工安装profile:在Xcode中, 选择 Window & Organizer选择左边 &Provisioning Profiles& 分类Ctrl-按下profile & Reveal in Finder将新profile拖入到 profile Finder 窗口3. 更新Xcode 设置在Xcode中安装了 profile 文件后,你需要对使用此provisiong profile的项目进行一些编辑工作:编辑项目 .plist 文件使其 Bundle ID 与 App ID 匹配。忽略ID开始部分的字母数字序列。例如,在Developer Portal中你的App ID为&.runm*****ter.runm*****terfree&,那麽在Bundle ID中你只需输入&com.runm*****ter.runm*****terfree& 。编辑项目的 target 信息以使用新的provisioning profile:选取 Project & Edit Active Target选取顶部&Build& 标籤选取需要的 configuration (通常为 Debug)在Code Signing Identity中选择新的provisioning profile在Code Signing Identity之下的行中(可能名为 Any iPhone OS Device)选择新的provisioning profile4. 添加你的应用程序如果你的程序已经发表到App Store了,那麽可以略过此步骤。在你将产品添加到 iTunes Connect之前,你必须添加此产品所需的程序。如果你的程序还没有100%完成也无需担心,你可以先提交具有部分数据的程序,最后再提交真实的程序。注意: 只有 SKU 和 version(版本)部分是以后不可修改的登录到&点击右边链接进入 iTunes Connect注意:你必须先登录到,否则会有不测发生(译者注:具体是什麽不测我也不太清楚,胆大的请自己试一下)在 iTunes Connect主页点击 &Manage Your Applicati*****&在右上角点击&Create New Application&填写程序所需的一切信息。当要求程序二进制码时,请选择稍后上传选项。5. 提交程序二进制码Apple的文档中没有任何地方提及详情,但它却是必须的步骤。要成功测IAP功能,你必须提交程序的二进制码。即使你的程序还没有100%完成,你仍然需要提交二进制码。然而,你也可以立即摈弃你的二进制码,使其不会进入审核阶段。下面这些步骤非常关键,我可是因为少做了某些步骤而度过了一段非常痛苦的时间:生成App Store发佈版程序如果你不知怎麽做,请在 iPhone Developer Portal 中点击左方的 Distribution标籤,并选择 &Prepare App& 标籤。然后,根据蓝色链接的指示:获取iPhone发行许可证创建并下载在App Store发行所需的iPhone Distribution Provisioning Profile在Xcode中生成程序的发行版在iTunes Connect中进入程序页选择 &Upload Binary&上传.zip压缩程序如果你的程序还没有100%完成以进行审核,那麽请点击iTunes Connect中你程序首页中的 &Reject Binary&链接。程序的状态应该更新为 &Developer Rejected&.不用担心,由于程序的状态是&Developer Rejected&,Apple是不会对其进行审核的。你可以在任何时候提交程序的新版本并使其状态为&Developer Rejected&,这不会对以后程序正式提交的等待时间有任何影响。6. 添加产品完成了以上所有步骤后,我们最终可以向iTunes Connect中添加产品了。确保登录到&进入 iTunes Connect 主页点击&Manage Your Applicati*****&点击刚建好的程序 点击view details点击 &Manage in-App Purchases& 链接点击 &Create New&填写下列产品信息:Reference Name(参考名称): 产品的通用名称。比如,我使用的是 &Pro Upgrade&。此名称是不允许进行编辑的,它不会显示于App Store中。Product ID(产品ID): 你产品的唯一id。通常格式是 pany.appname.product,但它可以说任何形式。它并不要求以程序的App ID作为前缀。Type(类型): 有三种选择Non-c*****umable(非消耗品): 仅需付费一次 (例如你希望将出现从免费版升级为专业版)C*****umable(消耗品): 每次下载都需要付费Subscription(预订): 循环反覆Price Tier(价格等级): 产品价格。参见不同等级的价格列表。Cleared for Sale(等待销售): 一定要选取此项,否则的话,测试时会发生非法产品ID的错误。Language to Add(增加的语言): 选一项。下列两项将出现:Displayed Name(显示名称): 用户看到的产品名称。比如我选择 &Upgrade to Pro&。Description(描述): 对产品进行描述。此处输入的文本将与Displayed Name 及 Price 一起在你代码中提取 SKProduct时出现。Screenshot(截屏): 展示你产品的截屏。儘管屏幕上会显示&提交截屏会触发产品审核过程&之类的文字(个人拙见,这是非常糟糕的设计),你还是可以安全地提交截屏而不会使产品进入审核过程。存储后,选择&Submit with app binary& (随程序二进制码一起提交)选项。是产品与程序二进制绑定在一起,所以在你最后正式提交100%完成的程序二进制码时,产品也会随之提交。点击 &Save&最后一定不要忘了回到view details &编辑In-App Purchases选择刚刚添加的iap版本7. 编写代码下面我们开始编写代码对刚加入到iTunes Connect中的产品信息进行提取。我访问产品数据,我们需要使用 StoreKit framework。注意: StoreKit 无法在模拟器上工作。你必须在真机上进行测试。1.添加 StoreKit framework 到你的项目中。2.添加SKProduct引用到你的 .h 文件中:
// InAppPurchaseManager.h
#import &StoreKit/StoreKit.h&
#define kInAppPurchaseManagerProductsFetchedNotification @"kInAppPurchaseManagerProductsFetchedNotification"
@interface InAppPurchaseManager : NSObject &SKProductsRequestDelegate&
&&&&SKProduct *proUpgradeP
&&&&SKProductsRequest *productsR
注意: InAppPurchaseManager 是一个单例类,它处理程序中所有IAP任务。它是本文中的示例程序。3.产品请求,并在相应.m文件中实现代理协议:
// InAppPurchaseManager.m
- (void)requestProUpgradeProductData
&&&&NSSet *productIdentifiers = [NSSet setWithObject:@"com.runm*****ter.runm*****terfree.upgradetopro" ];
&&&&productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
&&&&productsRequest.delegate =
&&&&[productsRequest start];
&&&&// we will release the request object in the delegate callback
#pragma mark -
#pragma mark SKProductsRequestDelegate methods
- (void)productsRequest:(SKProductsRequest *)request didReceiveResp*****e:(SKProductsResp*****e *)resp*****e
&&&&NSArray *products = resp*****e.
&&&&proUpgradeProduct = [products count] == 1 ? [[products firstObject] retain] :
&&&&if (proUpgradeProduct)
&&&&&&&&NSLog(@"Product title: %@" , proUpgradeProduct.localizedTitle);
&&&&&&&&NSLog(@"Product description: %@" , proUpgradeProduct.localizedDescription);
&&&&&&&&NSLog(@"Product price: %@" , proUpgradeProduct.price);
&&&&&&&&NSLog(@"Product id: %@" , proUpgradeProduct.productIdentifier);
&&&&for (NSString *invalidProductId in resp*****e.invalidProductIdentifiers)
&&&&&&&&NSLog(@"Invalid product id: %@" , invalidProductId);
&&&&// finally release the reqest we alloc/init&ed in requestProUpgradeProductData
&&&&[productsRequest release];
&&&&[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
上面代码有几点需要注意:指定产品id时,你必须使用完整产品id。例如,上例中使用 &com.runm*****ter.runm*****terfree.upgradetopro&。仅使用 &upgradetopro& 将不会正常工作。如果在productsRequest:didReceiveResp*****e:中resp*****e.products 为 nil,而你的产品id出现于 resp*****e.invalidProductIdentifers 数组中时,那麽请做好心理准备开始一场徒劳的搜索战吧。 StoreKit API没有提供任何帮助,也没有任何指示关于为什麽你的id是无效的。很可爱,不是吗?SKProduct类提供了有关程序标题和描述的本地化版本,但是价格则没有本地化版本。下面是针对此疏忽提供的代码:
// SKProduct+LocalizedPrice.h
#import &Foundation/Foundation.h&
#import &StoreKit/StoreKit.h&
@interface SKProduct (LocalizedPrice)
@property (nonatomic, readonly) NSString *localizedP
// SKProduct+LocalizedPrice.m
#import "SKProduct+LocalizedPrice.h"
@implementation SKProduct (LocalizedPrice)
- (NSString *)localizedPrice
&&&&NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
&&&&[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
&&&&[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
&&&&[numberFormatter setLocale:self.priceLocale];
&&&&NSString *formattedString = [numberFormatter stringFromNumber:self.price];
&&&&[numberFormatter release];
&&&&return formattedS
加入上述代码,测试一下。你应该在控制台窗口中看见产品信息了。然而更大的可能是,你得到了一个无效的产品id。我下一篇文章将介绍怎样对这个问题进行调试。但是,下面的步骤8有可能是阻碍你前进的障碍。8. 等待几小时遵循了上述所有步骤,但是你的产品仍然是无效的?你是否两次,三次,四次不懈努力地确认你是否遵循了上面提到的每个步骤?你是否已经对网上IAP信息少得可怜而感到绝望?那麽,你应该等待。你的产品要进入iTunes Connect使得Apple准备好沙箱环境需要一些时间。对于我而言,我是经过了无数次产品无效错误的绝望。而在24小时后,我没有修改任何一行代码,但产品id变为有效。我认为要使产品发佈到Apple的网络系统需要几个小时的时间,但如果你有时间的话,你可以像我一样等上24个小时。购买产品至此你应该已经成功地获取了 SKProduct 描述。比较而言,支持购买产品相对简单些。仅需下面三个步骤:编写代码支持事务(transaction)在iTunes Connect中添加程序测试用户在设备中登录你的 iTunes Store 帐号购买测试我们从编写支持事务所需代码开始。1. 编写代码支持事务首先注意:你将负责开发产品购买的用户界面。StoreKit 未提供任何与用户界面相关的元素。如果你希望你的购买用户界面与App Store一样,那麽你要自己完成。下面所有代码都是有关事务处理的后台部分。这是一个单独的类只有一条简单的API以供外部类(比如view controller)调用进行购买。如果你找到将其集成到你程序的购买部分的方法,那麽我推荐你使用类似方桉。首先,需要遵循 SKPaymentTransactionObserver 协议:
// InAppPurchaseManager.h
// add a couple notificati***** sent out when the transaction completes
#define kInAppPurchaseManagerTransactionFailedNotification @"kInAppPurchaseManagerTransactionFailedNotification"
#define kInAppPurchaseManagerTransacti*****ucceededNotification @"kInAppPurchaseManagerTransacti*****ucceededNotification"
@interface InAppPurchaseManager : NSObject &SKProductsRequestDelegate, SKPaymentTransactionObserver&
// public methods
- (void)loadS
- (BOOL)canMakeP
- (void)purchaseProU
上面我们定义了两个新的notification,它们将作为购买事务的结果被发送。在上例中我们仍然使用与获取产品描述同一个InAppPurchaseManager类。
// InAppPurchaseManager.m
#define kInAppPurchaseProUpgradeProductId @"com.runm*****ter.runm*****terfree.upgradetopro"
#pragma Public methods
// call this method once on startup
- (void)loadStore
&&&&// restarts any purchases if they were interrupted last time the app was open
&&&&[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
&&&&// get the product description (defined in early secti*****)
&&&&[self requestProUpgradeProductData];
// call this before making a purchase
- (BOOL)canMakePurchases
&&&&return [SKPaymentQueue canMakePayments];
// kick off the upgrade transaction
- (void)purchaseProUpgrade
&&&&SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId];
&&&&[[SKPaymentQueue defaultQueue] addPayment:payment];
#pragma Purchase helpers
// saves a record of the transaction by storing the receipt to disk
- (void)recordTransaction:(SKPaymentTransaction *)transaction
&&&&if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseProUpgradeProductId])
&&&&&&&&// save the transaction receipt to disk
&&&&&&&&[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:@"proUpgradeTransactionReceipt" ];
&&&&&&&&[[NSUserDefaults standardUserDefaults] synchronize];
// enable pro features
- (void)provideContent:(NSString *)productId
&&&&if ([productId isEqualToString:kInAppPurchaseProUpgradeProductId])
&&&&&&&&// enable the pro features
&&&&&&&&[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isProUpgradePurchased" ];
&&&&&&&&[[NSUserDefaults standardUserDefaults] synchronize];
// removes the transaction from the queue and posts a notification with the transaction result
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
&&&&// remove the transaction from the payment queue.
&&&&[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
&&&&NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, @"transaction" , nil];
&&&&if (wasSuccessful)
&&&&&&&&// send out a notification that we&ve finished the transaction
&&&&&&&&[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransacti*****ucceededNotification object:self userInfo:userInfo];
&&&&&&&&// send out a notification for the failed transaction
&&&&&&&&[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
// called when the transaction was successful
- (void)completeTransaction:(SKPaymentTransaction *)transaction
&&&&[self recordTransaction:transaction];
&&&&[self provideContent:transaction.payment.productIdentifier];
&&&&[self finishTransaction:transaction wasSuccessful:YES];
// called when a transaction has been restored and and successfully completed
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
&&&&[self recordTransaction:transaction.originalTransaction];
&&&&[self provideContent:transaction.originalTransaction.payment.productIdentifier];
&&&&[self finishTransaction:transaction wasSuccessful:YES];
// called when a transaction has failed
- (void)failedTransaction:(SKPaymentTransaction *)transaction
&&&&if (transaction.error.code != SKErrorPaymentCancelled)
&&&&&&&&// error!
&&&&&&&&[self finishTransaction:transaction wasSuccessful:NO];
&&&&&&&&// this is fine, the user just cancelled, so don&t notify
&&&&&&&&[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
#pragma mark -
#pragma mark SKPaymentTransactionObserver methods
// called when the transaction status is updated
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransacti*****:(NSArray *)transacti*****
&&&&for (SKPaymentTransaction *transaction in transacti*****)
&&&&&&&&switch (transaction.transacti*****tate)
&&&&&&&&&&&&case SKPaymentTransacti*****tatePurchased:
&&&&&&&&&&&&&&&&[self completeTransaction:transaction];
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&case SKPaymentTransacti*****tateFailed:
&&&&&&&&&&&&&&&&[self failedTransaction:transaction];
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&case SKPaymentTransacti*****tateRestored:
&&&&&&&&&&&&&&&&[self restoreTransaction:transaction];
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&default:
&&&&&&&&&&&&&&&&
要测试上面的新代码,你还需要编写调用 loadStore, canMakePurchases 以及 purchaseProUpgrade 方法的代码。有关上述代码的详细解释,请参考官方 In App Purchase Programming Guide (IAP编程指南)上述代码有几个部分是针对我的程序的。例如,在 provideContent:中,NSUserDefaults 中的@&isProUpgradePurchased& BOOL 字段被设定为 YES。程序的其他部分将检查此BOOL值以确定是否需要启动专业版功能。如果你正好也要实现免费升级专业版的功能,那麽你可以使用同样的方法。2. 添加测试用户为测试上述代码,你需要在 iTunes Connect 中创建测试用户以对IAP功能进行测试。你可以使用测试帐号购买产品而不被Apple收取费用。按以下步骤创建测试用户:登录到&进入 iTunes Connect选择iTunes Connect首页中的 &Manage Users&选择 &In App Purchase Test User&选择 &Add New User&填入用户信息. 所有信息都不必是合法的。建议使用虚假简短的email地址及简短的密码。选择 &Save&测试时你需要输入这些email地址和密码。3. 在你的设备中退出登录在进行程序购买功能测试前,你必须在你的设备中退出iTunes Store。遵循以下步骤:打开Settings App点击 &Store& 行点击 &Sign Out&4. 购买测试现在,终于可以开始进行IAP功能的测试了。测试很简单:运行你设备中的程序进行购买当程序提示输入用户名和密码时,输入参数用户的信息如果你使用同一账户进行购买时,系统将提示你已经购买了此产品。按&Yes&就可以再次下载此产品。总结实现IAP功能比想象的要複杂许多。我可是经过无数痛苦的经历才完成我的程序。希望能够帮助其他开发者减轻他们的痛苦。应用程序內购买
阅读(...) 评论()

我要回帖

更多关于 storepurchaseapp 的文章

 

随机推荐