TOPPay API接入文档
一、概述
本文档是平台服务端API的统一接口规范和开发指南。主要提供SDK服务器和CP服务器之间的交互接口规范。
二、接入前准备
- 接入前期准备工作,在Meetgames控制台创建游戏后获取以下参数:
| 参数名称 | 参数说明 |
|---|---|
| AppID | 平台分配给研发的应用 id |
| PaySecret | 平台分配给研发的本地化支付秘钥 |
- 接入前期准备工作,在Meetgames控制台配置充值回调接口:
| 参数名称 | 参数说明 |
|---|---|
| 充值结果回调接口 | 即充值结果通知地址,当支付成功后,SDK 服务器会将支付结果回调给该接口 |
三、通信协议
本接口采用HTTP 协议作为通信协议,调用方通过构造HTTP 请求(POST/GET 方式)发起接口请求。
请求与响应内容采用UTF-8字符编码
服务器请求示例
POST https://api-gamepay.meetsocial.com/xxx/xxx
Content-Type: application/json
success
- 响应数据示例
Content-Type: application/json
{
"code": 0,
"message": "success",
"result": {
"xxx": "xxx",
}
}
- 响应错误数据示例
Content-Type: application/json
{
"code": 5003,
"message": "parameter_error",
"success": false
}
四、接口列表
1、支付接口
接口描述:此接口会返回url,地址有效时长为30分钟
返回数据格式:JSON
版本:1
HTTP请求方式:POST
请求方:CP服务器
响应方:平台服务器
请求参数
| 字段名称 | 类型 | 是否必填 | 备注 |
|---|---|---|---|
| outTradeCode | String | 是 | cp订单号 |
| currencyCode | String | 是 | 货币编号, 目前只支持USD |
| countryCode | String | 否 | 国家编号(获取地区国家信息接口返回的result-code)(当paymentChannelCode为CHECKOUT_HOSTED_CARD或CHECKOUT_HOSTED_MADA必填) |
| amount | Long | 是 | 订单价格(货币最小单位)例子:1000 |
| products | Array | 是 | 查看products对象 |
| paymentAppId | Long | 是 | 支付应用id(联系平台获取) |
| paymentChannelCode | String | 是 | 支付方式编号: Card请传入:CHECKOUT_HOSTED_CARD(此支付通道暂时关闭) Mada请传入:CHECKOUT_HOSTED_MADA(此支付通道暂时关闭) AED请传入:PAYBY_HOSTED_AED(需要看下第六的注意点) tazapay请传入:TAZAPAY mix pay请传入:MIXPAY |
| callbackSuccessUrl | String | 否 | 支付成功后跳转地址(当paymentChannelCode为CHECKOUT_HOSTED_CARD或CHECKOUT_HOSTED_MADA必填) |
| callbackFailUrl | String | 否 | 支付失败后跳转地址(当paymentChannelCode为CHECKOUT_HOSTED_CARD或CHECKOUT_HOSTED_MADA必填) |
| callbackCancelUrl | String | 否 | 支付取消后跳转地址(当paymentChannelCode为CHECKOUT_HOSTED_CARD或CHECKOUT_HOSTED_MADA必填) |
| extraParam | Object | 否 | 额外参数(当paymentChannelCode为PAYBY_HOSTED_AED必填) |
| orderSign | String | 是 | 密钥(参照下面的加密方式) |
| ip | String | 否 | 用户IP,建议传入,能提高支付成功率和风控准确率 |
| roleId | String | 否 | 角色ID,建议传入,能提高支付成功率和风控准确率 |
| deviceId | String | 否 | 设备ID,建议传入,能提高支付成功率和风控准确率 |
| userAgent | String | 否 | 浏览器UserAgent信息,建议传入,能提高支付成功率和风控准确率 |
| String | 否 | 用户邮箱,建议传入,能提高支付成功率和风控准确率 | |
| paymentMethod | Object | 否 | 支付方式参数,当paymentChannelCode为TAZAPAY或MIXPAY时必传 |
- extraParam对象:
| 字段名称 | 类型 | 备注 |
|---|---|---|
| redirectUrl | String | AED通道支付后跳转地址(当paymentChannelCode为PAYBY_HOSTED_AED必填) |
- products对象:
| 字段名称 | 类型 | 备注 |
|---|---|---|
| productName | String | 商品名 |
| productCount | Int | 商品数量 |
| productPrice | Long | 商品单价(货币最小单位) |
- paymentMethod对象:
| 字段名称 | 类型 | 备注 |
|---|---|---|
| tazapay | Object | paymentChannelCode为TAZAPAY时必传 |
| mixpay | Object | paymentChannelCode为MIXPAY时必传 |
- tazapay对象:
| 字段名称 | 类型 | 是否必填 | 备注 |
|---|---|---|---|
| String | 是 | 买家邮箱 | |
| countryCode | String | 是 | 买家国家编码,可以从pay/config/countryList获取 |
| firstName | String | 是 | 买家姓氏,长度不能超出250个字符 |
| lastName | String | 是 | 买家名字,长度不能超出250个字符 |
| callbackSuccessUrl | String | 是 | 付款后客户重定向到的URL,请确保该网址位于https下 |
| callbackErrorUrl | String | 是 | 付款错误时重定向客户的URL,请确保该网址位于https下 |
- mixpay对象:
| 字段名称 | 类型 | 是否必填 | 备注 |
|---|---|---|---|
| returnUrl | String | 是 | 付款后客户重定向到的URL,请确保该网址位于https下 |
| failedReturnUrl | String | 是 | 付款错误时重定向客户的URL,请确保该网址位于https下 |
- 加密方式
如amount=aaaa&outTradeCode=xxx¤cyCode=xxx&secretKey=xxx
用此方式拼接以amount、outTradeCode、currencyCode、secretKey这4个字段,最终md5对此字符串加密。计算md5签名时,应以utf-8编码取字符串。
- 响应结果
| 字段名称 | 类型 | 备注 |
|---|---|---|
| url | String | 支付地址 |
| orderNo | String | 平台服务器订单号 |
| outTradeNo | String | CP支付订单号 |
{
"code": 0,
"message": "success",
"result": {
"url": "https://xxx",
"orderNo":xxx,
"outTradeNo":xxxxxx
}
}
- 错误Code编码
| 状态码 | 说明 |
|---|---|
| 0 | 成功 |
| 999 | 服务器内部错误 |
| 5003 | 请求参数错误 |
| 5011 | 订单创建失败 |
| 5014 | 货币单位错误 |
| 5015 | 支付失败 |
| 5017 | 签名校验失败 |
| 5018 | 国家编号错误 |
| 5019 | 支付方式错误 |
2、获取地区国家信息接口
接口描述:支付接口需要的国家编号接口
返回数据支持格式:JSON
版本:1
HTTP请求方式 :GET
请求方:CP服务器
响应方:平台服务器
请求地址:https://api-gamepay.meetsocial.com/pay/country-area/list
响应结果
| 字段名称 | 类型 | 备注 |
|---|---|---|
| code | int | 消息编码 |
| result | Array | code String 地区编号 name String 地区名称 taxPoint double 国家地区税点 |
| message | String | 消息信息 |
3、(tazapay支付方式) 获取支持地区国家信息接口
- 接口描述:支付接口需要的国家编号接口
- 返回数据支持格式:JSON
- 版本:1
- HTTP请求方式 :GET
- 请求方:CP服务器
- 响应方:平台服务器
- 请求地址:https://api-gamepay.meetsocial.com/pay/config/countryList
| 字段名称 | 类型 | 是否必填 | 备注 |
|---|---|---|---|
| paymentChannel | String | 是 | 支付方式:TAZAPAY |
- 响应结果
| 字段名称 | 类型 | 备注 |
|---|---|---|
| code | int | 消息编码 |
| result | Array | code String 地区编号 name String 地区名称 |
| message | String | 消息信息 |
4、查询支付订单接口
- 接口描述:主动查询订单支付状态
- 返回数据支持格式:JSON
- 版本:1
- HTTP请求方式 :POST
- 请求方:CP服务器
- 响应方:平台服务器
- 请求地址:https://api-gamepay.meetsocial.com/order/queryPayOrder
- 请求参数
| 字段名称 | 类型 | 是否必填 | 备注 |
|---|---|---|---|
| payId | String | 是 | 流水号 |
- 响应结果
| 字段名称 | 类型 | 备注 |
|---|---|---|
| code | int | 消息编码 |
| result | Object | 字段名称类型备注payTimedate支付时间payAmountint支付金额货币最小单位如:5099美分payIdString流水号outTradeNoStringCP厂商支付订单号orderStatusint订单状态:1为已完成; 2为失败;3为处理中notifyStatusint异步通知状态:0:未完成;1:已经完成;2:异步通知失败currencyCodeString货币单位如:USD |
| message | String | 消息信息 |
- 特殊响应代码说明
| 代码编号 | 备注 |
|---|---|
| 5001 | 订单不存在 |
5、充值结果回调接口
- 接口描述:即充值结果通知地址,当支付成功后,平台服务器会将支付结果在notify告诉CP服务器,由CP提供地址。
- 返回数据支持格式:JSON
- 版本:1
- HTTP请求方式 :POST
- 请求方:CP服务器
- 响应方:平台服务器
- 请求地址:cp提供回调地址
- Data信息
| 参数 | 必填 | 类型 | 描述 |
|---|---|---|---|
| tradeCode | Y | String | CP支付订单号 |
| orderCode | Y | String | 平台服务器订单号 |
| paymentChannelCode | Y | String | 支付方式 |
| paymentAmount | Y | Long | 订单价格(货币最小单位)例子:1000 |
| paymentStatus | Y | bool | 是否支付 |
| payTime | Y | Long | 时间戳 |
| sign | Y | String | md5加密后的签名(参考以下加密方式) |
- 验证充值是否成功,务必在保证sign正确情况下,paymentAmount也要进行对比,保证充值正确性
- 加密方式
如tradeCode=aaaa&orderCode=xxx&paymentChannelCode=xxx&paymentAmount=xxx&paymentStatus=xxx&payTime=xxx&secretKey=xxx
用此方式拼接以tradeCode、orderCode、paymentChannelCode、paymentAmount、paymentStatus、payTime、secretKey这7个字段,最终md5对此字符串加密。计算md5签名时,应以utf-8编码取字符串。
- 响应数据说明(如果对应的订单CP已经处理完成,请保持返回"true")
| 响应内容 | 描述 |
|---|---|
| true | true表示处理订单成功,平台服务端方收到响应true后不会再通知给cp方(也可返回其他错误信息,SDK服务器方收到true以外的字符串后都会多次重复通知) |
五、接口备注
在用户支付订单完成后,平台服务器会向商户方服务器发起通知,并异步不断尝试直到获取结果。以下为异步通知接口说明:
必须保证服务器异步通知页面(nify_url)上无任何字符,如空格、HTML 标签、开发系统自带抛出的异常提示信息等;
平台服务器使用 POST 方式发送通知信息,里面的数据是一个json字符串,json decode以后得到订单数组。
程序执行完后必须打印输出“true”(不包含引号,不能加入换行符,缩进符等不可见字符)。如果商户反馈给平台服务器的字符不是 true 这 4 个字符,平台服务器会不断重发通知,一般情况下, 一共7次通知(再次通知时间从首次通知失败后,按2m、10m、10m、1h、2h、6h、12h时间规则执行)。7次结束之后将不在重试。
cookies、session 等在此页面会失效,即无法获取这些数据;
该方式的调试与运行必须在服务器上,即互联网上能访问;
CP方不仅要对回调的签名进行验证,还需要对回调的金额进行比对,两方一致方可发货,避免用户造假篡改订单内容
六、注意点
1、如果paymentChannelCode=PAYBY_HOSTED_AED并且网页是WebView或者WKWebView承载,需要特殊处理,具体如下:
- Android
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
try {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
Uri uri = request.getUrl();
if (isPayby(uri) || isTotok(uri) || isBotim(uri)) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
return true;
}
}
} catch (Throwable t) {
t.printStackTrace();
}
return super.shouldOverrideUrlLoading(view, request);
}
private boolean isBotim(Uri uri) {
try {
String scheme = uri.getScheme();
String host = uri.getHost();
String path = uri.getPath();
return "https".equalsIgnoreCase(scheme) && "botim.me".equalsIgnoreCase(host) && "/botim/payby/open-iap-cashdesk".equalsIgnoreCase(path);
} catch (Throwable t) {
t.printStackTrace();
}
return false;
}
private boolean isTotok(Uri uri) {
try {
String scheme = uri.getScheme();
String host = uri.getHost();
String path = uri.getPath();
return "totok".equalsIgnoreCase(scheme) && "payby".equalsIgnoreCase(host) && "/open-iap-cashdesk".equalsIgnoreCase(path);
} catch (Throwable t) {
t.printStackTrace();
}
return false;
}
private boolean isPayby(Uri uri) {
try {
String scheme = uri.getScheme();
String host = uri.getHost();
String path = uri.getPath();
return ("payby".equalsIgnoreCase(scheme) && "payment".equalsIgnoreCase(host) && "/open-iap-cashdesk".equalsIgnoreCase(path))
|| ("https".equalsIgnoreCase(scheme) && "app.payby.com".equalsIgnoreCase(host) && "/open-iap-cashdesk".equalsIgnoreCase(path));
} catch (Throwable t) {
t.printStackTrace();
}
return false;
}
- iOS
- (void)webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString* urlStr = navigationAction.request.URL.absoluteString;
if ([urlStr containsString:@"https://app.payby.com/open-iap-cashdesk"] ||
[urlStr containsString:@"payby://payment/open-iap-cashdesk"] ||
[urlStr containsString:@"https://botim.me/botim/payby/open-iap-cashdesk"] ||
[urlStr containsString:@"totok://payby/open-iap-cashdesk"]) {
NSURL* url = [NSURL URLWithString:urlStr];
if (@available(iOS 10.0, *)) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {
}];
} else {
[[UIApplication sharedApplication] openURL:url];
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}