官方连接
Google Play 上的应用内购买结算提供了一个直接、简单的界面,让您可以使用 Google Play 发送应用内购买结算请求和管理应用内购买结算交易。 下面的信息涵盖了如何使用 API 版本 3 从您的应用调用应用内购买结算服务的基本知识。
注:要查看完整实现并了解如何测试您的应用,请参阅出售应用内商品培训课程。 培训课程提供了一个完整的示例应用内购买结算应用,包括多种工具类,便于处理关键任务(例如设置您的连接、发送购买结算请求和处理来自 Google Play 的响应以及管理后台线程),这样您就可以从主 Activity 调用应用内购买结算。
开始前,请务必阅读应用内购买结算概览,以便熟悉一些概念,使您能够轻松实现应用内购买结算。
要在您的应用中实现应用内购买结算,您需要执行以下操作:
AndroidManifest.xml
文件。ServiceConnection
并将其绑定到 IInAppBillingService
。IInAppBillingService
。IInAppBillingService.aidl
是一种定义应用内购买结算版本 3 服务接口的 Android 接口定义语言 (AIDL) 文件。 您可以使用此接口通过调用 IPC 方法调用来发送结算请求。
要获取 AIDL 文件,请执行以下操作:
Extras
部分。IInAppBillingService.aidl
文件将安装到
。
要将 AIDL 添加到您的项目,请执行以下操作:
IInAppBillingService.aidl
文件到您的项目。
src/main
。aidl
,再选择 OK。com.android.vending.billing
,再选择 OK。/extras/google/play_billing/
,复制 IInAppBillingService.aidl
文件,然后将其粘贴到项目中的 com.android.vending.billing
软件包。/src/com/android/vending/billing
,并将 IInAppBillingService.aidl
文件复制到此目录。 将 AIDL 文件添加到您的项目中并使用 Gradle 工具构建项目,从而生成 IInAppBillingService.java
文件。/gen
目录中看到名为 IInAppBillingService.java
的生成文件。应用内购买结算依赖于 Google Play 应用,后者将处理应用与 Google Play 服务器之间的所有通信。 要使用 Google Play 应用,您的应用必须请求适当的权限。 您可以通过将 com.android.vending.BILLING
权限添加到 AndroidManifest.xml 文件执行此操作。 如果您的应用未声明应用内购买结算权限,但试图发送结算请求,Google Play 将拒绝请求并使用错误响应。
要为您的应用授予必要的权限,请在 AndroidManifest.xml
文件中添加以下代码行:
您的应用必须拥有 ServiceConnection
才能实现应用与 Google Play 之间的通信。 您的应用至少需要执行以下操作:
IInAppBillingService
。要在 Google Play 上与应用内购买结算服务建立连接,请实现 ServiceConnection
,以便将您的 Activity 绑定到 IInAppBillingService
。 建立连接后,重写 onServiceDisconnected
和 onServiceConnected
方法以获取 IInAppBillingService
实例的引用。
IInAppBillingService mService; ServiceConnection mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IInAppBillingService.Stub.asInterface(service); } };
在您 Activity 的 onCreate
方法中,通过调用 bindService
方法执行绑定。 向方法传递引用应用内购买结算服务的 Intent
和您创建的一个ServiceConnection
实例,并明确地将 Intent 的目标软件包名称设置为 com.android.vending
— Google Play 应用的软件包名称。
注意:要保护结算交易的安全性,请始终确保使用下面示例中所示的 setPackage()
明确地将 Intent 的目标软件包名称设置为 com.android.vending
。 明确地设置软件包名称能够确保只有 Google Play 应用可以处理来自您的应用的结算请求,从而防止其他应用拦截这些请求。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); serviceIntent.setPackage("com.android.vending"); bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); }
现在,您可以使用 mService 引用来与 Google Play 服务通信。
重要说明:完成您的 Activity
后,请务必与应用内购买结算服务解除绑定。 如果不解除绑定,开启的服务连接会导致您的设备性能下降。 此示例说明了如何通过重写 Activity 的 onDestroy
方法对到应用内购买结算的服务连接 mServiceConn
执行解除绑定操作。
@Override public void onDestroy() { super.onDestroy(); if (mService != null) { unbindService(mServiceConn); } }
如需了解绑定到 IInAppBillingService
的服务连接的完整实现,请参阅出售应用内商品培训课程和相关示例。
将应用连接到 Google Play 后,您可以对应用内商品发送购买请求。 Google Play 为用户进入他们的付款方式提供了一个结账界面,这样您的应用就无需直接处理付款交易。 在商品被用户购买后,Google Play 会识别用户拥有此商品,并在此商品被消耗前阻止用户购买具有相同商品 ID 的另一商品。 您可以控制如何在应用中消耗商品,并通知 Google Play 该商品可供再次购买。 您也可以查询 Google Play,以便快速地检索用户的购买列表。 这样十分有用,例如,非常适合您希望在用户启动应用时恢复用户购买的情况。
在您的应用中,可以使用 In-app Billing Version 3 API 从 Google Play 查询商品详情。 要将请求传递至应用内购买结算服务,首先需要创建一个包含商品 ID 字符串 ArrayList
的 Bundle
,该字符串带有键“ITEM_ID_LIST”,每个字符串是可购买商品的商品 ID。
ArrayListskuList = new ArrayList (); skuList.add("premiumUpgrade"); skuList.add("gas"); Bundle querySkus = new Bundle(); querySkus.putStringArrayList(“ITEM_ID_LIST”, skuList);
要从 Google Play 检索此信息,请在 In-app Billing Version 3 API 上调用 getSkuDetails
方法,然后将 In-app Billing API 版本(“3”)、发起调用的应用的软件包名称、商品类型(“应用内”)以及您创建的 Bundle
传递给方法。
Bundle skuDetails = mService.getSkuDetails(3, getPackageName(), "inapp", querySkus);
如果请求成功,返回的 Bundle
将包含响应代码 BILLING_RESPONSE_RESULT_OK
(0)。
警告:请不要在主线程上调用 getSkuDetails
方法。 调用此方法会触发网络请求,进而阻塞主线程。 请创建单独的线程并从该线程内部调用getSkuDetails
方法。
要从 Google Play 查看所有可能的响应代码,请参阅应用内购买结算参考。
查询结果将保存在带有键 DETAILS_LIST
的字符串 ArrayList 中。购买信息存储在 JSON 格式的字符串中。 要查看返回的商品类型详细信息,请参阅应用内购买结算参考。
在此示例中,您将从之前代码段返回的 skuDetails Bundle
中检索您的应用内商品的价格。
int response = skuDetails.getInt("RESPONSE_CODE"); if (response == 0) { ArrayListresponseList = skuDetails.getStringArrayList("DETAILS_LIST"); for (String thisResponse : responseList) { JSONObject object = new JSONObject(thisResponse); String sku = object.getString("productId"); String price = object.getString("price"); if (sku.equals("premiumUpgrade")) mPremiumUpgradePrice = price; else if (sku.equals("gas")) mGasPrice = price; } }
要从您的应用发起购买请求,请在应用内购买结算服务上调用 getBuyIntent
方法。 将 In-app Billing API 版本(“3”)、发起调用的应用的软件包名称、要购买商品的商品 ID、商品类型(“应用内”或“订阅”)以及 developerPayload
字符串传递给方法。 developerPayload
字符串用于指定您想要 Google Play 随购买信息一同发送的任何其他参数。
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
如果请求成功,返回的 Bundle
将包含响应代码 BILLING_RESPONSE_RESULT_OK
(0) 和您可以用于开始购买流程的 PendingIntent
。 要从 Google Play 查看所有可能的响应代码,请参阅应用内购买结算参考。 接下来,请使用键 BUY_INTENT
从响应 Bundle
中提取 PendingIntent
。
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
要完成购买交易,请调用 startIntentSenderForResult
方法并使用您创建的 PendingIntent
。 在此示例中,您将任意值 1001 用于请求代码。
startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
Google Play 会将对您 PendingIntent
的响应发送至应用的 onActivityResult
方法。 onActivityResult
方法将获得结果代码 Activity.RESULT_OK
(1) 或Activity.RESULT_CANCELED
(0)。要查看响应 Intent
中返回的订单类型信息,请参阅应用内购买结算参考。
订单的购买数据是 JSON 格式的字符串,将映射到响应 Intent
中的 INAPP_PURCHASE_DATA
键,例如:
'{ "orderId":"GPA.1234-5678-9012-34567", "packageName":"com.example.app", "productId":"exampleSku", "purchaseTime":1345678900000, "purchaseState":0, "developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ", "purchaseToken":"opaque-token-up-to-1000-characters" }'
注:Google Play 会为购买生成令牌。此令牌是不透明的字符序列,最长可为 1,000 字符。 将整个令牌传递至其他方法(例如在您消耗购买时,如消耗购买中所述)。 不要省略或者截断此令牌,您必须保存并返回整个令牌。
继续前面的示例,您将从响应 Intent
获得响应代码、购买数据和签名。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 1001) { int responseCode = data.getIntExtra("RESPONSE_CODE", 0); String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); if (resultCode == RESULT_OK) { try { JSONObject jo = new JSONObject(purchaseData); String sku = jo.getString("productId"); alert("You have bought the " + sku + ". Excellent choice, adventurer!"); } catch (JSONException e) { alert("Failed to parse purchase data."); e.printStackTrace(); } } } }
安全性建议:在您发送购买请求时,请创建一个可以对此购买请求进行唯一标识的字符串令牌并在 developerPayload
中包含此令牌。您可以将随机生成的字符串作为令牌。 从 Google Play 接收到购买响应时,请确保检查返回的数据签名、orderId
和 developerPayload
字符串。 为了增强安全性,您应在自己安全的服务器上执行检查。 请确保验证 orderId
为您之前未处理的唯一值,且 developerPayload
字符串与您之前通过购买请求发送的令牌相匹配。
要从您的应用检索用户所发起购买的相关信息,请在应用内购买结算版本 3 服务上调用 getPurchases
方法。 将 In-app Billing API 版本(“3”)、发起调用的应用的软件包名称以及商品类型(“应用内”或“订阅”)传递给方法。
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
Google Play 服务仅会返回由当前登录设备的用户帐户发起的购买。 如果请求成功,返回的 Bundle
将包含响应代码 0。响应 Bundle
也会包含商品 ID 列表、每个购买的订单详情列表以及每个购买的签名。
为了提升性能,第一次调用 getPurchase
时,应用内购买结算服务仅会返回由用户拥有的最多 700 个商品。 如果用户拥有大量商品,Google Play 会在响应 Bundle
中包含映射到键 INAPP_CONTINUATION_TOKEN
的字符串令牌,以表明可以检索更多的商品。 然后,您的应用可以进行后续 getPurchases
调用,并将此令牌作为参数传递。 Google Play 会继续在响应 Bundle
中返回继续令牌,直到用户拥有的所有商品都发送到您的应用。
如需了解有关由 getPurchases
返回的数据的详细信息,请参阅应用内购买结算参考。 下面的示例说明了如何从响应中检索此数据。
int response = ownedItems.getInt("RESPONSE_CODE"); if (response == 0) { ArrayListownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST"); ArrayList purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); ArrayList signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN"); for (int i = 0; i < purchaseDataList.size(); ++i) { String purchaseData = purchaseDataList.get(i); String signature = signatureList.get(i); String sku = ownedSkus.get(i); // do something with this purchase information // e.g. display the updated list of products owned by user } // if continuationToken != null, call getPurchases again // and pass in the token to retrieve more items }
您可以使用 In-app Billing Version 3 API 跟踪在 Google Play 中购买的应用内商品的所有权。 应用内商品一经购买,就会被视为“被拥有”且无法从 Google Play 购买。 您必须对应用内商品发送消耗请求,然后 Google Play 才能允许再次购买。
重要说明:可以消耗托管的应用内商品,但不能消耗订阅。
如何在应用中使用消耗机制取决于您。通常情况下,您可以对用户想要购买多次、能够提供短期效益的应用内商品实现消耗(例如,游戏中使用的货币或设备)。 您通常不必对仅供购买一次和具有永久效应的应用内商品实现消耗(例如,高级版升级)。
要记录购买消耗,请将 consumePurchase
方法发送到应用内购买结算服务并在标识要移除购买的 purchaseToken
字符串值中传递。 purchaseToken
是由购买请求成功后 Google Play 服务在 INAPP_PURCHASE_DATA
字符串中所返回数据的一部分。 在此示例中,您会将使用 purchaseToken
标识的商品的消耗记录在 token
变量中。
int response = mService.consumePurchase(3, getPackageName(), token);
警告:请不要在主线程上调用 consumePurchase
方法。 调用此方法会触发网络请求,进而阻塞主线程。 请创建单独的线程并从该线程内部调用consumePurchase
方法。
您负责控制和跟踪如何向用户配置应用内商品。 例如,如果用户购买了游戏内货币,您应使用购买的货币金额更新玩家的库存。
安全性建议:您必须首先发送消耗请求,才能向用户配置可消耗的应用内购买商品。 确保已从 Google Play 接收到成功的消耗请求,然后再配置商品。
启动订阅的购买流程与启动商品的购买流程相似,不同之处是商品类型必须设置为“订阅”。 购买结果会传送至您 Activity 的onActivityResult
方法,与应用内商品的情况完全一样。
Bundle bundle = mService.getBuyIntent(3, "com.example.myapp", MY_SKU, "subs", developerPayload); PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT); if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) { // Start purchase flow (this brings up the Google Play UI). // Result will be delivered through onActivityResult(). startIntentSenderForResult(pendingIntent, RC_BUY, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); }
要查询有效订阅,请使用 getPurchases
方法,并将商品类型参数设置为“订阅”。
Bundle activeSubs = mService.getPurchases(3, "com.example.myapp", "subs", continueToken);
调用会返回 Bundle
,其包含由用户拥有的所有有效订阅。 如果订阅到期且不续订,将不会出现在返回的 Bundle
中。
为了确保发送到您应用的交易信息的完整性,Google Play 会签署包含购买订单响应数据的 JSON 字符串。 Google Play 会使用 Developer Console 中与您的应用关联的私钥来创建此签名。 Developer Console 会为每个应用生成一个 RSA 密钥对。
注:要找到此密钥对的公钥部分,请在 Developer Console 中打开应用的详细信息,然后点击 Services & APIs,并查看命名为 Your License Key for This Application 的字段。
由 Google Play 生成的以 Base64 编码的 RSA 公钥为二进制编码,格式为 X.509 subjectPublicKeyInfo DER SEQUENCE。 它与 Google Play 许可使用的公钥相同。
当您的应用接收到带签名的响应后,您可以使用 RSA 密钥对的公钥部分验证签名。通过执行签名验证,您可以检测到被篡改或假冒的响应。 您可以在应用中执行此签名验证步骤;不过,如果您的应用是连接到安全的远程服务器,我们建议您在该服务器上执行签名验证。