CanPay 为商户提供聚合支付能力。商户服务端调用开放 API 创建订单,用户在支付页完成付款后,平台向商户回调地址发送异步通知,商户亦可主动查询订单状态。
API 基础地址:https://canpay.vip
payUrl 域名:与 API 基础地址相同(生产 https://canpay.vip/pay/{token},无端口)
通信协议:HTTPS · JSON · Content-Type: application/json
金额单位:人民币分(整数,例如 100 表示 1.00 元)
平台回调 IP:支付成功后平台向商户 notifyUrl 发起通知的出口 IP,请加入商户侧防火墙/白名单:
103.251.113.102
| 类型 | 路径 | 说明 |
|---|---|---|
| 统一下单 | POST /api/pay/unifiedorder | 开放 API,与商户门户同域 |
| 查询订单 | POST /api/pay/query | 开放 API,与商户门户同域 |
| 测试支付通知 | POST /api/pay/testnotify | 联调用,将待支付订单置为已支付并回调商户 |
| 支付页 | GET /pay/{token} | 下单返回的 payUrl |
| 商户门户 | /merchant/ | 浏览器登录,非开放 API |
| 管理后台 | /admin/ | 独立端口 :18888,非开放 API |
| 项目 | 说明 |
|---|---|
| mchId | 平台分配的商户号(10 位数字) |
| secretKey | 商户密钥,用于签名;管理后台创建商户时随机生成,明文保存 |
| wayCode | 支付通道编码,固定 6020 |
所有开放 API 请求均需携带 sign 字段。签名步骤如下:
1. 取请求 JSON 中所有非空参数(sign 本身不参与签名)
2. 按参数名 ASCII 码升序排序
3. 拼接为 key1=value1&key2=value2...
4. 末尾追加 &key=secretKey(商户密钥)
5. 对拼接字符串做 MD5,取 32 位小写十六进制
示例待签名字符串:
amount=100&clientIp=127.0.0.1&mchId=1234567890¬ifyUrl=https://example.com/notify&outTradeNo=T20250101120000&reqTime=1735689600000&subject=测试商品&wayCode=6020&key=your_secret_key
商户发起收款,返回系统订单号与支付链接 payUrl,用户访问支付页扫码完成付款。
POST
请求参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mchId | String | 是 | 商户号 |
wayCode | Integer | 是 | 支付通道编码 |
subject | String | 是 | 商品标题 |
outTradeNo | String | 是 | 商户订单号,同一商户下唯一 |
amount | Integer | 是 | 金额(人民币分) |
clientIp | String | 是 | 客户端 IP |
notifyUrl | String | 是 | 支付成功异步通知地址 |
reqTime | Long | 是 | 请求时间戳(毫秒) |
sign | String | 是 | 签名 |
body | String | 否 | 商品描述 |
extParam | String | 否 | 扩展参数,原样回传 |
returnUrl | String | 否 | 支付完成跳转地址;支付页展示成功后约 1.5 秒自动跳转(仅 http/https) |
请求示例:
{
"mchId": "1234567890",
"wayCode": 6020,
"subject": "测试商品",
"body": "测试描述",
"outTradeNo": "T20250101120000",
"amount": 100,
"clientIp": "127.0.0.1",
"notifyUrl": "https://example.com/notify",
"reqTime": 1735689600000,
"sign": "..."
}
响应参数(data):
| 字段 | 类型 | 说明 |
|---|---|---|
mchId | String | 商户号 |
tradeNo | String | 平台订单号 |
outTradeNo | String | 商户订单号 |
originTradeNo | String | 上游通道单号 |
amount | String | 金额(人民币分) |
payUrl | String | 支付页链接,可生成二维码供用户扫码;未出码等异常时为空 |
expiredTime | String | 订单过期时间(毫秒时间戳);平台按 ORDER_EXPIRE_MINUTES(默认 30 分钟)在下单时计算,非商户入参、非上游返回 |
state | Integer | 订单状态,见订单状态 |
响应示例:
{
"code": 0,
"message": "success",
"sign": "...",
"data": {
"mchId": "1234567890",
"tradeNo": "20250101120000123456",
"outTradeNo": "T20250101120000",
"originTradeNo": "48243067968064",
"amount": "100",
"payUrl": "https://canpay.vip/pay/xxxxxxxx",
"expiredTime": "1735691400000",
"state": 0
}
}
code 为 0 表示请求受理成功;业务结果以 data.state(订单状态)为准(state=0 且 payUrl 非空表示可支付)。
订单 state=1(支付成功)后,平台向统一下单时指定的 notifyUrl 发起 POST 异步通知(JSON)。
POST(平台 → 商户)
通知参数:
| 字段 | 类型 | 说明 |
|---|---|---|
mchId | String | 商户号 |
tradeNo | String | 平台订单号 |
outTradeNo | String | 商户订单号 |
originTradeNo | String | 上游通道单号 |
amount | Integer | 金额(人民币分) |
subject | String | 商品标题 |
body | String | 商品描述 |
extParam | String | 扩展参数 |
state | Integer | 订单状态,1 表示支付成功,见订单状态 |
notifyTime | Long | 通知时间(毫秒时间戳) |
sign | String | 签名 |
通知示例:
{
"mchId": "1234567890",
"tradeNo": "20250101120000123456",
"outTradeNo": "T20250101120000",
"originTradeNo": "48243067968064",
"amount": 100,
"subject": "测试商品",
"body": "测试描述",
"extParam": "",
"state": 1,
"notifyTime": 1735689700000,
"sign": "..."
}
商户应返回:纯文本 SUCCESS 或 OK(大小写不敏感)。
平台回调 IP:
103.251.113.102
根据商户订单号查询订单最新状态与支付信息。
POST
请求参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mchId | String | 是 | 商户号 |
outTradeNo | String | 是 | 商户订单号 |
reqTime | Long | 是 | 请求时间戳(毫秒) |
sign | String | 是 | 签名 |
响应参数(data):
| 字段 | 类型 | 说明 |
|---|---|---|
mchId | String | 商户号 |
wayCode | Integer | 通道编码 |
tradeNo | String | 平台订单号 |
outTradeNo | String | 商户订单号 |
originTradeNo | String | 上游通道单号 |
amount | String | 金额(人民币分) |
subject | String | 商品标题 |
body | String | 商品描述 |
extParam | String | 扩展参数 |
notifyUrl | String | 异步通知地址 |
payUrl | String | 支付页链接 |
expiredTime | String | 过期时间(毫秒);平台下单时按配置计算,非商户入参 |
successTime | String | state=1 时的时间(毫秒),否则为空 |
createTime | String | 创建时间(毫秒) |
state | Integer | 订单状态:0 待支付,1 支付成功,2 支付失败,3 未出码,4 异常 |
notifyState | Integer | 通知状态:0 未通知,1 成功,2 失败 |
notifyIsTest | Integer | 最近一次通知是否来自测试接口:1 是,0 否 |
exchangeSource | String | 下单时汇率来源:wise / frankfurter / fallback 等 |
exchangeRateNote | String | 下单时汇率说明 |
响应示例:
{
"code": 0,
"message": "success",
"sign": "...",
"data": {
"mchId": "1234567890",
"wayCode": 6020,
"tradeNo": "20250101120000123456",
"outTradeNo": "T20250101120000",
"amount": "100",
"state": 1,
"payUrl": "https://canpay.vip/pay/xxxxxxxx",
"successTime": "1735689700000",
"createTime": "1735689600000",
"notifyState": 1
}
}
用于联调商户 notifyUrl:按平台订单号将订单置为 state=1(已支付),再向 notifyUrl 发送回调;通知次数 +1。
POST
请求参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mchId | String | 是 | 商户号 |
tradeNo | String | 是 | 平台订单号 |
reqTime | Long | 是 | 请求时间戳(毫秒) |
sign | String | 是 | 签名 |
请求示例:
{
"mchId": "1234567890",
"tradeNo": "P20250101120000abcd",
"reqTime": 1735689600000,
"sign": "..."
}
响应参数(data):
| 字段 | 类型 | 说明 |
|---|---|---|
mchId | String | 商户号 |
tradeNo | String | 平台订单号 |
outTradeNo | String | 商户订单号 |
state | Integer | 调用后订单状态,成功置为已支付时为 1 |
notifyState | Integer | 通知状态:1 成功,2 失败 |
notifyTimes | Integer | 累计通知次数 |
notifyOk | Integer | 本次是否通知成功:1/0 |
notifyIsTest | Integer | 固定 1,表示本次为测试通知 |
state=1),并写入 successTime;回调 payload 中 state 同为 1。请结合 notifyIsTest 区分测试与正式通知。
| wayCode | 说明 |
|---|---|
6020 | QRIS 动态二维码(印尼盾通道,人民币计价) |
更多通道请联系平台运营开通。
| state | 说明 |
|---|---|
0 | 待支付 |
1 | 支付成功 |
2 | 支付失败(上游关闭、失败等) |
3 | 未出码(上游未返回有效二维码) |
4 | 异常(如上游通信失败、响应无法解析) |
| notifyState | 说明 |
|---|---|
0 | 尚未通知商户 |
1 | 通知成功 |
2 | 通知失败 |
接口层 code 为 0 表示请求处理成功;业务结果以 data.state(订单状态)为准。