一步付款 (OSP)
一步付款(OSP)是一种实施Catappult计费的简单易用的解决方案。 它包含一个URL,该URL可启动AppCoins Wallet应用处理付款,然后根据付款结果通知您将商品提供给最终用户。
总结
具备OSP功能的应用的计费流程如下:
- 最终用户尝试在您的应用上购买产品;
- 应用将您的OSP URL作为意图调用,进而启动一步付款计费流程;
- AppCoins Wallet读取OSP URL意图,处理付款,然后在完成时调用您的Web服务端点;
- 您的Web服务验证交易数据;
- 您将产品提供给最终用户。
接下来在您的应用上实施OSP,您的第一个目标是搜索您的应用启动计费流程的位置。 每当最终用户选择要购买的产品时,您的应用代码中就有个计费流程启动位置。 通常情况下,该流程会创建一个意图,以便最终用户可以在外部应用上完成购买付款。
既然您已经找到您的应用启动计费流程的位置,是时候开始实施一步付款了。 此实施操作包括3个步骤:
- 生成您的OSP URL;
- 创建意图以处理付款;
- 创建要用作回调URL的Web服务端点。
Unity集成
如果您使用Unity构建游戏,请在此处查看我们的文档,了解如何在Unity中集成一步付款:Unity OSP。
1. 生成您的OSP URL
实施一步付款的第一步包括生成您的OSP URL。 OSP URL调用的服务是https://apichain.catappult.io/transaction/inapp但有一些查询参数需要填写,如下表所示:
名称 | 类型 | 描述 | 必须 | 示例 |
---|---|---|---|---|
产品 | 字符串 | 正在购买的产品名称(又名 SKU)。\n它只能包含小写字母、数字、下划线 (_) 和英文句点 (.) | 是 | sword.001 |
域名 | 字符串 | 应用ID,也称为应用包名称。 | 是 | com.appcoins.trivialdrivesample |
callback_url | 字符串 | 交易完成后调用URL的URL编码版本。 | 是 | https%3A%2F%2Fwww.mygamestudio.com%2FcompletePurchase%3FuserId%3D1234是 https://www.mygamestudio.com/completePurchase?userId=1234的URL编码版本 |
order_reference | 字符串 | 开发者创建的交易的唯一标识符(不能用于不同的购买)。 | 否 | XYZ98880032 |
签名 | 字符串 | 用于验证的签名URL的十六进制字符串。\n签名必须为小写。 | 是 | 49bc6dac9780acfe5419eb16e862cf096994c15f807313b04f5a6ccd7717e78e |
价值 | 数字 | 所选产品的价值。 | 否 | |
货币 | 字符串 | 发送款项所用的货币。 须遵守ISO 4217。 | 否 |
最后应该是这样的:
https://apichain.catappult.io/transaction/inapp?product=sword.001&domain=com.appcoins.trivialdrivesample&callback_url=https%3A%2F%2Fwww.mygamestudio.com%2FcompletePurchase%3FuserId%3D1234&signature=91e3488303d93eb637e57f6abb7908837b9d8a3144261aad4b2247de3b1c525a
请注意,签名参数是通过使用SHA256算法使用HMAC函数签名来构建的。 此过程所需的密钥应仅在服务器级别可用,而且应该在开发人员和提供商之间共享。 如想了解有关密钥管理的更多信息,请单击此处。 因此,我们强烈建议您在服务器级别(例如:在Web服务端点上)生成OSP URL。 然后,您的应用必须在创建意图之前向您的服务器请求生成的OSP URL。
产品和价格应在Catappult上注册,以便钱包获得购买价值。 如果应用内产品未在Catappult中注册,并且自动获取处于打开状态,那么它将从Google Play获取产品的美元价值。 如果找不到价值或自动获取功能关闭,建议您手动注册。
下面是您需要在服务器上实现的功能示例,来使用不同的编程语言生成签名的OSP URL:
$product = 'sword.001';
$domain = 'com.appcoins.trivialdrivesample';
$callback_url = 'https://www.mygamestudio.com/completePurchase?userId=1234';
$encoded_callback_url = urlencode($callback_url);
$url = 'https://apichain.catappult.io/transaction/inapp';
$url .= '?product='.$product;
$url .= '&domain='.$domain;
$url .= '&callback_url='.$encoded_callback_url;
$SECRET_KEY = 'secret';
$signature = hash_hmac('sha256', $url, $SECRET_KEY, false);
$signed_url = $url.'&signature='.$signature;
const crypto = require('crypto');
let product = 'sword.001';
let domain = 'com.appcoins.trivialdrivesample';
let callback_url = 'https://www.mygamestudio.com/completePurchase?userId=1234';
let encoded_callback_url = encodeURIComponent(callback_url);
let url = 'https://apichain.catappult.io/transaction/inapp';
url += '?product=' + product;
url += '&domain=' + domain;
url += '&callback_url=' + encoded_callback_url;
let secret_key = 'secret';
let signature = crypto.createHmac("sha256", secret_key).update(url).digest('hex');
let signed_url = url + '&signature=' + signature;
import urllib.parse
import hmac
import hashlib
product = "sword.001"
domain = "com.appcoins.trivialdrivesample"
callback_url = "https://www.mygamestudio.com/completePurchase?userId=1234"
encoded_callback_url = urllib.parse.quote(callback_url, safe="")
url = "https://apichain.catappult.io/transaction/inapp"
url += "?product=" + product
url += "&domain=" + domain
url += "&callback_url=" + encoded_callback_url
secret_key = b'secret'
signature = hmac.new(secret_key, url.encode("utf-8"), hashlib.sha256).hexdigest()
signed_url = url + "&signature=" + signature
2. 创建意图以处理付款
您已经生成了OSP URL,现在可以用其创建意图,并请求其由应用的当前活动处理。
如果设备上安装了AppCoins Wallet,您可以请求它处理已创建的意图。 否则,将由设备的默认Web浏览器处理意图。 这部分的实施应如下所示:
fun launchOsp(activity: Activity) {
try {
val domain = "com.appcoins.trivialdrivesample"
val product = "sword.001"
val ospUrl = generateOspUrl(domain, product)
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(ospUrl)
if (isAppCoinsWalletInstalled(activity)) {
intent.setPackage("com.appcoins.wallet")
}
activity.startActivityForResult(intent, 10003)
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun generateOspUrl(domain: String, sku: String, value: Number, currency: String): String {
// 待办事项:发送请求以从您的服务器获取OSP URL,然后将其返回
return "https://apichain.catappult.io/transaction/inapp?domain=com.appcoins.trivialdrivesample&product=sword.001&callback_url=https%3A%2F%2Fmygamestudio.co%2Fappcoins%3Fout_trade_no%3D1234&signature=49bc6dac9780acfe5419eb16e862cf096994c15f807313b04f5a6ccd7717e78e"
}
private fun isAppCoinsWalletInstalled(activity: Activity): Boolean {
val packageManager = activity.applicationContext.packageManager
val intentForCheck = Intent(Intent.ACTION_VIEW)
if (intentForCheck.resolveActivity(packageManager) != null) {
try {
packageManager.getPackageInfo("com.appcoins.wallet", PackageManager.GET_ACTIVITIES)
return true
} catch (e: PackageManager.NameNotFoundException) {}
}
return false
}
public static void launchOsp(Activity activity) {
try {
String domain = "com.appcoins.trivialdrivesample";
String product = "sword.001";
String ospUrl = generateOspUrl(domain, product);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(ospUrl));
if (isAppCoinsWalletInstalled(activity)) {
intent.setPackage("com.appcoins.wallet");
}
activity.startActivityForResult(intent, 10003);
} catch (Exception e) {
e.printStackTrace();
}
}
private static String generateOspUrl(String domain, String sku, float value, String currency) {
// 待办事项:发送请求以从您的服务器获取OSP URL,然后将其返回
return "https://apichain.catappult.io/transaction/inapp?domain=com.appcoins.trivialdrivesample&product=sword.001&callback_url=https%3A%2F%2Fmygamestudio.co%2Fappcoins%3Fout_trade_no%3D1234&signature=49bc6dac9780acfe5419eb16e862cf096994c15f807313b04f5a6ccd7717e78e";
}
private static boolean isAppCoinsWalletInstalled(Activity activity) {
PackageManager packageManager = activity.getApplicationContext().getPackageManager();
Intent intentForCheck = new Intent(Intent.ACTION_VIEW);
if (intentForCheck.resolveActivity(packageManager) != null) {
try {
packageManager.getPackageInfo("com.appcoins.wallet", PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {}
}
return false;
}
注意:如果您的应用的目标是SDK 30或更高版本,那么您需要将意图添加到AndroidManifest,如下所示:
<manifest>
...
<queries>
<package android:name="com.appcoins.wallet" />
...
</queries>
...
</manifest>
此时由AppCoins Wallet负责处理付款过程。 最终用户要么使用其现有积金支付,要么使用一种可用方式为其钱包充值,然后再完成支付。
3. 创建要用作回调URL的Web服务端点
AppCoins Wallet上的交易完成后,将向您在OSP URL的查询字符串参数callback_url中指定的Web服务端点发出POST请求。 在此POST请求的正文中,将发送一个包含名为transaction的字段的JSON对象。
交易字段也是个JSON对象,应该进行解析,以便获取有关刚刚完成的交易的信息。
名称 | 类型 | 说明 | 示例 |
---|---|---|---|
uid | 字符串 | 交易资源的唯一ID | B27YBHAHN2G3J6RE |
域名 | 字符串 | 程序包名称 | com.appcoins.trivialdrivesample |
产品 | 字符串 | 产品名称(又名SKU) | sword.001 |
参考 | 字符串 | 开发人员创建的交易的唯一标识符。 | XYZ98880032 |
状态 | 字符串 | 交易状态 | 已完成,或已取消,或失败 |
已添加 | 字符串 | 交易添加了时间戳 | 2020-04-18T06:15:18+00:00 |
已修改 | 字符串 | 交易修改了时间戳 | 2020-04-18T07:17:19+00:00 |
类型 | 字符串 | 交易类型 | INAPP_UNMANAGED |
price.appc | 字符串 | 以AppCoins表示的交易价格 | 115 |
price.currency | 字符串 | 交易价格货币(由最终用户用来进行购买) | 美元、APPC、欧元等 |
price.value | 字符串 | 交易价格值 | 11.5 |
price.usd | 字符串 | 以美元表示的交易价格 | 4.99 |
要验证数据完整性,您可以在自己的Web服务上向我们交易的API (https://api.catappult.io/broker/8.20220927/transactions/)发出GET请求,并在该请求中传递交易UID(例如:https://api.catappult.io/broker/8.20220927/transactions/2DtyvTOSShc1xT9C)。 返回的数据必须等于回调URL在其主体上收到的交易数据。 最后,一旦您在服务器上完成了所有验证,您将需要通知您的应用,并将商品提供给最终用户。
常见问题解答
如何从Catappult获取您的产品价格?
要从Catappult获取您的产品详细信息(说明、价格等),您可以在https://api.catappult.io/productv2/8.20220928/applications/${DOMAIN}/inapp/consumables/${PRODUCT_ID}向我们的API发出GET请求,其中字段${DOMAIN}
应填写您的应用程序包名称,字段${PRODUCT_ID}
则填写您的产品ID(例如:https://api.catappult.io/productv2/8.20220928/applications/com.appcoins.trivialdrivesample/inapp/consumables/gas,其中com.appcoins.trivialdrivesample是我们的应用程序包名称,而gas是我们的产品ID)。
同一个应用内商品可以有多个不同的价格/货币吗?
是。 在应用内产品菜单中,可以添加新产品和编辑现有产品。 要查看如何在Catappult上向同一产品添加多种价格和货币,请点击此处。 当产品在Catappult中注册了多种货币时,我们建议您不要在URL中设置价值和货币。 如果您确实在URL上设置,这可能会导致值不匹配,因为如果在Catappult中注册,URL上的货币必须与用户的货币相匹配
Updated over 1 year ago