适用于商户在移动端APP中集成Gate Pay支付功能
商户APP调用Gate Pay提供的SDK调用Gate Pay支付模块,商户APP会跳转到 Gate APP中完成支付,支付完后跳回到商户APP内,最后展示支付结果。
目前支持手机系统有:IOS(苹果)、Android(安卓)。
交互细节如下:
步骤1:用户进入商户APP,选择商品下单、确认购买,进入支付环节。商户服务后台生成支付订单,签名后将数据传输到APP端。
步骤2:用户点击后发起支付操作,进入到Gate 界面,调起Gate Pay支付,出现确认支付界面。
步骤3:用户确认收款方和金额,点击立即支付后出现输入密码界面。
步骤4:输入正确密码后,支付完成,现支付结果页面。
步骤5:回跳到商户APP中,商户APP根据支付结果个性化展示订单处理结果。
以下是交互时序图,下单API、支付结果通知API和查询订单API等都涉及签名过程,调用都必须在商户服务器端完成。如下图所示
调起支付主要流程:
步骤1-6:
用户发起支付,商户端调用GatePay下单(/v1/pay/order
)API创建预支付订单。
商户调用下单API后,分正常返回和异常返回情况:
正常返回:预付单创建成功返回prepay_id
。
异常返回:返回http code或错误码,商户可根据错误码说明来排查原因并执行下一步操作。
步骤6-14:
情况一:第六步如果商户在成功调用下单接口获取预订单ID,并且同时调起支付组件接口需求的参数和签名返回给商户APP端,商户APP端可直接调用调起支付组件接口。
情况二:第六步如果商户在返回预订单ID没有返回调起参数和签名,商户服务端侧需要提供获取签名接口,商户客户端需要主动请求商户后端获取。
当调起支付组件签名验证返回成功,SDK弹出支付组件并调用查询订单信息。
步骤15-17:
用户支付成功后,支付结果会同步通知给客户端。商户可通过以下两种方式获取订单状态:
方法一:支付结果通知。用户支付成功后,GatePay会将支付成功的结果以回调通知的形式同步给商户,商户的回调地址需要在调用APP下单API时传入notify_url
参数。
方法二:当因网络抖动或本身notify_url
存在问题等原因,导致无法接收到回调通知时,商户也可主动调用查询订单API(/v1/pay/order/query
) 来查询订单状态 。
作用: 调起支付组件签名验证接口。
JSON (content-type: application/json)
POST
/v1/pay/open/sdk
接口定义:
参数名 | 变量 | 类型 | 必填 | 描述 |
---|---|---|---|---|
应用ID | clientid | string | 是 | Gate开放平台审核通过的移动应用appid |
商户号 | merchantid | string | 是 | 商户号 |
预订单ID | prepayid | string | 是 | 预订单ID |
订单详情扩展字符串 | package | string | 是 | 暂填写固定值GatePay |
请求示例:
curl --location --request POST 'https://openplatform.gateapi.io/v1/pay/open/sdk' \
--header 'Content-Type: application/json' \
--header 'X-GatePay-Certificate-ClientId: zb6WUrBDZlRAT7qz' \
--header 'X-GatePay-Timestamp: 1674117221032' \
--header 'x-GatePay-Nonce: 7436636664' \
--header 'x-GatePay-Signature: 11e658cf6b09d917caf9d4bb6ec4493431c55f066a694e58a5571a4a1a114ebc2011d346f340e54a5a2e2edaa172e907742fbdc99b94d009dade4b551daabd07' \
--data-raw '{"prepayid":"50620368071692288","package_ext":"ext"}'
成功响应:
{
"status":"SUCCESS",
"code":"000000",
"errorMessage":"",
"data":{
"prepayid":"50620368071692288"
}
}
失败响应:
{
"status":"FAIL",
"code":"10002",
"errorMessage":"",
"data":{}
}
为方便商户同步订单信息,GatePay提供了查询接口用于查询预付单与退款单信息
JSON (content-type: json)
POST
/v1/pay/order/query
接口定义:
属性名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
prepayId | string | 否 | 预付单信息 |
merchantTradeNo | string | 否 | 商户订单id。预付单id与此id提供一个即可 |
请求示例:
curl --location '127.0.0.1:8201/v1/pay/order/query' \
--header 'Content-Type: application/json' \
--header 'X-GatePay-Certificate-ClientId: zb6WUrBDZlRAT7qz' \
--header 'X-GatePay-Timestamp: 1678442053228' \
--header 'x-GatePay-Nonce: 2002257450' \
--header 'x-GatePay-Signature: ce9ed61041a3a76d292c7475e3957d2e788dd21e7e8b6aa523867a047892e4395a0dfd0e851e9a357a3582babe6a02e40ccff8111ee86d317cf56838f0b5568c' \
--data '{
"prepayId": "56416503889661952"
}'
响应示例:
{
"status": "SUCCESS",
"code": "000000",
"errorMessage": "",
"data": {
"prepayId": "56335302571069440",
"merchantId": 10002,
"merchantTradeNo": "118223456798",
"transactionId": "",
"goodsName": "NF2T",
"currency": "USDT",
"orderAmount": "1.9",
"status": "EXPIRED",
"createTime": 1675392982792,
"expireTime": 1675396480000,
"transactTime": 0,
"order_name": "MiniApp-Payment#118223456798",
"pay_currency": "",
"pay_amount": "0",
"expectCurrency": "BTC",
"rate": "0"
}
}
请求回应类型:
属性名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
status | string | 是 | SUCCESS 或者 FAIL |
code | string | 是 | 出错代码 |
data | query order return type | 否 | 支付订单信息 |
errorMessage | string | 否 | 错误信息 |
query order return type:
属性名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
prepayId | string | 是 | 预付单id |
merchantId | int64 | 是 | 用于申请商户账号的Gate UID |
merchantTradeNo | string | 是 | 商户订单 |
transactionId | string | 是 | 交易id |
goodsName | string | 是 | 商品名称 |
currency | string | 是 | 订单币种 |
orderAmount | string | 是 | 订单金额 |
status | string | 是 | 订单状态 PENDING -订单待支付,PROCESS -地址支付处理中状态,PAID -支付成功,EXPIRED -订单超时,CANCELLED - 订单被商户主动取消,ERROR -订单支付失败错误 |
createTime | int64 | 是 | 预付单创建时间,单位毫秒 |
expireTime | int64 | 是 | 预付单过期时间,单位毫秒 |
transactTime | int64 | 是 | 支付完成时间,单位毫秒 |
order_name | string | 是 | 订单名称 |
pay_currency | string | 是 | 用户实际支付币种 |
pay_amount | string | 是 | 用户实际支付金额 |
expectCurrency | string | 否 | 商家创建订单时,指定营收币种,注:仅在商户指定结算币种的订单详情中返回 |
actualCurrency | string | 否 | 订单支付完成后,Gate后台实际结算到商户账户的币种,注:仅在订单Gate结算给商户后才在订单详情中返回 |
actualAmount | string | 否 | 订单支付完成后,对应Gate后台实际结算到商户账户的币种的金额,注:仅在订单Gate结算给商户后才在订单详情中返回 |
rate | string | 是 | 闪兑支付时的汇率 |
在预付单状态发生改变时,比如超出支付时间而导致的过期或者支付成功,GatePay后台会向商户发送通知。通知地址是商户注册时提供的callback url。
如果由于网络或者其他未知原因导致通知失败,GatePay后台会每隔3秒重试10次。如果重试也失败,商户可以通过查询API查询预付单最新状态。
JSON (content-type: application/json)
POST
属性名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
bizType | string | 是 | 业务类型,必须为 "PAY" |
bizId | string | 是 | 预付单的 ID |
bizStatus | string | 是 | 业务状态,可为 "PAY_SUCCESS" 或 "PAY_CLOSED" |
data | string | 是 | 订单数据类型对象的 JSON 格式字符串 |
order data 类型
属性名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
merchantTradeNo | string | 是 | 商户订单 ID |
goodsName | string | 是 | 商品名称 |
terminalType | string | 是 | 交易来源,例如 "MINIAPP" |
currency | string | 是 | 订单币种 |
orderAmount | string | 是 | 交易金额 |
payerId | int64 | 否 | 付款用户 ID |
请求示例:
{
"bizType": "PAY",
"data": "{\"merchantTradeNo\":\"56236\", \"terminalType\":\"MINIAPP\", \"currency\":\"BTC\", \"orderAmount\":\"1.91\", \"payerId\":513301}",
"bizId": "1647557960944",
"bizStatus": "PAY_SUCCESS"
}
返回示例:
{
"returnCode": "SUCCESS",
"returnMessage": null
}
Cocoapods: pod 'GateOpen'
Info.plist
配置Info.plist必要的参数:
在LSApplicationQueriesSchemes内添加参数: gateio、gatepay
GOPayRequest
GOPayRequest用于封装支付请求必需的参数的类。
@interface GOPayRequest : GORequest
///Gate 提供的api_key
@property (nonatomic, copy) NSString *api_key;
///请求生成时的UTC时间戳
@property (nonatomic, copy) NSString *timestamp;
///随机字符串,建议长度在32个字符以内,字符串组成为数字和字母。
@property (nonatomic, copy) NSString *nonce;
///请求签名
@property (nonatomic, copy) NSString *sign;
/// 预订单ID
@property (nonatomic, copy) NSString *prepayid;
/// 回跳scheme 如:scheme://xxxx
/// 如果是深度连接:<https://xxxx>
@property (nonatomic, copy) NSString *redirectUri;
@end
GateOpenManager
GateOpenManager用于发起和接收支付请求回调的类。
@interface GateOpenManager : NSObject
+ (instancetype)shared;
/// 调起支付方法
/// @param payItem 支付所需参数
/// @param handle 支付完成回调
- (void)payment:(GOPayRequest *)payItem result:(PayResultHandle)handle;
/// appdelegate 跳转解析
/// @param url delegate OpenURL
- (void)handle:(NSURL *)url;
@end
GOPayResponse
GOPayResponse用于接收支付返回结果的类。
/// 支付完成回跳返回模型
@interface GOPayResponse : GOResponse
/// 是否支付成功
@property (nonatomic, assign) BOOL isSuccess;
/// 带的消息
@property (nonatomic, strong) NSString * _Nullable message;
- (instancetype _Nonnull)initWith:(BOOL)isSuccess
message:(NSString * _Nullable)message;
@end
第三方应用需要从自己服务器获取支付请求参数,创建GOPayRequest对象后, 再向支付SDK发起支付请求
/// 创建发起支付请求参数 GOPayRequest
GOPayRequest *payRequest = [[GOPayRequest alloc] init];
payRequest.prepayid = @"prepayId";
payRequest.timestamp = @"timestamp"
payRequest.nonce = @"signature";
payRequest.sign = @"signature";
payRequest.api_key = @"api_key";
payRequest.redirectUri = @"schemes://";
/// 发起支付请求
[[GateOpenManager shared] payment:payRequest result:^(GOPayResponse * _Nullable response) {
wSelf.payResultInfo.text = response.isSuccess ? @"支付成功" : @"支付失败";
wSelf.payResultInfo.textColor = response.isSuccess ? UIColor.greenColor : UIColor.redColor;
}];
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
[[GateOpenManager shared] handle:url];
return YES;
}
<repos>
文件夹集成到项目根目录,或集成到本地maven仓库。在project级别的build.gradle添加本地maven地址
allprojects {
repositories {
maven {
url uri("${rootProject.projectDir}/repos")
}
}
}
在app module的build.gradle文件的dependencies中添加依赖
implementation 'com.gateio.sdk:gatepay-sdk:1.0.0'
在Application 中 onCreate 中初始化:
GatePaySDK.init(boolean isDebug, final Context context, String clientId, String redirectUri)
参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
isDebug | boolean | 否 | 是否调试,开启调试模式(线上版本需要关闭!!!)(Log 日志筛选 "gate_pay_sdk" 可获得对应异常信息提示) |
context | Context | 是 | 建议传递 Application Context。 |
clientId | String | 是 | GatePay 平台获取的 clientId (即 Gate 提供的 api_key)。 |
redirectUri | String | 是 | 当前应用需要回调 scheme uri(如项目未实现 scheme 跳转,可尝试传入 "gate_default_scheme",gatepay-sdk 自动获取当前 Activity 实现跳转)。 |
注:参数都不能为null,否则会造成初始化失败
在当前页面获取预支付订单和签名信息后,通过GatePaySDK.startGatePay来调起Gate支付组件。
GatePaySDK.startGatePay(Activity activity, String signature, String timesTamp, String nonce,
String prepayId, String packageExt, NavigationGatePayListener navigationGatePayListener)
参数说明:
属性名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
activity | Activity | 是 | 当前页面activity |
signature | String | 是 | 请求签名,Gate Pay通过此签名来确定此请求是否合法 |
timesTamp | String | 是 | 请求⽣成时的UTC时间戳,milliseconds。请注意,Gate Pay不处理收到请求时间与这个时间戳差距⼤于5秒钟的请求 |
nonce | String | 是 | 随机字符串,字符符合HTTP Header 头部的规范,建议⻓度在32个字符以内,字符串组成为数字和字⺟ |
prepayId | String | 是 | 获取的预支付订单ID |
packageExt | String | 是 | 扩展字段,取固定值"GatePay" |
navigationGatePayListener | NavigationGatePayListener | 是 | 调起组件成功或失败监听,可处理调起逻辑 |
错误码code说明:
onGateOpenFailed(int code, String errorMessage)
code:10021 errorMessage:openPackage failed 请检查App是否有调起其他应用权限。
code:10022 errorMessage:intentData error 请检查PrepayId和redirectUri是否正确。
code:10023 errorMessage:response data is null 请检查验签的参数是否正确。
code:10023 errorMessage:动态获取throwable.getMessage() 请检查onGateOpenFailed异常信息提示,做对应排查。
code:10024 errorMessage:params error 请检查初始化或者调起支付传参是否正确。
注:非以上code,可直接查看errorMessage错误信息,或对应查看Gate Pay服务端对接错误码。
注:GatePaySDK.gateResumePayResult()方法避免重复被调用,内部已实现清除逻辑,只能获取一次,如需Log打印或作其它判断,可获取后暂存该值。
说明:
回调中Code值
Code:10005---failed 支付失败(常量值:GatePayConstant.MESSENGER_STATE_CODE_FAILED)
Code:10006---cancel 取消支付(常量值:GatePayConstant.MESSENGER_STATE_CODE_FAILED_CANCEL_PAY)
Code:10010---success 支付成功(常量值:GatePayConstant.MESSENGER_STATE_CODE_SUCCESS)
注: 如为10010--支付成功,则去后台查询支付结果再展示用户实际支付结果。注意一定不能以客户端返回作为用户支付的结果,应以服务器端的接收的支付通知或查询API返回的结果为准。
填写了redirectUri仍无法回到当前App? 查看AndroidManifest,当前Activity是否已设置android:exported="true" (建议启动模式为android:launchMode="singleTask")
调起startGatePay方法没有响应? 在初始化的时候将isDebug设置为true,通过Logcat筛选"gate_pay_sdk"关键字可查看对应异常提示。 包括但不限于以下情况:
- SDK未初始化
- 没有填写clientId
- 没有填写redirectUri
- 暂未安装Gate App (SDK已实现跳转下载)
- 暂未升级最新版Gate App[3.11.1+] (SDK已实现弹框提醒更新下载)
获取GatePayConstant.INTENT_GATE_B_CLASS_PARAM,参数为Json格式,如:{"code":10006,"state":"cancel"}
方式1):直接onNewIntent中获取
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null) {
String intentData = intent.getStringExtra(GatePayConstant.INTENT_GATE_B_CLASS_PARAM);
}
}
方式2):onResume()中获取
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
@Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
if (intent != null) {
String intentData = intent.getStringExtra(GatePayConstant.INTENT_GATE_B_CLASS_PARAM);
}
}
提供平台信息接入后,Gate Pay收到这些信息后会给第三⽅提供api_key和api_secret,其中api_key作为身份标识,api_secret⽤于请求签名,其中api_secret是⼀定要妥善保存防⽌泄漏的,只能通过服务端生成签名信息。
注:可参考GatePayDemo查看完整接入流程。