0, 概述
应用程序内部付费机制(Google Play In-app Billing, 以下简称应用内支付)是Google Play的一项服务,这种服务为应用内购买提供支付流程。
要使用这项服务,你的应用会对一个特定的应用内产品发送一个结账请求。然后该服务会处理这笔交易的所有细节,包括请求和确认支付形式和处理金融方面的交易。支付流程完成后,该服务会发送购买细节到你的App,比如订单号、订单时间、价格。你的App无需理会金融方面的交易,这都由Google Play的应用内支付服务来提供。
1, 产品类型与购买方式
应用内支付支持不同种类的产品类型和购买类型,为你的App提供灵活的赚钱方式。在所有情况下,你都要使用Google Play开发者控制台定义你的产品,包括产品类型、购买类型、库存单位、价格、描述等等。想了解更多信息,请看 管理应用内支付
产品类型
使用本服务你能卖两种产品:应用内产品 和 订阅
两种产品的账单特性完全不同,但是我们的API能让你使用同样的通信模型、数据结构、用户交互来处理他们,后面会讲到。
购买方式
我们的应用内支付服务提供两种购买方式:“限定账号”和“不限账号”
购买方式决定了Google Play如何来处理和跟踪购买:
2, 应用内支付架构
你的App使用设备中Google Play App提供的API 来访问应用内支付服务。Google Play App 使用异步消息循环来传达账单请求,并且在你的App和Google Play服务器间执行响应。在实践中,你的App绝不会直接与Google Play服务器交互(见图1)。相反,你的App使用IPC发送结账请求到Google Play App,然后取回购买响应,方式是异步广播。你的App自己不会去管与Google Play服务器的网络连接或者使用其他特殊的API。
图1:你的App通过Google Play App发送和取回结账消息,后者负责与Google Play服务器通信。
Your App 《——》 Google Play App 《——》 Market Server
有些应用内支付实现会使用私有的服务器来交付内容或确认交易,但是远程服务器不必实现应用内支付。如果你出售需要下载到用户设备的数字内容到媒体文件,这种情况下私有服务器会有用。你也可能使用远程服务器来存储用户交易历史或执行各种确保支付安全的任务,比如签名验证。虽然你能够在App里面处理所有安全相关任务,但还是建议你放在远程服务器里,因为这样有助于你的App减少被攻击的风险。
典型的应用内支付实现包含3个组件:
• 一个Service (在示例中被命名为BillingService),它处理从你的App发送账单请求到Google Play 应用内支付服务的购物消息。
• 一个BroadcastReceiver (在示例中被命名为 BillingReceiver),他接收来自Google Play App的所有的账单异步响应。
• 一个安全组件 (在示例中被命名为Security),它执行安全相关的任务, 比如签名验证和随机数生成。想了解应用内支付安全的更多信息, 请看本文中后续的Security controls。
你可能同样希望使用另外两个支持支付的组件:
• 一个响应Handler (在示例中被命名为 ResponseHandler),他提供App指定的购物通知、报错和其他状态消息的处理。
• 一个观察者 (在示例中被命名为PurchaseObserver),它负责发送回调到你的App,以便你能使用购买信息和状态更新你的GUI。
除了这些组件,你的App必须实现保存用户信息的方法,还有用户的购买、选择货物的接口。你不必提供结账的接口,当用户初始化一个应用内购买,Google Play App会展示出结账接口的。当用户完成结账流程,你的App会继续运行。
3, 应用内支付消息宏定义
basic request-response messaging that takes place between your application and the Google Play application. 当用户开始购买,你的App使用IPC函数调用发送购物消息到Google Play的应用内支付服务(MarketBillingService)。Google Play App同步响应所有支付请求,为你的App提供状态通知等等信息。Google Play App 也异步响应一些账单请求,为你的App提供出错消息和交易细节。下面的章节描述了你的App和Google Play App之间基本的请求/响应消息。
请求宏定义
你的App发出应用内支付请求,需要调用IPC函数 (sendBillingRequest()),这个函数由MarketBillingService接口提供。该接口定义于 Android Interface Definition Language 文件(IMarketBillingService.aidl). 你能够下载该AIDL文件和应用内支付示例程序。
sendBillingRequest()函数只有一个Bundle参数。你发送的Bundle必须包含一系列键值对来制定各种请求参数,比如账单请求的类型、被购买的物品和它的类型, 还有发送该请求的App。想了解更多的关于请求中的Bundle键的信息,请见应用内支付Service接口。
Bundle中最重要的键之一是 BILLING_REQUEST 键,它让你指定账单请求的类型。Google Play应用内支付服务支持如下5种账单请求:
• CHECK_BILLING_SUPPORTED
这个请求用来验证Google Play App是否支持应用内支付。你常常得在App首次运行时候发送这个请求。这个请求非常有用,因为你可以根据是否支持应用内支付来安排你下一步的UI。
• REQUEST_PURCHASE
发送购买请求到Google Play,它也是应用内支付的基础。 You send this request when a user indicates that he or she wants to purchase an item in your application.当用户表示他想在你的App里面购买一些东西的时候,你就发送这个请求。 Google Play通过显示结账GUI来响应这个请求。
• GET_PURCHASE_INFORMATION
取回购买状态改变的信息。用户成功或失败购物都会使购买状态改变。退款也会触发状态改变。一旦购买状态改变,Google Play会主动通知你,所以你有你想自己取回信息的时候才发送该请求。
• CONFIRM_NOTIFICATIONS
确认你的App收到了购买状态改变的通知。Google Play会一直发送状态改变通知到你的App,直到你发送这个确认。
• RESTORE_TRANSACTIONS
取回用户的交易状态,只针对限定账号的购买 和 订阅。仅仅在你想取回用户交易状态的时候才发送该请求,这种情况往往发生于你的App被重新安装或者首次安装时。
响应宏定义
Google Play App可以响应同步的或异步的应用内支付请求,同步响应的 Bundle 包含如下的3个键:
• RESPONSE_CODE 提供请求的状态、出错信息。
• PURCHASE_INTENT 提供 PendingIntent, 你使用它来生成一个结账界面。
• REQUEST_ID 提供请求的身份识别,你用它来匹配请求与异步响应。
这些键不是跟每个请求都相关的。 想了解更多,请看下文的消息传递流程。
异步响应消息被以个别广播的形式来发送,包括下面3个宏:
• com.android.vending.billing.RESPONSE_CODE
该响应包括一个Google Play服务器响应码,它在你做出应用内支付请求后发送。服务器响应码能显示你的账单请求已成功发送到Google Play ,或者是请求出错。该响应不会用来报告购买状态的变更 (比如退款或购买信息)。想了解更多的关于该响应的码字信息,请看 应用内支付的服务器响应码.
• com.android.vending.billing.IN_APP_NOTIFY
该响应表示购买状态变更,也就是说购买成功、取消、退款。该响应包含一个或多个通知ID。每个通知ID跟一个指定的服务器端消息绑定, 每个消息又包含了一个或多个交易。在你的App收到IN_APP_NOTIFY广播后,你发送一个 GET_PURCHASE_INFORMATION 请求,连同通知ID,去检索消息细节。
• com.android.vending.billing.PURCHASE_STATE_CHANGED
该响应包含一个或多个交易的细节信息,交易信息在一个JSON串中。该JSON串是已签名的,而且签名连同那个JSON串(未加密)发送给你的App。为帮助确保你的应用内支付消息的安全,你的App可以校验JSON串的签名。
该JSON串由叫做PURCHASE_STATE_CHANGED 的intent返回,它为你的App提供一个或多个账单交易的细节。一个针对订阅的JSON串示例如下:
{ "nonce" : 1836535032137741465,
"orders" :
[{ "notificationId" : "android.test.purchased",
"orderId" : "transactionId.android.test.purchased",
"packageName" : "com.example.dungeons",
"productId" : "android.test.purchased",
"developerPayload" : "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
"purchaseTime" : 1290114783411,
"purchaseState" : 0,
"purchaseToken" : "rojeslcdyyiapnqcynkjyyjh" }]
}
想了解更多关于JSON串的域的信息, 请看应用内支付广播。
消息传递流程
典型的消息传递流程在图2中显示。每次sendBillingRequest()传递的请求类型都用粗体标示,广播intents用斜体标示。为了讲得明白,图2没有显示为每次请求发送的RESPONSE_CODE 广播intents。
图2.购物请求的消息传递流程
基本的流程是如下的9步:
1. 你的App发送一个购买请求(REQUEST_PURCHASE),指定一个产品ID和其他参数。
2. PURCHASE_INTENT 键提供一个 PendingIntent, 你的App利用它来为给定的产品ID生成一个结账的UI。 Google Play App给你的App发送一个Bundle,其中包含3个键:RESPONSE_CODE, PURCHASE_INTENT, REQUEST_ID
3. 你的App发起一个挂起intent,同时弹出一个埋单的UI。(注意:你必须从一个Activity context发起挂起intent,而不能是整个程序的context。)
4. 当结账流程结束(用户成功购买了货物或者取消了购买),Google Play 会给你的App发送一个通知消息(IN_APP_NOTIFY 广播)。这个通知消息包括了指向该交易的通知ID。
5. 你的App通过发送一个GET_PURCHASE_STATE_CHANGED来请求交易信息,该请求指定了交易的通知ID。
6. Google Play App发送一个 Bundle,携带两个键:RESPONSE_CODE、REQUEST_ID。
7. Google Play 给你的App发送交易信息,该信息保存在PURCHASE_STATE_CHANGED 广播 intent中。
8. 你的App通过发送一个确认消息(CONFIRM_NOTIFICATIONS)来确认你接收到了给定通知ID的交易信息,该消息指定了你接收到交易信息对应的通知ID
9. Google Play App给你的App发送一个Bundle,里面包含RESPONSE_CODE键和 REQUEST_ID键。
记住,当你从Google Play 接收到交易信息(图2第8步),你必须发送一个确认。否则,Google Play会继续发送IN_APP_NOTIFY消息因为你没有确认。作为最佳实践,对一个货物,直到你交付客户之前你都不应发送CONFIRM_NOTIFICATIONS请求。这样可以保证:一旦你的应用崩溃或者其他原因无法发货,你的App仍然会接收来自Google Play的IN_APP_NOTIFY广播,表示你需要发货。同样的,作为最佳实践,你的App必须能够处理包含多重订单的IN_APP_NOTIFY 消息。
图3显示的是,修复交易请求的消息流程。对每个 sendBillingRequest()方法的请求类型都用粗体标示, 广播用斜体. 为了简明,图3没有显示发给每个请求的RESPONSE_CODE 广播。
图3. 修复交易请求的消息流程
该请求触发了三个响应:
第一个响应是一个Bundle ,它携带一个RESPONSE_CODE键和一个REQUEST_ID 键。
第二个响应是Google Play App发送一个 RESPONSE_CODE 广播,它提供请求的状态信息和错误信息。RESPONSE_CODE消息总是指向一个特定的请求ID,所以你能决定RESPONSE_CODE消息适合哪个请求。
第三个响应是RESTORE_TRANSACTIONS请求,也会触发一个PURCHASE_STATE_CHANGED广播,此广播包含在一个购买请求内发送的同类交易信息。然而,不像购买请求一样,该交易不会同通知ID一起交给你。
所以你不必使用CONFIRM_NOTIFICATIONS消息响应这个intent。
注意:只有当你的App首次安装或者卸载后再次安装,你才应当使用RESTORE_TRANSACTIONS 请求类型。
图4显示了检查系统是否支持应用内支付的消息流程。sendBillingRequest()函数的请求类型用粗体显示。
图4. 检查系统是否支持应用内支付的消息流程
对CHECK_BILLING_SUPPORTED请求的同步响应提供了一个携带服务器响应码的Bundle。
1) RESULT_OK 响应码表明应用内支付被支持;
2) RESULT_BILLING_UNAVAILABLE 响应码表明 应用内支付不被支持,因为你指定的API 版本不可识别,或者用户无法合法地进行应用内购买(比如说,用户位于一个无法使用应用内支付的国家).
3) SERVER_ERROR也可能被返回,表明Google Play服务器有问题。
处理 IN_APP_NOTIFY 消息
通常,你的App接到一个来自Google Play的 IN_APP_NOTIFY广播,作为 REQUEST_PURCHASE消息的响应 (请看图2). IN_APP_NOTIFY广播通知你的App请求购买的状态改变了。要检索购买细节的话,你的App要发送 GET_PURCHASE_INFORMATION 请求。Google Play用PURCHASE_STATE_CHANGED广播来响应, 该广播包含了购买状态变更的信息。然后你的App发送给一个 CONFIRM_NOTIFICATIONS消息,通知 Google Play你收到了购买状态变更的信息。
Your App ——》 REQUEST_PURCHASE(You) ——》 IN_APP_NOTIFY(Play) ——》 GET_PURCHASE_INFORMATION(You)——》PURCHASE_STATE_CHANGED(Play) ——》 CONFIRM_NOTIFICATIONS(You)——》RESPONSE_CODE(Play)
在一些特殊情况下,你会收到多条IN_APP_NOTIFY消息,即使你确认接收到了购买信息;或者你会收到IN_APP_NOTIFY信息说购买状态改变了,但你从来没有发起过购买。这两种特殊情况你的App都必须能够处理。
处理多次 IN_APP_NOTIFY 消息
当Google Play接到对应于PURCHASE_STATE_CHANGED 的CONFIRM_NOTIFICATIONS消息,它通常会停止发送针对该 PURCHASE_STATE_CHANGED 消息的IN_APP_NOTIFY intents。 然而,有时候 Google Play会发送重复的IN_APP_NOTIFY intents 虽然你的App已经发送 CONFIRM_NOTIFICATIONS。当你发送CONFIRM_NOTIFICATIONS时设备丢失网络连接,这就可能发生。 此时Google Play可能不会接到 CONFIRM_NOTIFICATIONS 所以它会发送多个IN_APP_NOTIFY 直到它受到你的确认。所以你的App必须能够识别后来的IN_APP_NOTIFY 消息是对应以往处理的哪个交易。你能够通过检查JSON串中的orderID来做到这一点,因为每个交易有唯一的一个orderId。
处理退款和其他未请求便发送的 IN_APP_NOTIFY 消息
两种情况下你的App会收到IN_APP_NOTIFY广播,即使你的App没有发送REQUEST_PURCHASE。图5 显示了这两种情况的消息传递流程。每个 sendBillingRequest()函数的请求类型用粗体显示,广播用斜体显示。为了清晰,图5 没有画出针对每次请求的RESPONSE_CODE广播。
图5. 处理退款和其他未请求便发送IN_APP_NOTIFY消息的流程
第一种情况,你的App可能收到IN_APP_NOTIFY, 当用户把你的App安装到多台设备中,然后用户从其中一台发起应用内购买。
此时Google Play发送一个IN_APP_NOTIFY 消息到第二台设备, 通知App购买状态发生了改变。你的App要能处理这条信息,就像它处理来自应用初始化的REQUEST_PURCHASE消息响应一样, 以便你的App最终接收到 PURCHASE_STATE_CHANGED 广播intent消息,该消息包括了被购买商品的信息。 这只适用于 购买信息 被设置为“限定账号”的商品。
第二种情况,你的App会收到IN_APP_NOTIFY广播,当Google Play接到一个来自Google Wallet的退款通知。
此时Google Play 发送一个IN_APP_NOTIFY到你的App。你的App就像它处理来自应用初始化的REQUEST_PURCHASE消息响应一样处理这个消息,最终使得你的App能收到PURCHASE_STATE_CHANGED消息,包含被退款的商品信息。
退款信息在JSON串中,该串与PURCHASE_STATE_CHANGED广播是一起的。同样的 JSON串中的purchaseState 域被置为2
重要提醒:你不能使用Google Wallet API来发出退款或者取消应用内支付交易。你必须通过你的Google Wallet商业账号手工操作。但你可以使用Google Wallet API取回订单信息。
4, 安全控制
为帮助确保发送给你的交易信息的完整性,Google Play 对JSON字符串进行了签名,它位于PURCHASE_STATE_CHANGED广播intent中。Google Play 使用私钥来关联你的发布账号来创建这个签名。发布者站点生成一个RSA key来匹配每个发布账号。在你的账号概览页面,你可以找到这个密钥对的公钥部分。他跟Google Play许可证使用的公钥一样。
当Google Play对一个账单响应做签名,它包括未加密的JSON串和一个签名。当你的App接到这个签名过的响应后,你可使用你的RSA key的公钥部分来校验该签名。通过执行签名验证你能够检测到被篡改的或被欺骗的响应。你能在App里执行这个签名校验步骤。然而,如果你的App连接到一个安全的远程服务器,我们建议你在服务器上完成校验步骤。
应用内支付也使用nonce(一次性随机数)来帮助验证Google Play返回的购买信息的完整性。你的App必须生成一个随机数然后用GET_PURCHASE_INFORMATION和 RESTORE_TRANSACTIONS请求跟它一起发送。当Google Play 接收到请求,它把随机数加入包含交易信息的JSON串,然后对这个JSON串签名并返回给你的App。当你的App收到此JSON串后你必须校验它的随机数和签名。
如果想了解更多最佳安全设计的实践,请看 安全与设计
5, 应用内支付的要求和限制
在你开始应用内支付之前,确保你知悉如下要求和限制:
• 应用内支付只能在Google Play发布的App中使用。
• 想使用Google Play应用内支付,你必须拥有一个Google钱包商业版账号。
• 应用内支付需要2.3.4或更高版本的Android Market App. 想用“订阅”的话,需要3.5或更高版本的Google Play App。 在Android 3.0平板上, 需要安装5.0.12或更高版本的MyApps。
• 运行Android 1.6 (API level 4)或更高版本的设备才能使用应用内支付。
• 使用应用内支付可以卖数字内容,应用内支付不可出售实物、个人服务或者其他任何需要实物交付的东西。
• Google Play 不提供任何形式的内容交付,这由你自己负责。
• 在一个不联网的设备里无法使用应用内支付。为完成购买请求,用户必须能够连接上Google Play 服务器。
要了解更多应用内支付的要求,请看 应用内支付可用性与政策
6, 示例代码
官方示例与下载 :
In-app Billing Version 2(Dungeons)
In-app Billing Version 3(TrivialDrive)
或 打包下载
示例代码教程:
in-app-billing v2
in-app-billing v3
参考推荐:
In-app Billing Overview(EOE wiki 百科)
In-app Billing Overview(android developer)