跳到主要内容

Android 集成指南

环境要求

  • Gradle 版本不低于 6.1.1,Android AGP 插件版本不低于 4.0.1;

准备

SDK 指南

SDK 集成

打开项目的 project/app/build.gradle 文件,添加 gradle 配置如下:

dependencies {
...
// TapTapIAP dependency
implementation 'com.taptap.android.payment:iap:4.3.1'
implementation 'com.taptap.android.payment:base:4.3.1'
implementation 'com.taptap.android.payment:stripe:4.3.1'
implementation 'com.taptap.android.payment:braintree:4.3.1'
implementation 'com.taptap.android.payment:alipay:4.3.1'
}

SDK 初始化

添加 TapTapIAP 的依赖项后,您需要初始化 TapTapIAP 实例。TapTapIAP 是 SDK 与应用的其余部分之间进行通信。TapTapIAP 为许多常见的操作提供了方法。

首先 在应用启动时需要进行 SDK 初始化, 通过设置 TapTapSdkOptions, 完成 SDK 初始化, TapTapSdk.init

这里需要设置对应在开发者后台申请的 ClientIDClientToken,用于校验是否有权限使用 TapTapIAP

TapTapSdkOptions sdkOptions = new TapTapSdkOptions(
"应用的 ClientID", // clientId 开发者平台申请
"应用的 ClientToken", // clientToken 开发者平台申请
TapTapRegion.GLOBAL, // 地区
"", // 分包渠道名称
"", // 游戏版本, 为空为null会取AppVersion
false, // 是否自动上报GooglePlay内购支付成功事件 仅 [TapTapRegion.GLOBAL] 生效
false, // 自定义字段是否能覆盖内置字段
null, // 自定义属性,启动首个预置事件(device_login)会带上这些属性
null, // OAID证书, 用于上报 OAID 仅 [TapTapRegion.CN] 生效
false // 是否开启 log,建议 Debug 开启,Release 关闭
);

TapTapSdk.init(context, sdkOptions);

创建 TapTapIAP,请使用 newBuilder()这里会根据SDK.init所设置的 ClientIDClientToken校验是否有权限使用 TapTapIAP

// 创建 TapTapIAP 实例
TapTapIAP tapTapIAP = TapTapIAP.newBuilder().build();

展示可供购买的商品

初始化完成 TapTapIAP 后,您就可以查询可售的商品并将其展示给用户了。

查询应用内商品详情,请调用 queryProductDetailsAsync()。为了处理该异步操作的结果,您还必须指定实现 ProductDetailsResponseListener 接口的监听器。然后,您可以替换 onProductDetailsResponse(),该方法会在查询完成时通知监听器,如以下示例所示:

List<Product> queryProductList = new ArrayList<>();
// 支持批量查询 Product, 设置好对应的 ProductID、ProductType
// ProductType 目前仅支持 ProductType.INAPP
for (int i = 0; i < products.length; i++) {

String productId = products[i];
Product product = Product.newBuilder()
.setProductId(productId)
.setProductType(ProductType.INAPP)
.build();
queryProductList.add(product);
}
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
.setProductList(queryProductList).build();
tapTapIAP.queryProductDetailsAsync(params, new ProductDetailsResponseListener() {
@Override
public void onProductDetailsResponse(TapPaymentResult result,
List<ProductDetails> productDetails, List<String> unavailableProductIds) {
...

// check TapPaymentResult
// process returned productDetails
}
});

启动购买流程

如需从应用发起购买请求,请从应用的主线程调用 launchBillingFlow() 方法。此方法接受对 BillingFlowParams 对象的引用,该对象包含通过调用 queryProductDetailsAsync() 获取的相关 ProductDetails 对象。如需创建 BillingFlowParams 对象,请使用 BillingFlowParams.Builder 类。

// An activity reference from which the billing flow will be launched.
Activity activity = ...;
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
// retrieve a value for "productDetails" by calling queryProductDetailsAsync()
.setProductDetails(productDetails)
.build();

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParams(productDetailsParams)
.setObfuscatedAccountId("xxx") //Specifies an optional obfuscated string that is uniquely associated with the order(or another information) in your app.
.build();
// Launch the billing flow
TapPaymentResult result = tapTapIAP.launchBillingFlow(activity,
billingFlowParams,
new PurchaseUpdatedListener() {
@Override
public void onPurchaseUpdated(TapPaymentResult result, Purchase purchases) {
// To be implemented in a later section.
}
}
);

