本文档基于now.gg的官方示例应用程序和游戏,提供了各种 代码示例
。您可以查看文档中内联的参考资料,以便更好地理解now.gg支付模块。
1. 解压下载的now.gg支付模块压缩包,并在其中找到.aar文件:
Payments.aar
2. 添加以下依赖项,到您游戏的 build.gradle
文件中:
dependencies { implementation fileTree(dir: 'libs', include: ['*.aar']) }
now.gg支付的购买生命周期流程如下:
now.gg支付使用购买令牌(Purchase Tokens)和订单ID(Order IDs)来跟踪您游戏或应用内的产品和交易。
权利或授予权利一词用于表示在用户购买产品后,向用户“交付应用内产品”。
SERVICE_DISCONNECTED
错误代码表明now.gg支付服务(now.gg Payments Service)与您的游戏之间的连接已断开。解决此问题的方法是重新初始化您的应用/游戏,以重新建立与now.gg支付服务的连接。BillingClient 接口是 now.gg支付模块和您的应用/游戏之间的主要通信机制。它负责为一般的计费操作提供同步和异步方法。
BillingClient的创建 --> 使用 newBuilder() 设置购买的状态更新 --> Call setListener() 通过应用/游戏接收状态更新 --> 使用 PurchasesUpdatedListener 为计费设置PAYMENT_ID --> 使用 setAppId(PAYMENT_ID) 初始化后查询未消费的购买 --> 使用 queryPurchasesAsync()
以下示例展示了如何创建和初始化 BillingClient
:
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() { @Override public void onPurchasesUpdated(int billingResult, List<Purchase> purchases) { if (billingResult == BillingResponse.OK) { if (purchases == null) { return; } for (Purchase purchase: purchases) { String payload = purchase.getDeveloperPayload(); // 获取开发者负载 // 在此处理成功购买 } } else if (billingResult == BillingResponse.USER_CANCELED) { Log.i(TAG, "onPurchasesUpdated() - user cancelled the purchase flow - skipping"); } else { Log.w(TAG, "onPurchasesUpdated() got unknown billingResult: " + billingResult); } } }; private BillingClient billingClient = BillingClient.newBuilder(activity) .setListener(purchasesUpdatedListener) .setAppId(PAYMENT_ID) .build();
private val purchasesUpdatedListener = object : PurchasesUpdatedListener { override fun onPurchasesUpdated(billingResult: Int, purchases: List<Purchase>?) { if (billingResult == BillingResponse.OK) { if (purchases == null) { return } for (purchase in purchases) { val payload = purchase.developerPayload // 获取开发者负载 // 在此处理成功购买 } } else if (billingResult == BillingResponse.USER_CANCELED) { Log.i(TAG, "onPurchasesUpdated() - user cancelled the purchase flow - skipping") } else { Log.w(TAG, "onPurchasesUpdated() got unknown billingResult: $billingResult") } } } private val billingClient = BillingClient.newBuilder(activity) .setListener(purchasesUpdatedListener) .setAppId(PAYMENT_ID) .build()
now.gg支付服务有一个异步连接过程,并且需要您实现 BillingClientStateListener
来捕获客户端设置完成时的回调。
startConnection()
。startConnection()
一次。queryPurchasesAsync()
方法查询未消费的购买,以确保用户之前游戏会话中的任何未消费购买都能正确分配给用户。
注意:您可以选择编写自己的重试逻辑。不过,我们也提供了一个重试逻辑供您选择实现。
示例
billingClient.startConnection(new BillingClientStateListener() { @Override public void onBillingSetupFinished(int billingResult) { if (billingResult == 0) { // BillingClient已准备好,您可以在此处查询购买。 QueryPurchasesParams params = QueryPurchasesParams.newBuilder().setProductType(BillingClient.SkuType.ALL).build(); billingClient.queryPurchasesAsync(params, new PurchasesResponseListener() { @Override public void onQueryPurchasesResponse(BillingResult billingResult, List < Purchase > list) { // 将购买的物品分配给用户,并调用消耗/确认。 } }); } } @Override public void onBillingServiceDisconnected() { // 调用startConnection()方法, // 以在下次向支付模块发起请求时尝试重启连接。 } });
billingClient.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: Int) { if (billingResult == 0) { // The BillingClient is ready. You can query purchases here. val params = QueryPurchasesParams.newBuilder().setProductType(BillingClient.SkuType.ALL).build() billingClient.queryPurchasesAsync(params, object : PurchasesResponseListener { override fun onQueryPurchasesResponse(billingResult: BillingResult, list: List< Purchase >) { // 将购买的物品分配给用户,并调用消耗/确认。 } }) } } override fun onBillingServiceDisconnected() { // 调用startConnection()方法, // 以在下次向支付模块发起请求时尝试重启连接。 } })
本章节将引导您完成在应用/游戏中查询和列出可用的应用内产品以及处理购买的过程。
此流程的步骤如下:
在开始之前,请检查您是否已经:
第一步是查询可用的产品并将它们列出给您的用户。要查询应用内产品的详细信息,您需要调用 querySkuDetailsAsync()
。当您查询 SKU的详细信息时,now.gg 付款模块将返回本地化的产品信息以便展示给您的用户。
注意:在向用户展示应用内产品之前,您应该先查询 SKU的详细信息,因为它会返回最新相关的产品信息。
SKU类型和订单字符串(Order Strings)
以下是 SKUType
与now.gg支付相关的内容:
当您调用 querySkuDetailsAsync()
,记得传递一个 SkuDetailsParams
的实例,该实例指定了在 nowStudio 中配置的 SKUType
以及产品ID。
结果
查询可用的产品后,必须分配一个侦听器来捕获传递的异步操作的结果。
以下的示例展示了 SkuDetailsResponseListener
接口的实现,并且允许您重写 onSkuDetailsResponse()
,以便在查询完成时通知侦听器。
List < String > skuList = new ArrayList < > (); skuList.add("premium_upgrade"); skuList.add("gas"); SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); params.setSkusList(skuList).setType(SkuType.ALL); billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() { @Override public void onSkuDetailsResponse(int billingResult, List < SkuDetails > skuDetailsList) { // 在此处处理结果。 } });
val skuList = mutableListOf<String>() skuList.add("premium_upgrade") skuList.add("gas") val params = SkuDetailsParams.newBuilder() .setSkusList(skuList) .setType(SkuType.ALL) billingClient.querySkuDetailsAsync(params.build(), object : SkuDetailsResponseListener { override fun onSkuDetailsResponse(billingResult: Int, skuDetailsList: List<SkuDetails>) { // 在此处处理结果。 } })
SkuDetails
对象,以列出有关应用内产品的信息。SkuDetails
对象列表中。在列出可供用户购买的可用产品后,您必须从应用/游戏启动购买流程。
要开始购买流程,您需要从应用程序的主线程调用 launchBillingFlow()
方法。
在此,调用 BillingFlowParams 对象作为参考,因为它包含了储存在 SkuDetails
对象中的相关的详细信息。SkuDetails 对象是通过调用 querySkuDetailsAsync()
获得的。
要开始购买流程,你需要使用 BillingFlowParams.Builder 类创建 BillingFlowParams
对象。请使用以下代码片段作为参考:
// 计费流程启动的 activity 引用。 Activity activity = ...; // 调用 querySkuDetailsAsync() 以获取 'skuDetails' 的值。 BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetails) .setDeveloperPayload("可选开发者负载") .build(); billingClient.launchBillingFlow(activity, billingFlowParams); //结果
// 计费流程启动的 activity 引用。 val activity : Activity = ...; // 调用 querySkuDetailsAsync() 以获取 'skuDetails' 的值。 val flowParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetails) .setDeveloperPayload("可选开发者负载") .build() billingClient.launchBillingFlow(activity, flowParams) //结果
您可以使用 BillingClient.BillingResponse 作为返回的响应代码的参考。
0
)表示成功启动。launchBillingFlow()
后,系统将显示购买页面。onPurchasesUpdated()
将购买结果传达给实现的接口监听器 PurchasesUpdatedListener
。setListener()
方法 初始化计费客户端 时指定的。以下示例概述了重写 onPurchasesUpdated()
方法的过程:
@Override void onPurchasesUpdated(int billingResult, List<Purchase>) { if (billingResult == 0 && purchases != null) { for (Purchase purchase : purchases) { String payload = purchase.getDeveloperPayload(); // 获取开发者负载 handlePurchase(purchase); } } else if (billingResult == 1) { // 错误处理 - 由于用户终止购买流程 } else { // 错误处理 - 其他原因(基于错误码) } }
override fun onPurchasesUpdated(billingResult: int, purchases: List<Purchase>?) { if (billingResult == 0 && purchases != null) { for (purchase in purchases) { val payload: String = purchase.developerPayload // 获取开发者负载 handlePurchase(purchase) } } else if (billingResult == 1) { // 错误处理 - 由于用户终止购买流程 } else { // 错误处理 - 其他原因(基于错误码) } }
注意:要处理各种响应代码,应实现 onPurchasesUpdated()
方法。
本章节重点介绍如何处理购买。开始购买流程后,您需要按以下方式处理购买:
您可以使用以下两种方法之一来验证购买:
验证购买的第一种方法是使用 verifyPurchase API。您可以使用此 API 与我们的后端服务器验证购买。
要验证购买,请从您的应用程序后端服务器调用 verifyPurchase API,并使用 purchaseToken
,如下示例请求代码所示:
import requests url = "https://payments-api.now.gg/v2/sellers/order/verifyPurchase" headers = { "Authorization": "<payment_api_key_here>", "Content-Type": "application/x-www-form-urlencoded" } data = { "purchaseToken": "<purchase_token_here>", "productId": "<product_id_here>", // 仅适用于多商店 "currency": "<currency>", // 仅适用于多商店 "type": "<store>" // 选项: xiaomi, onestore, amazon, huawei. 如果不存在,则默认值为 nowgg。 } response = requests.post(url, headers=headers, data=data) print("响应状态码:", response.status_code) print("响应正文:", response.text)
curl --location --request POST 'https://payments-api.now.gg/v2/sellers/order/verifyPurchase' \ --header 'Authorization: <payment_api_key_here>' \ --header 'Content-Type': 'application/x-www-form-urlencoded' \ --data '{ "purchaseToken": "<purchase_token_here>", "productId": "<product_id_here>", // 仅适用于多商店 "currency": "<currency>", // 仅适用于多商店 "type": "<store>" // xiaomi | onestore | amazon | huawei 如果不存在,则默认值为 nowgg。 }'
验证购买的第二种方法就是使用公钥。使用公钥来验证购买的实现方式可以参考演示应用程序中的 billingManager.java
代码文件。
verifyValidSignature
函数,再定位 verifyPurchase
函数。通过参考我们演示应用程序中的上述函数的实现源文件 billingManager.java
,您可以在您的应用中实现购买验证。
注意:
public key
。在您验证购买之后,您的应用可以授予用户所购买产品的权利,并标记此产品为“已消费”(consumed,于可消耗型产品而言)或者“已确认”(Acknowledged,于订阅型产品而言)。
如何分配购买的权利给用户,见如下方式:
我们提供了两种方式可来实现购买:
我们提供了两种方法来授权确认购买的订阅:
我们提供了两种方法来实现购买:
注意:如果您的应用仅有客户端,并无后端服务器,那么您应该使用这一方法。
如何授予权利:
consumeAsync()
方法。
Consumed
),这样一来,通过now.gg计费服务使得该产品变为可再次购买的状态。.onConsumeResponse()
,这个函数您可以进行重写。ConsumeResponseListener
接口的对象没有被更新。下列代码片段展示了如何实现购买验证和消费操作:
void handlePurchase(Purchase purchase) { // 从 BillingClient#queryPurchasesAsync 或者 您的 PurchasesUpdatedListener 获取的购买。 Purchase purchase = ...; // 验证购买。 // 确保还未将purchaseToken的权利分配给用户后, // 将权利分配给用户。 // 将购买消费掉 ConsumeResponseListener listener = new ConsumeResponseListener() { @Override public void onConsumeResponse(int billingResult, String purchaseToken) { if (billingResult == 0) { // 消费操作成功进行的处理。 } } }; billingClient.consumeAsync(purchase.getPurchaseToken(), listener); }
fun handlePurchase(purchase: Purchase) { // 从 BillingClient#queryPurchasesAsync 或者 您的 PurchasesUpdatedListener 获取的购买。 val purchase: Purchase = ...; // 验证购买。 // 确保还未将purchaseToken的权利分配给用户后, // 将权利分配给用户。 // 将购买消费掉 billingClient.consumeAsync(purchase.getPurchaseToken(), { billingResult, outToken - > if (billingResult == 0) { // 消费操作成功进行的处理。 } }) }
对于具有后端服务器的应用, consumePurchase
API 应被用于服务器端的购买消费。
在您验证购买之后,您应该标记该产品为“已消费”。
实现购买的消费包括:
下列代码范本展示了使用 consumePurchase
API 的相关请求。
import requests url = "https://payments-api.now.gg/v2/order/consumePurchase" headers = { "Authorization": "<payment_api_key_here>", "Content-Type": "application/x-www-form-urlencoded" } data = { "purchaseToken": "<purchase_token_here>", "productId": "<product_id_here>", // 仅适用于多商店 "developerPayload": "developerPayload", // 可选 仅适用于多商店 "currency": "<currency>", // 仅适用于多商店 "type": "<store>" // xiaomi, onestore. /如果不存在,则默认值为 nowgg。 } response = requests.post(url, headers=headers, data=data) print("响应状态码:", response.status_code) print("响应正文:", response.text)
curl --location --request POST 'https://payments-api.now.gg/v2/order/consumePurchase' \ --header 'Authorization: <payment_api_key_here>' \ --header 'Content-Type': 'application/x-www-form-urlencoded' \ --data '{ "purchaseToken": "<purchase_token_here>", "productId": "<product_id_here>", // 仅适用于多商店 "developerPayload" : developerPayload, // 可选, 仅适用于多商店 "currency": "<currency>", // 仅适用于多商店 "type": "<store>" // xiaomi | onestore // 如果不存在,则默认值为 nowgg }'
参考资料
consumePurchase
API之前,请确保将购买的产品分配给用户。Payment API Key
)。 更多信息我们提供了两种方式来实现订阅型产品的确认:
注意:如果您的应用仅包含供客户端,没有后端服务器,则应使用此方法。
如何授予权利:
acknowledgePurchase()
方法。
acknowledged
),并允许now.gg 计费服务将产品置为可重新购买的状态。onAcknowledgePurchaseResponse()
,您可以重写此函数。重要信息:
AcknowledgePurchaseResponseListener
接口状态的对象未被更新。以下代码演示了购买验证和确认操作:
void handlePurchase(Purchase purchase) { // 从 BillingClient#queryPurchasesAsync 或者您的 PurchasesUpdatedListener 获取的购买。 Purchase purchase = ...; // 验证购买。 // 确保还未将purchaseToken的权利分配给用户后, // 将权利分配给用户。 // 确认订阅 final AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = new AcknowledgePurchaseResponseListener() { @Override public void onAcknowledgePurchaseResponse(@BillingResponse int responseCode) { if (responseCode == 0) { // 确认操作成功进行的处理。 } } }; AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchaseToken) .build(); BillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener); }
fun handlePurchase(purchase: Purchase) { // 从 BillingClient#queryPurchasesAsync 或者您的 PurchasesUpdatedListener 获取的购买。 val purchase: Purchase = ...; // 验证购买。 // 确保还未将purchaseToken的权利分配给用户后, // 将权利分配给用户。 // 确认订阅 val acknowledgePurchaseResponseListener = object : AcknowledgePurchaseResponseListener { override fun onAcknowledgePurchaseResponse(@BillingResponse responseCode: Int) { if (responseCode == 0) { // 确认操作成功进行的处理。 } } val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchaseToken) .build() BillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener) }
acknowledgePurchase
API 应被用于具有后端服务器的应用的服务器端的购买确认。
当您验证购买之后,应将其标记为已确认。
以下示例代码演示了使用 acknowledgePurchase
API 的关联请求。
import requests url = "https://payments-api.now.gg/v2/seller/order/acknowledgepurchase" payload = 'purchaseToken=nowgg-_purchase_token' headers = { 'Authorization': 'payment_api_key_here', 'Content-Type': 'application/x-www-form-urlencoded' } response = requests.request("POST", url, headers=headers, data=payload) print(response.text)
参考资料
Payment API Key
)可以在 nowStudio 的证书章节中找到。 更多信息PurchasesUpdatedListener
为您提供购买结果,但是在某些情况下,您的应用/游戏可能不会及时更新用户所有成功的购买。
其中一些情况包括:
PurchasesUpdatedListener
,的成功购买通知。要成功处理与上述情况相关的购买,请执行以下操作:
onResume()
和 onCreate()
方法中,确保您的应用调用了 BillingClient.queryPurchasesAsync(SkuType.ALL)
。以下部分说明了检查和分配未消费的应用内购买和订阅的过程。
您可以使用 queryPurchasesAsync 方法,以返回用户的未消费购买列表,如下所示:
QueryPurchasesParams params = QueryPurchasesParams.newBuilder().setProductType(BillingClient.SkuType.ALL).build(); billingClient.queryPurchasesAsync(params, new PurchasesResponseListener() { @Override public void onQueryPurchasesResponse(BillingResult billingResult, List < Purchase > list) { // 将购买的物品分配给用户,并调用消耗/确认。 } });
val params = QueryPurchasesParams.newBuilder() .setProductType(BillingClient.SkuType.ALL) .build() billingClient.queryPurchasesAsync(params, object : PurchasesResponseListener { override fun onQueryPurchasesResponse(billingResult: BillingResult, list: List) { // 将购买的物品分配给用户,并调用消耗/确认。 } })
queryPurchasesAsync()
方法查询未消费的购买,以确保用户之前游戏会话中的任何未消费购买都能正确分配给用户。
本章节说明了 API 规范,以便您为我们提供 SubscriptionStatusCallback
API,用于发送订阅状态的更新。
以下是相关的工作流程:
参考资料: Subscription Status Callback API 章节。