本文档基于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支付服务的连接。在初始化 now.gg Payments 之前,请务必检查 now.gg IAP 是否可用。只有当 now.gg IAP 不可用时,才应初始化其他 IAP 服务。
if (!BillingClient.checkIAPAvailability(activity)) {
// now.gg iap 不可用
return;
}
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 付款模块将返回本地化的产品信息以便展示给您的用户。
querySkuDetailsAsync() 时,请确保传入包含您在 nowStudio 中配置的商品 ID 的 SkuDetailsParams 实例。结果
以下的示例展示了 SkuDetailsResponseListener 接口的实现,并且允许您重写 onSkuDetailsResponse(),以便在查询完成时通知侦听器。
List < String > skuList = new ArrayList < > ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList);
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)
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 验证购买。
要验证购买,请从您的应用程序后端服务器调用 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>"
}
response = requests.post(url, headers=headers, data=data)
print("响应状态码:", response.status_code)
print("响应正文:", response.text)
curl -X \ POST "https://payments-api.now.gg/v2/sellers/order/verifyPurchase" \ -H \ "Authorization: <payment_api_key_here>" \ -H \ "Content-Type: application/x-www-form-urlencoded" \ -d "purchaseToken=<purchase_token_here>"
在您验证购买之后,您的应用可以授予用户所购买产品的权利,并标记此产品为“已消费”(consumed,于可消耗型产品而言)或者“已确认”(Acknowledged,于订阅型产品而言)。
如何分配购买的权利给用户,见如下方式:
我们提供了两种方式可来实现购买:
我们提供了两种方法来授权确认购买的订阅:
我们提供了两种方法来实现购买:
注意:如果您的应用仅有客户端,并无后端服务器,那么您应该使用这一方法。
如何授予权利:
consumeAsync() 方法。
Consumed),这样一来,通过now.gg计费服务使得该产品变为可再次购买的状态。.onConsumeResponse(),这个函数您可以进行重写。queryPurchasesAsync() 来检查未消耗的购买。参考此处。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>",
"developerPayload": "developerPayload" // 可选
}
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>",
"developerPayload" : "developerPayload" // 可选
}'
参考资料
我们提供了两种方式来实现订阅型产品的确认:
注意:如果您的应用仅包含供客户端,没有后端服务器,则应使用此方法。
如何授予权利:
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=<purchase_token_here>'
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 章节。