launchBillingFlow() 方法会返回 TapPaymentResponseCode 中列出的几个响应代码之一。请务必检查此结果,以确保在启动购买流程时没有错误。TapPaymentResponseCodeOK 表示成功启动。成功调用 launchBillingFlow() 后,会向用户展示收银台。

购买流程中订单状态监听

在购买流程中, TapTapIAP 会调用 onPurchasesUpdated(),以将购买的订单状态变更实时传给实现 PurchasesUpdatedListener 接口的监听器。您可以在初始化时使用 setListener() 方法指定监听器。您必须实现 onPurchasesUpdated() 来处理可能的响应代码。以下提供了一个 onPurchasesUpdated() 示例 :

@Override
public void onPurchaseUpdated(TapPaymentResult result, Purchase purchase) {
if (result.getResponseCode() == TapPaymentResponseCode.OK
&& purchases != null) {
handlePurchase(purchase);
} else if (result.getResponseCode() == TapPaymentResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
} else {
// Handle any other error codes.
}
}

进行商品的发放,且完成订单

在用户进行完了任意商品的购买, 请确认为该用户发放对应的商品或者解锁对应的关卡。在确定商品发放之后需要调用 finishPurchaseAsync 告知 TapTapIAP 已完成商品的发放。以下是个代码实例:

Purchase purchase = ...;
FinishPurchaseParams params = FinishPurchaseParams.newBuilder()
.setId(purchase.getOrderId()) // Required
.setOrderToken(purchase.getOrderToken()) // Required
.setPurchaseToken(purchase.getPurchaseToken()) // Required
.build();
tapTapIAP.finishPurchaseAsync(params, new FinishPurchaseResponseListener() {
@Override
public void onFinishPurchaseResponse(TapPaymentResult result, Purchase purchase) {
}
});
提示

确认发放商品非常重要, 如果您没有调用 finishPurchaseAsync 来完成订单, 用户将无法再次购买该商品,且该订单将会在 3天后自动退款。

获取未完成的订单列表

使用 PurchasesUpdatedListener 监听购买交易变更,无法完全确保您的应用会处理所有购买交易。有时您的应用可能不知道用户进行了部分购买交易。在下面这几种情况下,您的应用可能会跟踪不到或不知道购买交易的发生:

  • 在购买过程中出现网络问题:用户成功购买了商品并收到了对应渠道的确认消息,但用户设备在通过 PurchasesUpdatedListener 收到购买交易的通知之前失去了网络连接。
  • 多部设备:用户在一部设备上购买了一件商品,然后在切换设备时期望看到该商品。
  • 异常崩溃:用户在外部购买成功时,应用出现了崩溃的情况。

为了处理这些情况,请确保您的应用在 onResume() 方法中调用 tapTapIAP.queryUnfinishedPurchaseAsync(),以确保所有购买交易都得到正确处理。

以下示例展示了如何提取用户的未完成订单列表:

tapTapIAP.queryUnfinishedPurchaseAsync(new PurchasesResponseListener() {
@Override
public void onQueryPurchasesResponse(TapPaymentResult result, List<Purchase> purchases) {
if (purchases != null) {
// Process Purchases.
...
...
}
}
});

处理 TapPaymentResult 响应代码

当使用 TapTapIAP 结算库调用触发操作时,该库会返回 TapPaymentResult 响应,并将结果告知开发者。例如,如果您使用 queryProductDetailsAsync 且返回 OK ,并提供正确的 ProductDetails 对象;或者返回了其他类型,代表了无法提供 ProductDetails 对象的原因。

并非所有类型都是错误。下面列举了一些 TapPaymentResponseCode 不是错误的:

  • TapPaymentResponseCode.OK:代表业务已成功执行。
  • TapPaymentResponseCode.USER_CANCELED:代表用户没有完成流程,就离开了页面

其他的一些错误类型可以用于调试和上报使用:

可重试的 CODE问题可以尝试的解决方案
NETWORK_ERROR此错误代表设备和 TapTapIAP 系统之间的网络连接出现问题可以使用简单的重试策略或指数退避算法
ITEM_ALREADY_OWNED这个类型表明用户已经购买过 非消耗品 商品, 再次购买时会返回该错误为了避免出现这个问题, 在展示商品界面就提示用户已经购买该商品, 且无法点进进行购买流程.
USER_CANCELED用户已退出结算流程
ITEM_UNAVAILABLE商品无效, 有可能是商品已经过期或者商品已经被下架确保您通过 queryProductDetailsAsync 刷新商品详情。如果商品无效则不在界面上展示给用户
DEVELOPER_ERROR这是一个严重错误,表明您未正确使用 API。例如,向 launchBillingFlow 提供不正确的参数可能会导致此错误