这个特性允许你支持应用内支付。当前,只有Apple iTunes Store被支持。在未来也许其他商店会加入这个特性。
Apple iTunes Store 的应用内支付
应用内支付允许用户购买额外的内容。然而, Apple iTunes Store只管理交易信息!开发者不能使用 Apple App Store来分发内容。所以,要么你捆绑内容在你的app里,等到付费之后来解锁这个内容;要么你不得不开发一个你自己的系统来下载付费后你想分发的内容。为了支持可下载内容,你应该使用我们新的 Network/AsynchHTTP API。
为了让你的app可以在Apple iTunes Store正常工作,你必须遵守在Apple的规则,关于在供应门户上发生供应和在iTunesConnect上发生购买。这个是一个复杂的过程。网上有许多有用的教程;一些文章的末尾都列有不错的建议。简短来说,关键步骤如下:
• 确保你向Apple提交了你的税收和银行信息。应用内支付在这些没有写清楚并不会工作,并且你也不会得到任何错误信息告诉你是这个问题。
• 在供应门户上,创建一个新的唯一且完全合格的AppID(例如 com.anscamobile.NewExampleInAppPurchase)(不要使用通配符)
• 为你的AppID创建一个供应配置信息。
• 在iTunes Connect上,用相同的 bundle identifier创建一个新App
• 用他们的产品标识符和分类, 添加你的可购买项。(消费品,非消费品,或订阅)
• 添加然后配置一个测试用户帐号,来实验你的应用内支付代码
这里有个有用的范例工程: InAppDemo.zip
使用Corona的store模块
你也需要在你的Corona应用中创建代码,来处理App Store的交易。
Initialization / Getting Started
首先你应该在你的程序里require一下store模块store = require("store")
然后,你需要调用 store.init 函数指定一个listener来处理交易回调(下面会详细介绍)
store.init( listener )
你应该在你的程序完成启动后,尽快调用 store.init() 。原因是App Store希望积极确保任何未完成的交易,能够尽可能完成。如果一个交易在你程序运行的最后时间被中断(比如来电话或者网络中断),用户应该可以继续完成这笔交易。通过调用 store.init(),你的app通知商店,你已经准备好处理交易的回调。
获取关于可用产品的销售信息
Corona提供 store.loadProducts() 来取得关于可用物品的销售信息。这包括每个物品的价格,一个本地化的名称,一个本地化的描述。然而,Apple App Store不会提供一种方法来让你查询所有可用的产品。因此,你的代码必须包括(或生成)所有物品的产品标识符。
store.loadProducts( arrayOfProductIdentifiers, listener )
• arrayOfProductIdentifiers -- 一个每个元素都包含一个字符串的lua数组,每个字符串都是你想知道的应用内物品的产品标识符 。
• listener -- 一个回调函数,当从商店完成产品信息的获取,这个函数才会被调用。
产品的ID就是你在iTunes Connect中输入的那些。典型的惯例是,用你的bundle id再追加上物品名,例如(com.anscamobile.NewExampleInAppPurchase.MyNonConsumableItem)
loadProductsCallback listener回调中,得到的event有下列属性:
• event.products -- 一个lua数组,其每个元素包含一个lua table:包含多个产品信息,例如标题,描述,价格和产品标识符
• event.invalidProducts -- 一个lua数组,其每个元素包含一个字符串,这个字符串就是你请求的产品标识符。这里你只会获得你请求的,但实际上无效或者不存在的条目。
event.products 数组中每个条目支持下列字段:
• title -- 条目的本地化名称。
• description -- 条目的本地化描述。
• price -- 条目的价格(一个数字)
• productIdentifier -- 产品标识符
这里有一个简单的代码例子:
function loadProductsCallback( event )
print("showing products", #event.products)
for i=1, #event.products do
local currentItem = event.products[i]
print(currentItem.title)
print(currentItem.description)
print(currentItem.price)
print(currentItem.productIdentifier)
end
print("showing invalidProducts", #event.invalidProducts)
for i=1, #event.invalidProducts do
print(event.invalidProducts[i])
end
end
arrayOfProductIdentifiers =
{
"com.anscamobile.NewExampleInAppPurchase.MyConsumableItem",
"com.anscamobile.NewExampleInAppPurchase.MyNonConsumableItem",
"com.anscamobile.NewExampleInAppPurchase.MySubscriptionItem",
}
store.loadProducts(arrayOfProductIdentifiers,loadProductsCallback)
如果需要你的产品列表是动态的,Apple建议你在你自己的服务器上提供一个你的app可以获取的最新的产品列表。
通常你会借助回调这个机会来创建和显示一个UI,使用户可以浏览和购买物品。
可以购买了吗?
iOS设备有一个禁止购买的设置。这是为了阻止小孩在没有得到父母允许的情况下意外购买而设的。Corona提供一个API来检查是否可能进行购买。提前使用这个检查一下,可以避免你的用户浏览了你很多购买步骤,结果最后一步发现购买被禁用了。
store.canMakePurchases
如果购买被允许返回true,否则false。
购买产品
为了开始一个购买,Corona提供 store.purchase()。
store.purchase( arrayOfProducts )
• arrayOfProducts -- 一个lua数组指向你想购买的产品。每个元素包含一个字符串(产品标识符)或一个lua table(和通过 loadProductsCallback listener传回的 event.products数组的元素字段一样)
这个函数会发出购买请求到应用商店。你在 store.init()中指定的listener将会在商店完成交易的处理后,被调用。
注意:目前,没有明确的API可以确定可消费物品的数量。然而,作为一个后门,你可以多次把产品放在数组中,然后Corona在幕后设置数量。
(Transaction=交易)
Transaction Listener Callback Events
调用 store.init() 来让你的程序里的listener处理来自AppStore的交易回调。 这个listener应该处理下面所有的情况:
• 一个物品被购买 (通过store.purchase())
• 一个购买交易被用户取消 (在store.purchase() 被调用之后)
• 因为各种原因一个购买交易失败了 (通过 store.purchase())
• 一个物品在之前app被打断前被购买了(也许因为接听一个来电),而app再次运行起来时App Store试着恢复/完成这个交易。
• 一个 A restore already purchased items request was initiated (通过 store.restore(), 下面会解释)
从交易回调监听器( transaction callback listener)得到的event有下列属性:
event.transaction : 一个包含交易信息的对象。
交易对象支持下面的只读属性:
• state -- 一个包含交易状态的字符串。有效值是 "purchased", "restored", "cancelled", 和 "failed"。
• productIdentifier -- 和交易关联的产品标识符。
• receipt -- 从Store返回的唯一收据。这将会返回一个十六进制字符串。
• identifier -- 从Store返回的一个唯一的交易标识符,是一个字符串。
• date -- 交易发生时的日期。
• originalReceipt -- 基于最初购买尝试,从商店返回的一个唯一收据。 这大多数和修复有关。这也是一个十六进制字符串。
• originalIdentifier -- 基于最初购买尝试,从商店返回的一个唯一交易标识符。 这大多数和修复有关。这也是一个字符串。
• originalDate -- 原始交易发生的日期。 这大多数和修复有关。
• errorType -- 当state是“fail”时,发生的错误类型。(一个字符串)
• errorString -- 当在“fail”情况下,更详细的错误描述。
你收到一个交易事件后,由你来决定要做什么。例如,如果用户成功购买了一个物品,你可以记录这个信息在一个偏好文件里,以解锁用户使用这个物品的能力。这个文件将来应该被引用,好在将来要知道这个物品你已经购买过了。简单来说,如果必须下载内容,你可以在这里开始下载。
在你处理完交易后,你必须对你的交易对象调用store.finishTransaction() 。如果你不这么做,AppStore将认为你的交易被打断了,并且试着在下次应用启动时恢复之。
store.finishTransaction( transaction )
恢复购买的物品
擦掉设备上信息、或者购买了新的设备的用户,可能希望恢复他们之前买过的物品,而不用再次为之付费。 store.restore() API开始这个处理。被恢复的交易将调用你之前用 store.init()注册的 transactionCallback listener。这时交易state将是“restored”,你的app将会再次使用交易对象的 originalReceipt, originalIdentifier, 和 originalDate字段。
Transaction Callback Example
function transactionCallback( event )
local transaction = event.transaction
if transaction.state == "purchased" then
print("Transaction succuessful!")
elseif transaction.state == "restored" then
print("Transaction restored (from previous session)")
print("productIdentifier", transaction.productIdentifier)
print("receipt", transaction.receipt)
print("transactionIdentifier", transaction.identifier)
print("date", transaction.date)
print("originalReceipt", transaction.originalReceipt)
print("originalTransactionIdentifier", transaction.originalIdentifier)
print("originalDate", transaction.originalDate)
elseif transaction.state == "cancelled" then
print("User cancelled transaction")
elseif transaction.state == "failed" then
print("Transaction failed, type:", transaction.errorType, transaction.errorString)
else
print("unknown event")
end
-- Once we are done with a transaction, call this to tell the store
-- we are done with the transaction.
-- If you are providing downloadable content, wait to call this until
-- after the download completes.
store.finishTransaction( transaction )
end
store.init( transactionCallback )
-- Might try buying something here:
-- store.puchase"com.anscamobile.NewExampleInAppPurchase.MyNonConsumableItem" }
-- Or might try restoring here:
-- store.restore()
Additional Notes & Documentation
在App中,购买并不容易设置。你可能会发现你比实际写代码,花费了更多的时间在iTunes Connect中设置东西,以及创建 Provisioning Profiles。这里有一些提示和连接,会在这方面帮助到你。
创建一个新的App ID和 provisioning profile,并使用它。使用完整合格的bundle标识符,比如 com.yourdomain.yourapp。不要使用通配符例如com.*。
一些人声称设置你的应用内支付和在Apple网络中传播,需要24个小时。这意味着你应该提早在iTunes Connect里把应用程序的各种东西设置好,以便到时候可以开始测试代码,而不必等待。不幸的是,你不知道如果有错误,到底是网络传播延迟还是有些地方,这就是为什么你应该提早把这些设置好的原因。
记住当测试的事后,创建一个测试用户帐号,然后在你的iOS设备设置->store中登出你自己的帐号。这里先别登入你的测试帐号;等一下知道你的app运行,并且你被提示要这样做时,你再登入你的测试帐号。当你用Corona构建你的产品时,确保你使用的是正确的 provisioning profile(你用完整合格的 bundle identifier 为这个应用创建的那个)。
Apple Links
• In App Purchase Programming Guide
• Technical Note TN2259: Adding In App Purchase to your iOS Applications
• iTunes Connect Frequently Asked Questions > Manage Your In App Purchases
Other Links
• In App Purchases: A Full Walkthrough
• Things I learned implementing my first InAppPurchase