In App Purchases(IAP 应用程序內购买): 完全攻略 (2合1)

原文: http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/
参考: http://www.cocoachina.com/bbs/read.php?tid-11357.html
新: http://www.cocoachina.com/gamedev/misc/2012/0409/4129.html     demo
第一印象觉得In-App Purchase(简称IAP)非常简单。 Apple 提供的大量文档应该让 开发 者很快熟悉地熟悉。那麽,为什麽在你的 应用 中集成IAP特性就如此令人生厌呢?

这是因为在开发过程中不可避免会出现一些错误。而但这些错误发生的时候,你就抓瞎了。虽然Apple提供了有关IAP的大量文档,但他们并未提及集成IAP的详细步骤。而且对StoreKit集成过程中出现的 问题 也没有一个核对清单。另外对于为什麽诸如产品ID非法之类的问题也没有提供NSError之类的对象来告诉你原因。

在试用了各种可能的解决方桉后,你只能身心疲惫,彷徨无助。

为了提高你的效率和减少你的痛苦,我觉定利用此文来介绍一下实现IAP的详细步骤。本文很详细,有点长。甚至可能太长了,但不像Apple的文档,它提供了为实现IAP的每一个步骤。

废话少说,我们直入主题吧。


概况
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是否包括通配符,请登录 http://developer.apple.com/iphone ,在  iPhone  Developer Program Portal中选择左边菜单中的 “App IDs”检查你的 App ID。

下面是一个唯一的App ID:

7DW89RZKLY.com.runmonster.runmonsterfree

下面不是一个唯一的 App ID:

7DW89RZKLY.com.runmonster.*

如果你还没有一个唯一的App ID,按如下步骤创建一个:

在developer portal中的 App IDs 部分,选择“New App ID”
填写下列信息:
Display name(显示名): 选取一个不同的App ID的名称。你不能编辑或删除旧的App ID,所以你必须为你的App ID提供一个新名称以避免溷淆。
Prefix(前缀): 生成一个新的前缀,或者如果你的程序是通过Keychain Services API分享数据的系列程序中之一的话,则选用已存在的前缀。
Suffix(后缀): com.companyname.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
如果你在Actions条目下看到 “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为“7DW89RZKLY.com.runmonster.runmonsterfree”,那麽在Bundle ID中你只需输入“com.runmonster.runmonsterfree” 。
编辑项目的 target 信息以使用新的provisioning profile:
选取 Project > Edit Active Target
选取顶部“Build” 标籤
选取需要的 configuration (通常为 Debug)
在Code Signing Identity中选择新的provisioning profile
在Code Signing Identity之下的行中(可能名为 Any iPhone OS Device)选择新的provisioning profile
4. 添加你的应用程序
如果你的程序已经发表到App Store了,那麽可以略过此步骤。

在你将产品添加到 iTunes Connect之前,你必须添加此产品所需的程序。如果你的程序还没有100%完成也无需担心,你可以先提交具有部分数据的程序,最后再提交真实的程序。

注意: 只有 SKU 和 version(版本)部分是以后不可修改的

登录到  http://developer.apple.com/iphone
点击右边链接进入 iTunes Connect
注意:你必须先登录到developer.apple.com,否则会有不测发生(译者注:具体是什麽不测我也不太清楚,胆大的请自己试一下)
在 iTunes Connect主页点击 “Manage Your Applications”
在右上角点击“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中添加产品了。

确保登录到  http://developer.apple.com/iphone
进入 iTunes Connect 主页
点击“Manage Your Applications”
点击刚建好的程序 点击view details
点击 “Manage in-App Purchases” 链接
点击 “Create New”
填写下列产品信息:
Reference Name(参考名称): 产品的通用名称。比如,我使用的是 “Pro Upgrade”。此名称是不允许进行编辑的,它不会显示于App Store中。
Product ID(产品ID): 你产品的唯一id。通常格式是 com.company.appname.product,但它可以说任何形式。它并不要求以程序的App ID作为前缀。
Type(类型): 有三种选择
Non-consumable(非消耗品): 仅需付费一次 (例如你希望将出现从免费版升级为专业版)
Consumable(消耗品): 每次下载都需要付费
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 文件中:
复制代码
  1. // InAppPurchaseManager.h
  2. #import <StoreKit/StoreKit.h>
  3. #define kInAppPurchaseManagerProductsFetchedNotification @"kInAppPurchaseManagerProductsFetchedNotification"
  4. @interface InAppPurchaseManager : NSObject <SKProductsRequestDelegate>
  5. {
  6.     SKProduct *proUpgradeProduct;
  7.     SKProductsRequest *productsRequest;
  8. }

