[!TIP] 目前 SDK 支持
Google play 应用内支付功能,需要在管理后台开启。
1. 支付充值(必接)
1.1 方法说明
调用充值方法,打开 SDK 充值界面。充值成功或者失败,会触发初始化监听器中的 onPaymentSuccess()/onPaymentFailed() 回调方法。代码示例如下:
private void doPayment() {
SFOrder order = new SFOrder();
order.setProductID(productId); // 游戏内的商品ID
order.setProductName(productName); // 游戏内商品名称
order.setProductDesc(productDesc); // 游戏内商品描述
order.setPrice(price); // 商品价格(单位:美分)
order.setCurrency(currency); // 货币单位(固定:USD)
order.setRoleID(roleId); // 角色ID
order.setRoleName(roleName); // 角色名称
order.setRoleLevel(roleLevel); // 角色等级
order.setServerID(serverId); // 服务器ID
order.setServerName(serverName); // 服务器名称
order.setVipLevel(vipLevel); // VIP等级
order.setCpOrderID(cpOrderId); // 游戏自己的订单号(游戏内唯一)
order.setExtra(extra); // 支付成功之后会原样返回给游戏服务器,默认传 1
order.setPayNotifyUrl(payNotifyUrl); // 游戏服务器的支付通知回调地址
SFPlatform.getInstance().pay(activity, order);
}
SFOrder 数据结构如下:
| 参数名称 | 参数类型 | 是否可为空 | 说明 |
|---|---|---|---|
| cpOrderID | String | 否 | 游戏自己的订单号(游戏内唯一) |
| productId | String | 否 | 游戏内的商品 ID |
| productName | String | 否 | 游戏内商品名称,比如 100 元宝,500 钻石... |
| productDesc | String | 否 | 游戏内商品描述,比如 充值 100 元宝,赠送 20 元宝 |
| price | int | 否 | 商品金额(单位:美分) |
| currency | String | 否 | 货币单位,固定 USD |
| serverID | String | 否 | 玩家所在服务器的 ID |
| serverName | String | 否 | 玩家所在服务器的名称 |
| roleID | String | 否 | 玩家角色 ID |
| roleName | String | 否 | 玩家角色名称 |
| roleLevel | Integer | 否 | 玩家角色等级/通关关卡 |
| vipLevel | Integer | 否 | 玩家 VIP 等级 |
| payNotifyUrl | String | 否 | 客户端设置后,SDK Server 优先根据该地址,通知游戏服务器发货 |
| extra | String | 是 | 支付成功之后,SDK Server 原样返回给游戏服务器 |
| isSandbox | boolean | 否 | 是否为沙盒订单(仅用于支付完成的订单) |
| orderResult | SFOrderResult | 否 | 订单详情信息(仅用于支付完成的订单) |
SFOrderResult 数据结构如下:
| 参数名称 | 参数类型 | 是否可为空 | 说明 |
|---|---|---|---|
| orderType | int | 否 | 订单类型(1:游戏订单;2:推荐订单) |
| productType | int | 否 | 商品类型(1:消耗型;2:非消耗型;3:订阅) |
| orderID | String | 否 | SDK 订单 ID(唯一标识) |
| productID | String | 否 | 游戏内商品 ID |
| realProductID | String | 否 | 对应在当前支付通道平台后台的商品ID |
| platformPrice | String | 是 | 平台商品价格 |
| platformCurrency | String | 是 | 平台商品货币单位 |
| platformOrderId | String | 否 | 平台订单 ID(仅用于支付完成的订单,唯一标识) |
| platformProductId | String | 否 | 平台商品 ID(仅用于支付完成的订单) |
1.2 支付测试
设置 Google Play 开发者账号后,如果需要集成Google应用内支付功能,还必须在 Google 付款中心设置付款资料。 应用必须集成 Google Play 结算库,然后构建并发布应用,可以将应用发布到任何轨道,包括内部测试轨道。在进行支付测试前,还需要完成以下步骤:
- 创建和配置商品:打开Google Play Console,选择具体应用后配置应用内可购买的商品,包括商品的名称、价格、描述等信息。
- 配置完应用内商品一定要发布,使之生效。
- 一定要保证网络环境所对应的国家在发布范围内。
- 游戏服务不支持商品配置,应用才支持商品信息设置。
- 上传并发布应用:将集成Google Play结算库的应用上传到Google Play Console,可以将应用发布到任何轨道,包括内部测试轨道。
- 应用必须发布后,才可以测试支付功能。
- 应用发布后不会立即生效,如果应该状态变为【已发布】说明发布成功。
- 上传的APK/AAB包必须要有签名,而且不能使用debug签名。
- 测试阶段包发布到内部测试轨道即可,不需要发布到正式渠道。
- 测试轨道也会进行严格审核,一些隐私问题或者政策问题会导致应用无法通过审核甚至下架。
- 安装到设备上用于测试的包可以和上传到Google Play的不同,但要保证这两个包使用相同的包名、签名。
- 测试时使用的网络环境所属的国家和地区一定要在应用发布的国家或者地区范围内。
- 首次将版本发布到开放式、封闭式或内部测试轨道后,测试人员可能需要过几个小时后才能获得您的测试链接。
- 添加测试人员:选择具体的应用后,选择具体的发布渠道(如内部测试]),可以管理测试人员,包括添加、移除测试人员账号。
- 测试人员在设备上安装最新版本的Google Play商店,并登录Google账号。
- 将测试人员的Google账号添加进测试轨道,并将测试连接分享给测试人员。
- 测试人员打开加入测试链接后,会看到测试人员的权利和义务相关说明,点击加入测试的按钮后可以加入测试。
- 在测试设备上下载安装测试包后,可以打开应用进行支付测试,测试不会发生真实付款。
- 结束测试:打开Google Play Console,然后前往要结束的测试对应的测试页面(如内部测试]),然后选择管理轨道,选择页面右上角附近的暂停轨道。
- 根据结束的测试类型和运行的测试数量,可能不需要执行此步骤。
- 测试结束后,测试人员将不会再收到更新,但应用仍会继续安装在他们的设备上。
[!TIP|labelVisibility:hidden|iconVisibility:hidden] 内部测试:创建内部测试版本,快速将应用分发给最多 100 名测试人员,以进行初步质量保证检查。建议在将应用发布到封闭式或开放式轨道之前进行内部测试。如果需要,也可以同时对应用的不同版本进行封闭式和开放式测试以及内部测试。
封闭式测试:创建封闭式测试版本,让更多测试人员测试应用的预发布版本,以收集更有针对性的反馈。如果需要,还可以创建和命名其他封闭式轨道。如果正在测试之前发布的现有应用程序,则只有测试组中的用户才会收到封闭版本的更新。
开放式测试:创建开放式测试版本,对大量用户进行测试,并在 Google Play 上展示应用测试版本。如果进行开放式测试,任何人都可以加入测试计划并向提交私人反馈。在选择此选项之前,请确保应用和商品详情已准备好在 Google Play 上展示。
1.3 许可测试
在开发阶段进行测试,还利用许可测试和Play 结算实验室 来测试应用的 Google Play 结算库集成。在进行许可测试前,还需要完成以下步骤:
- 打开Google Play Console,在左侧菜单中打开
设置 > 许可测试,添加测试人员并保存更改。- 一般来说,未经过签名并上传到 Google Play 的应用不能使用 Google Play 结算库,许可测试人员可以绕过此检查。
- 许可测试人员可以使用测试用付款方式,该方式不会针对测试人员的购买交易收取真实费用。此外,该方式也可以用来模拟付款遭拒等情况。
- 安装Play 结算服务实验室:从 Play 商店下载并安装Play 结算实验室
。
- 在 Play 结算实验室中更改 Play 国家/地区,然后将设置应用于测试。
- 使用同一账号重复测试试用优惠或初次体验优惠。
- 测试订阅价格变动,而不影响其他活跃订阅者。
1.4 优惠方案
Google Play 控制台支持对商品添加优惠方案,优惠方案尽可能不要变更太频繁,否则谷歌返回的商品详情可能是缓存的信息,查询到的优惠信息和实际的支付信息不一致,导致上报的数据有误。添加优惠方案时不要直接删除旧的方案,应该先停用旧方案,再添加新方案。
2. 客户端发货通知(选接)
对于不依赖服务端通知客户端发货的游戏(比如单机游戏),可以采用客户端通知发货的方式,减少出现掉单和重复发货的情况。
2.1 启用客户端发货通知
如果需要使用客户端发货通知功能,需要在SDK初始化成功/登录成功后启用客户端发货通知,可以调用以下方法:
SFPlatform.getInstance().enableClientDeliveryNotification(); // 启用客户端发货通知(仅针对不依赖服务端通知发货的游戏)
2.2 通知服务端发货成功
启用客户端发货通知后,在初始化监听器中的 onPaymentSuccess() 回调方法中根据接收到的订单信息对玩家完成发货并记录订单信息避免重复发货,发货成功后需要通知SDK服务端发货成功,在通知成功之前,SDK会在合适的时机重复触发 onPaymentSuccess() 回调方法,避免出现掉单和遗漏的情况。通知服务端发货成功可以调用以下方法:
// 对于Google推荐订单,由于不是游戏通过正常支付流程发起的,所以没有游戏订单号,需要游戏创建一个新的订单用于存档
String cpOrderID = order.getCpOrderID();
if (cpOrderID == null || cpOrderID.length() == 0) {
// 根据商品ID创建游戏订单
cpOrderID = createGameOrder(order.getProductID());
// 创建游戏订单后传入cpOrderID
order.setCpOrderID(cpOrderID);
}
// 通知服务端发货成功(仅针对不依赖服务端通知发货的游戏,未通知成功的订单需要在合适的时机再次发起通知)
boolean success = SFPlatform.getInstance().notifyServerDeliverySuccess(order);
[!TIP|labelVisibility:hidden|iconVisibility:hidden] 注意:必须启用客户端发货通知之后,才可以通过客户端通知服务端发货成功,两者必须同时使用。在完成通知服务端发货成功之前,游戏可能会多次接收到同一个订单的支付成功回调,游戏需要记录订单信息避免重复发货!游戏可通过
order.getOrderResult()获取SFOrderResult,然后使用orderID和platformOrderId两种订单号作为订单的唯一标识,正常情况下支付成功的订单都存在orderID和platformOrderId。
3. 恢复购买(选接)
在游戏中购买了非消耗型商品或通过消耗性商品购买了永久性权益(如:免广告卡),可以通过恢复购买功能来恢复用户已购买的权益。
如果是通过
非消耗型商品购买的永久性权益,则可以通过以下方法恢复所有已购买的非消耗型商品,请求结束会触发onRestoreSuccess/onRestoreFailed回调:// 恢复已完成的购买(仅恢复非消耗型商品) SFPlatform.getInstance().restoreCompletedPurchases();如果是通过
消耗型商品购买的永久性权益,则可以通过以下方法恢复指定的已购买的消耗性商品:// 恢复已完成的购买(可恢复消耗型商品和非消耗型商品) SFPlatform.getInstance().restoreCompletedPurchase(productId, new SFRequestResultListener<SFOrderRecord>() { @Override public void onSuccess(SFOrderRecord data) { } @Override public void onFailed(int code, String message) { } });
SFOrderRecord 数据结构如下:
| 参数名称 | 参数类型 | 是否可为空 | 说明 |
|---|---|---|---|
| orderID | String | 否 | 订单ID |
| productID | String | 否 | 商品ID |
| productName | String | 否 | 商品名称 |
| productDesc | String | 是 | 商品描述 |
| productType | int | 否 | 商品类型(1:消耗型;2:非消耗型;3:订阅) |
| price | String | 否 | 订单金额 |
| cpOrderID | String | 否 | 游戏自己的订单号 |
| platformOrderID | String | 否 | 平台订单ID |
| status | int | 否 | 订单状态 |
| createTime | String | 否 | 订单创建时间(UTC+0) |
| finishTime | String | 否 | 订单完成时间(UTC+0) |
| errorCode | int | 是 | 恢复失败的错误码 |
| errorMessage | String | 是 | 恢复失败的错误信息 |
4. 掉单检测(选接)
SDK内部已经集成了掉单检测和补单逻辑,如果需要在游戏内显示由用户触发的补单按钮,可以调用以下方法:
SFPlatform.getInstance().checkPendingPurchases(activity);
5. 监听所在地区的货币(选接)
游戏在游戏内需要监听所在地区的货币单位和商品价格时,可以调用该方法,SDK 只有在成功获取到所在地区的商品详情时才会通知游戏端,游戏端可以将返回的商品详情信息展示给用户,如果返回的商品详情不存在,则游戏端展示原始金额和货币(美元)。代码示例如下:
SFPlatform.getInstance().setLocaleCurrencyListener(new SFLocaleCurrencyListener() {
@Override
public void onLocaleCurrency(List<SFProductDetails> productDetails) {
// productDetails为转换后的商品详情信息,用户重新登录或者切换账号都会触发此回调
}
});
[!WARNING]
- 如果没有收到此回调,金额跟货币单位都使用默认的
- 展示的金额和货币请使用接口动态获取,以支持全球化,不要使用数据表配死
- 此处返回的货币单位和金额仅做展示使用,支付时的货币单位和金额请传原始值,无需改动
SFProductDetails 数据结构如下
| 参数名称 | 参数类型 | 是否可为空 | 说明 |
|---|---|---|---|
| productId | String | 否 | 平台商品 ID |
| productType | String | 否 | 商品类型(inapp:应用内商品;subs:订阅) |
| name | String | 否 | 商品名称 |
| title | String | 否 | 商品标题 |
| description | String | 否 | 商品描述 |
| currency | String | 否 | 货币单位(如:CNY、USD) |
| price | Double | 否 | 商品金额 |
6. 查询已完成订单(选接)
游戏在游戏内需要查询已完成订单时,可以调用该方法,SDK 在查询成功时会通知游戏端。代码示例如下:
SFPlatform.getInstance().queryCompleteOrders(new SFResultListener<List<SFOrderRecord>>() {
@Override
public void onResult(List<SFOrderRecord> orderRecords) {
// orderRecords为用户已完成的订单记录,可能为空
}
});
6. 查询已退款订单(选接)
游戏在游戏内需要查询已退款订单时,可以调用该方法,SDK 在查询成功时会通知游戏端。代码示例如下:
SFPlatform.getInstance().queryRefundOrders(new SFResultListener<List<SFOrderRecord>>() {
@Override
public void onResult(List<SFOrderRecord> orderRecords) {
// orderRecords为用户已退款的订单记录,可能为空
}
});