注意: InAppPurchaseManager 是一个单例类,它处理程序中所有IAP任务。它是本文中的示例程序。

3.产品请求,并在相应.m文件中实现代理协议:
复制代码
  1. // InAppPurchaseManager.m
  2. - (void)requestProUpgradeProductData
  3. {
  4.     NSSet *productIdentifiers = [NSSet setWithObject:@"com.runmonster.runmonsterfree.upgradetopro" ];
  5.     productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
  6.     productsRequest.delegate = self;
  7.     [productsRequest start];
  8.     
  9.     // we will release the request object in the delegate callback
  10. }
  11. #pragma mark -
  12. #pragma mark SKProductsRequestDelegate methods
  13. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
  14. {
  15.     NSArray *products = response.products;
  16.     proUpgradeProduct = [products count] == 1 ? [[products firstObject] retain] : nil;
  17.     if (proUpgradeProduct)
  18.     {
  19.         NSLog(@"Product title: %@" , proUpgradeProduct.localizedTitle);
  20.         NSLog(@"Product description: %@" , proUpgradeProduct.localizedDescription);
  21.         NSLog(@"Product price: %@" , proUpgradeProduct.price);
  22.         NSLog(@"Product id: %@" , proUpgradeProduct.productIdentifier);
  23.     }
  24.     
  25.     for (NSString *invalidProductId in response.invalidProductIdentifiers)
  26.     {
  27.         NSLog(@"Invalid product id: %@" , invalidProductId);
  28.     }
  29.     
  30.     // finally release the reqest we alloc/init’ed in requestProUpgradeProductData
  31.     [productsRequest release];
  32.     
  33.     [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
  34. }

上面代码有几点需要注意:

指定产品id时,你必须使用完整产品id。例如,上例中使用 “com.runmonster.runmonsterfree.upgradetopro”。仅使用 “upgradetopro” 将不会正常工作。
如果在productsRequest:didReceiveResponse:中response.products 为 nil,而你的产品id出现于 response.invalidProductIdentifers 数组中时,那麽请做好心理准备开始一场徒劳的搜索战吧。 StoreKit API没有提供任何帮助,也没有任何指示关于为什麽你的id是无效的。很可爱,不是吗?
SKProduct类提供了有关程序标题和描述的本地化版本,但是价格则没有本地化版本。下面是针对此疏忽提供的代码:
复制代码
  1. // SKProduct+LocalizedPrice.h
  2. #import <Foundation/Foundation.h>
  3. #import <StoreKit/StoreKit.h>
  4. @interface SKProduct (LocalizedPrice)
  5. @property (nonatomic, readonly) NSString *localizedPrice;
  6. @end

复制代码
  1. // SKProduct+LocalizedPrice.m
  2. #import "SKProduct+LocalizedPrice.h"
  3. @implementation SKProduct (LocalizedPrice)
  4. - (NSString *)localizedPrice
  5. {
  6.     NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
  7.     [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
  8.     [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
  9.     [numberFormatter setLocale:self.priceLocale];
  10.     NSString *formattedString = [numberFormatter stringFromNumber:self.price];
  11.     [numberFormatter release];
  12.     return formattedString;
  13. }
  14. @end

加入上述代码,测试一下。你应该在控制台窗口中看见产品信息了。然而更大的可能是,你得到了一个无效的产品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 协议:
复制代码
  1. // InAppPurchaseManager.h
  2. // add a couple notifications sent out when the transaction completes
  3. #define kInAppPurchaseManagerTransactionFailedNotification @"kInAppPurchaseManagerTransactionFailedNotification"
  4. #define kInAppPurchaseManagerTransactionSucceededNotification @"kInAppPurchaseManagerTransactionSucceededNotification"
  5. @interface InAppPurchaseManager : NSObject <SKProductsRequestDelegate, SKPaymentTransactionObserver>
  6. {
  7.     …
  8. }
  9. // public methods
  10. - (void)loadStore;
  11. - (BOOL)canMakePurchases;
  12. - (void)purchaseProUpgrade;
  13. @end

上面我们定义了两个新的notification,它们将作为购买事务的结果被发送。在上例中我们仍然使用与获取产品描述同一个InAppPurchaseManager类。
复制代码
  1. // InAppPurchaseManager.m
  2. #define kInAppPurchaseProUpgradeProductId @"com.runmonster.runmonsterfree.upgradetopro"
  3. #pragma -
  4. #pragma Public methods
  5. //
  6. // call this method once on startup
  7. //
  8. - (void)loadStore
  9. {
  10.     // restarts any purchases if they were interrupted last time the app was open
  11.     [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
  12.     
  13.     // get the product description (defined in early sections)
  14.     [self requestProUpgradeProductData];
  15. }
  16. //
  17. // call this before making a purchase
  18. //
  19. - (BOOL)canMakePurchases
  20. {
  21.     return [SKPaymentQueue canMakePayments];
  22. }
  23. //
  24. // kick off the upgrade transaction
  25. //
  26. - (void)purchaseProUpgrade
  27. {
  28.     SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId];
  29.     [[SKPaymentQueue defaultQueue] addPayment:payment];
  30. }
  31. #pragma -
  32. #pragma Purchase helpers
  33. //
  34. // saves a record of the transaction by storing the receipt to disk
  35. //
  36. - (void)recordTransaction:(SKPaymentTransaction *)transaction
  37. {
  38.     if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseProUpgradeProductId])
  39.     {
  40.         // save the transaction receipt to disk
  41.         [[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:@"proUpgradeTransactionReceipt" ];
  42.         [[NSUserDefaults standardUserDefaults] synchronize];
  43.     }
  44. }
  45. //
  46. // enable pro features
  47. //
  48. - (void)provideContent:(NSString *)productId
  49. {
  50.     if ([productId isEqualToString:kInAppPurchaseProUpgradeProductId])
  51.     {
  52.         // enable the pro features
  53.         [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isProUpgradePurchased" ];
  54.         [[NSUserDefaults standardUserDefaults] synchronize];
  55.     }
  56. }
  57. //
  58. // removes the transaction from the queue and posts a notification with the transaction result
  59. //
  60. - (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
  61. {
  62.     // remove the transaction from the payment queue.
  63.     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
  64.     
  65.     NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, @"transaction" , nil];
  66.     if (wasSuccessful)
  67.     {
  68.         // send out a notification that we’ve finished the transaction
  69.         [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionSucceededNotification object:self userInfo:userInfo];
  70.     }
  71.     else
  72.     {
  73.         // send out a notification for the failed transaction
  74.         [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
  75.     }
  76. }
  77. //
  78. // called when the transaction was successful
  79. //
  80. - (void)completeTransaction:(SKPaymentTransaction *)transaction
  81. {
  82.     [self recordTransaction:transaction];
  83.     [self provideContent:transaction.payment.productIdentifier];
  84.     [self finishTransaction:transaction wasSuccessful:YES];
  85. }
  86. //
  87. // called when a transaction has been restored and and successfully completed
  88. //
  89. - (void)restoreTransaction:(SKPaymentTransaction *)transaction
  90. {
  91.     [self recordTransaction:transaction.originalTransaction];
  92.     [self provideContent:transaction.originalTransaction.payment.productIdentifier];
  93.     [self finishTransaction:transaction wasSuccessful:YES];
  94. }
  95. //
  96. // called when a transaction has failed
  97. //
  98. - (void)failedTransaction:(SKPaymentTransaction *)transaction
  99. {
  100.     if (transaction.error.code != SKErrorPaymentCancelled)
  101.     {
  102.         // error!
  103.         [self finishTransaction:transaction wasSuccessful:NO];
  104.     }
  105.     else
  106.     {
  107.         // this is fine, the user just cancelled, so don’t notify
  108.         [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
  109.     }
  110. }
  111. #pragma mark -
  112. #pragma mark SKPaymentTransactionObserver methods
  113. //
  114. // called when the transaction status is updated
  115. //
  116. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
  117. {
  118.     for (SKPaymentTransaction *transaction in transactions)
  119.     {
  120.         switch (transaction.transactionState)
  121.         {
  122.             case SKPaymentTransactionStatePurchased:
  123.                 [self completeTransaction:transaction];
  124.                 break;
  125.             case SKPaymentTransactionStateFailed:
  126.                 [self failedTransaction:transaction];
  127.                 break;
  128.             case SKPaymentTransactionStateRestored:
  129.                 [self restoreTransaction:transaction];
  130.                 break;
  131.             default:
  132.                 break;
  133.         }
  134.     }
  135. }

要测试上面的新代码,你还需要编写调用 loadStore, canMakePurchases 以及 purchaseProUpgrade 方法的代码。

有关上述代码的详细解释,请参考官方 In App Purchase Programming Guide (IAP编程指南)

上述代码有几个部分是针对我的程序的。例如,在 provideContent:中,NSUserDefaults 中的@”isProUpgradePurchased” BOOL 字段被设定为 YES。程序的其他部分将检查此BOOL值以确定是否需要启动专业版功能。如果你正好也要实现免费升级专业版的功能,那麽你可以使用同样的方法。

2. 添加测试用户
为测试上述代码,你需要在 iTunes Connect 中创建测试用户以对IAP功能进行测试。你可以使用测试帐号购买产品而不被Apple收取费用。

按以下步骤创建测试用户:

登录到  http://developer.apple.com/iphone
进入 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功能比想象的要複杂许多。我可是经过无数痛苦的经历才完成我的程序。希望能够帮助其他开发者减轻他们的痛苦。应用程序內购买


------------------------------------------------------------------------------------------------------


Unity做东西是快,但是有些功能是需要额外开发的,比如 IAP (In App Purchase,应用程序内购买)

还好unity提供了灵活的扩展功能,允许嵌入原生代码来做一些unity未实现的功能。

这几天折腾IAP,碰到很多问题,现在终于调通了,记下来备忘。

首先要提到两个链接:

http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/

帖子的名字a full walkthrough,翻译过来可以叫做"全攻略"或者“完全手册"

通过在google搜索关键字 in app purchase 全攻略 可以找到好心人翻译的中文版。

原作者是大好人,自己在做iap的时候,一路艰辛,搞定之后分享他的经验,提供完整好用的代码,减少后来者的痛苦。

用作者自己的话说:It took several days of blood, sweat, and tears to get it working in my own application, and hopefully this post has helped short circuit that cycle of pain and suffering for you as well.

十分感谢原作者!

可惜的是这个文章很旧了,里面有些内容已经过时,例如:

1。调试IAP必须上传App。现在已经不需要这样了,上传app之前apple已经允许你管理 InAppProduct

2。建立好Product之后需要等几个小时才能访问。这个貌似也不需要了,我弄好之后很快就能访问了。


分割线————————————————————————————————

下面开始讲代码实现和调试过程:

原作者的代码我就不贴了,只贴一段unity调用该代码的代码。

//InAppPurchase.mm


#import <Foundation/Foundation.h>

#import <StoreKit/StoreKit.h>

#import "InAppPurchaseManager.h"


extern "C" {

bool CanPurchase()

{

return ([SKPaymentQueue canMakePayments]);

}

InAppPurchaseManager *sharedMgr = nil;

void BuyPro()

{

if (sharedMgr == nil) {

sharedMgr = [[InAppPurchaseManager alloc]init];

[sharedMgr loadStore];

}

[sharedMgr purchaseProUpgrade];

}

}


代码说明:这段代码提供了两个函数

一个 CanPurchase用来判断是否可以程序内购买,如果用户通过设置关掉了程序内购买,需要提醒用户打开。

一个是购买Pro版本,即调用InAppPurchaseManager的 purchaseProUpgrade方法

原作者实现的是一个非消费型的产品,即一次付费,终身使用。如果要做消费型的产品,即每次购买都需要付费(常用的道具付费),也很简单,在iTunesConnect里面添加产品的时候选对类型就好了,代码本身都支持。


把包含上面代码的InAppPurchase.mm文件放到unity的Assets/Plugins/ios目录下,记住目录结构,不要犯错,否则xcode会链接不过,报Symbol(s) not found

另外把原作者的4个文件 也放到此目录下。4个文件分别是:

InAppPurchaseManager.h

InAppPurchaseManager.m

SKProduct+LocalizedPrice.h

SKProduct+LocalizedPrice.m


在unity脚本中使用这两个函数的代码如下:(貌似只能c#)

using UnityEngine;

using System.Collections;

using System.Runtime.InteropServices;

public class IAP: MonoBehaviour

{

[DllImport ("__Internal")]

private static extern bool CanPurchase();

[DllImport ("__Internal")]

private static extern void BuyPro();


void OnGUI()

{

if (GUI.Button(Rect(10, 10, 120, 120), "Buy"))

{

if(CanPurchase())

{

Debug.Log("can buy.");

BuyPro();

}

else

{

Debug.Log("you need open in app purchase.");

}

}

}

}

我是把它放在一个gui的button上,当然你也可以根据需要放在任何你需要的地方。


如果要反过来调用,即在原生代码中调用脚本的函数,unity提供一个方法:

UnitySendMessage("game_object""BuyProSuccess""Ok");

这个方法调用相当于给名为 game_object的对象调了一个SendMessage("BuyProSuccess",”Ok")

当然对象名,消息名(函数名),参数内容你可以根据自己的需要随便填,参数类型貌似只能String

可以把这行代码加到 InAppPurchaseManager.m的provideContent方法中,用来通知unity对象购买成功,作出相应的处理。


代码相关的部分就这些,其实更复杂更痛苦的更容易犯错的倒是代码之外的东西。


几个注意事项:

1。别忘了在xcode中给你的工程加上StoreKit.framework。官方指南中就一句,make sure link to StoreKit.framework,这句话折腾我半天,我还以为是链接选项呢,找半天没找到,原来是在xcode的Groups & Files窗口(左边的那个列出文件的窗口)中找Frameworks组点右键,Add -> Existing Frameworks... 在弹出对话框中找到StoreKit.framework


2。unity菜单 File-> build settings -> player settings 中 Identification一节中 Bundle Identifier必须要和你在iTunes Connect中建立应用的时候填的bundle identifier一致,一般都是com.yourcompany.appname


3。full walkthrough作者代码中的宏定义kInAppPurchaseProUpgradeProductId对应的字符串需要改成你自己在iTunesConnect中添加Product时设定的id,一般是com.yourcompany.appname.productname,不能丝毫有错,否则Invalid product id


4.在Xcode中project-》edit project settings 中build页,code signing identity->any ios的值必须是你添加app时生成的provisioning profile,即此app专用的provisioning profile,否则 invalid product id,建议删除手机中的其他无关的profile


5。如果消费型产品出现 “您已经购买产品,但尚未下载。。。”,说明事务没有正确结束,如果严格按照我的做法,应该不会出现此问题。


6。碰到各种诡异的问题,实在没辙了,可以考虑从设备中删掉你的应用,重新安装,重启设备等手段。


7。IAP不能在模拟器上运行,直接真机。当然debug或release都行。记得在你的设备上退出你的正式账号,用测试iap一定要用测试账号。测试账号建立请在member center中选择manager users。


8。将下面提供的参考链接通读3遍,有助于避免一些不必要的错误,虽然都很长,但是不要着急,慢慢看。


9。万事不决问google



参考链接:

1。原版的iap walkthrough:

http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/

中文链接http://www.cocoachina.com/bbs/read.php?tid-69165-fpage-2.html


2。apple提供的iap教程

http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html


3。apple提供的itunes connect开发者指南(有中文版)

https://itunesconnect.apple.com/docs/iTunesConnect_DeveloperGuide.pdf


4。unity手册中关于插件的部分

http://unity3d.com/support/documentation/Manual/Plugins.html


5。应对xcode 输出 the program being debugged is not being run错误的帖子

http://www.dragdrop.it/devcorner/2010/05/error-from-the-debugger-the-program-being-debugged-is-on-being-run/

In App Purchases(IAP 应用程序內购买): 完全攻略   


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: IAP是In-App Purchases(应用内购买)的缩写,是一种让用户在应用内购买虚拟物品、功能或者服务的功能。通过IAP,用户可以在应用程序内直接购买并支付虚拟商品,而不需要离开应用程序购买。 Bootloader(引导程序)是计算机系统中的一个特殊程序,它在系统启动时最先被加载执行,用于初始化硬件、引导操作系统以及其他系统初始化工作。 AppApplication(应用程序)的缩写,是一种在移动设备上安装和运行的应用软件。MFC是Microsoft Foundation Classes(微软基础类库)的缩写,是一个用于开发Windows应用程序的C++类库框架。 因此,"IAP bootloader app MFC" 的意思是,在使用MFC类库框架开发的Windows应用程序中,通过IAP功能实现引导程序的更新或者应用程序的更新。 一个典型的应用场景是,如果用户在使用某个应用程序时,开发者可以使用IAP功能提供新的引导程序或者应用程序更新的提醒,并提供购买选项供用户选择。一旦用户购买了更新,应用程序会通过IAP功能将更新的引导程序或者应用程序直接下载并进行安装,而不需要用户离开应用程序或者手动进行更新的安装操作。 这种方式可以让用户方便地在应用程序内更新或购买新的功能或虚拟物品,提升用户体验,也给开发者提供了一种增加收益和提供新功能的方式。 ### 回答2: IAP(In-App Purchases)是指在移动应用程序中进行购买和付款的功能。通过IAP,用户可以使用应用内购买来解锁额外的功能、获取虚拟物品或订阅服务等。在移动设备上,IAP是一种常见的商业模式,为开发者提供了一种方式来获取收入。 Bootloader是启动引导程序的意思,它负责在计算机或移动设备启动时加载操作系统。在移动设备上,Bootloader负责验证和加载操作系统,确保设备能够正确启动。Bootloader通常由设备制造商提供,且不可修改。 AppApplication的缩写,指移动设备上的应用程序。MFC(Microsoft Foundation Classes)是一种C++应用程序框架,通常用于开发Microsoft Windows操作系统上的应用程序。 综来看,IAP bootloader app mfc可以理解为在移动设备上使用MFC框架开发的应用程序,具备IAP功能,并且由设备制造商提供的启动引导程序负责加载该应用程序。这种应用程序可以通过IAP进行付款和购买,提供额外的功能、虚拟物品或订阅服务等。这样的应用程序可以为开发者提供收入,同时由于使用了MFC框架,开发者可以更快地开发出功能丰富的应用程序。总之,IAP bootloader app mfc结了应用内购买功能、启动引导程序和MFC框架,为用户提供更好的应用体验,同时为开发者带来商业机会。 ### 回答3: IAP(In-App Purchase)是应用内购买的简写,指的是在移动应用程序中实现购买和下载额外功能或内容的机制。通过IAP,用户可以使用应用内部的货币或在线支付购买额外的功能、解锁附加内容或去除广告等。 Bootloader(引导加载程序)是指在计算机硬件上最先运行的程序,用于启动操作系统。它位于计算机的ROM芯片中,负责调用操作系统的内核,并加载操作系统至内存中,以便开始系统的正常运行。 App应用程序)是指在移动设备上使用的软件程序,可以用于各种不同的用途,例如社交媒体、游戏、生产力工具等。移动应用程序通常是通过应用商店(如App Store、Google Play)进行下载和安装的。 MFC(Microsoft Foundation Classes)是微软提供的一套C++类库,用于开发基于Windows平台的图形用户界面(Graphical User Interface, GUI)应用程序。通过MFC,开发人员可以使用更高层次的抽象来简化Windows编程的过程,使开发更加高效和便捷。 综上所述,IAP是一种用于在移动应用中进行购买和下载额外功能或内容的机制。Bootloader是计算机硬件上最先运行的程序,用于启动操作系统。App是移动设备上使用的各种不同用途的软件程序。而MFC是微软提供的一套用于开发Windows平台GUI应用程序的类库。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值