接口说明
本文档面向接入本平台的商户开发者,说明网关地址、签名鉴权、请求参数、响应结构及业务处理要求
https://ceshi.18w.org
接口金额字段单位为元,建议商户侧固定保留两位小数;所有需要签名的接口请先按签名规则生成签名后再提交
公共规则
| 网关地址 | https://ceshi.18w.org |
| 提交数据格式 | application/x-www-form-urlencoded |
| 字符编码 | UTF-8 |
| 请求方式 | POST |
| 签名算法 | MD5 |
签名规则
签名密钥只应保存在服务端,不应出现在浏览器、App 客户端或可公开访问的前端代码中
签名步骤
1. 移除 sign 和 sign_type 字段 2. 移除空值字段 3. 按参数名升序排序 4. 拼接为 key=value&key2=value2 格式 5. 在末尾追加商户密钥 6. 对最终字符串计算 md5
params 为商户扩展透传字段,下单签名、退款签名和异步通知验签时均不参与签名,无论是否传递或是否为空示例说明
以下示例使用演示密钥 test_key_123456实际接入时请使用商户后台展示的真实 api_key,并保证请求编码为 UTF-8
待签名参数
api_id=10001 mch_order_no=M202606130001 name=test amount=100.00 notify_url=https://merchant.example.com/notify return_url=https://merchant.example.com/return client_ip=127.0.0.1 timestamp=1781260800 method=api sign_type=MD5 params=merchant_attach
拼接过程
1. 移除 sign_type 字段
2. 自有协议移除 params 字段
3. 按参数名升序排序
4. 拼接得到:
amount=100.00&api_id=10001&client_ip=127.0.0.1&mch_order_no=M202606130001&method=api&name=test¬ify_url=https://merchant.example.com/notify&return_url=https://merchant.example.com/return×tamp=1781260800
5. 追加商户密钥后计算 MD5:
md5("amount=100.00&api_id=10001&client_ip=127.0.0.1&mch_order_no=M202606130001&method=api&name=test¬ify_url=https://merchant.example.com/notify&return_url=https://merchant.example.com/return×tamp=1781260800test_key_123456")
6. 得到 sign:
88b747f0b64dc6f990ffe786ee3c928e
PHP 生成签名
function build_sign(array $params, string $apiKey, bool $excludeParams = true): string
{
unset($params['sign'], $params['sign_type']);
if ($excludeParams) {
unset($params['params']);
}
ksort($params);
$pairs = [];
foreach ($params as $key => $value) {
if ($value === '' || $value === null || is_array($value)) {
continue;
}
$pairs[] = $key . '=' . $value;
}
return md5(implode('&', $pairs) . $apiKey);
}
PHP 验签示例
$apiKey = 'test_key_123456';
$params = $_POST;
$merchantSign = $params['sign'] ?? '';
$systemSign = build_sign($params, $apiKey, true);
if (!hash_equals($systemSign, $merchantSign)) {
exit('sign error');
}
注意事项
1. sign 字段必须使用小写 MD5 字符串 2. sign 和 sign_type 永远不参与签名 3. 自有协议 params 永远不参与签名,无论下单请求、退款请求还是异步通知 4. 自有协议统一下单的 method 必填并参与签名 5. 空字符串和 null 不参与签名 6. 金额建议商户侧按字符串处理,例如 100.00,避免浮点数导致签名不一致 7. 验签时必须使用原始接收到的字段值,不要先做 URL 解码之外的格式化、四舍五入或类型转换
支付方式列表
下单接口的 type 字段需使用下表中的编码后台禁用或通道未配置的支付方式可能无法创建订单
方式编码
| 编码 | 说明 |
|---|---|
alipay
|
支付宝 |
wxpay
|
微信支付 |
qqpay
|
QQ钱包 |
设备类型列表
下单接口的 device字段需使用下表中的编码,系统将根据对应设备类型选择最优通道
类型编码
| 编码 | 说明 |
|---|---|
pc
|
pc(电脑浏览器) |
mobile
|
mobile(手机浏览器) |
android
|
android(安卓应用) |
ios
|
ios(苹果应用) |
qq
|
QQ内置浏览器 |
wechat
|
微信内置浏览器 |
alipay
|
支付宝客户端 |
mini
|
小程序 |
对接方式列表
下单接口的method字段需使用下表中的,系统将根据不同的对接方式返回不同的内容
对接方式
| 对接方式 | 说明 |
|---|---|
api
|
传入此值将返回json格式数据,商户可根据返回的支付数据自行安排发起支付的方式 |
submit
|
传入此值可用于用户前台直接发起支付,使用form表单跳转或拼接成url跳转 |
统一下单接口
https://ceshi.18w.org/payment/submit
统一下单接口用于创建支付订单,商户订单号必须保持唯一;重复提交同一订单号时只能沿用首次请求的对接方式,跨 API/submit 模式将被拒绝
请求参数
| 字段名 | 参数 | 必填 | 参与签名 | 说明 |
|---|---|---|---|---|
| 商户对接ID | api_id | 是 | 是 | 商户对接ID |
| 支付方式 | type | 否 | 传入非空时参与 | 支付方式,详见支付方式列表;不传时系统返回收银台地址,由用户选择支付方式 |
| 商户订单号 | mch_order_no | 是 | 是 | 商户订单号,格式为1至64位字母、数字、下划线或短横线;同一商户内必须唯一 |
| 异步回调地址 | notify_url | 是 | 是 | 支付订单异步回调地址 |
| 同步回调地址 | return_url | 是 | 是 | 支付订单页面跳转回调地址 |
| 当前时间戳 | timestamp | 是 | 是 | 10位秒级Unix时间戳,默认允许300秒误差,具体以系统配置为准 |
| 付款用户IP | client_ip | 是 | 是 | 实际付款用户的合法IPv4或IPv6地址,不能固定填写商户服务器IP |
| 商品名称 | name | 是 | 是 | 如超过64个字节会自动截取 |
| 商品金额 | amount | 是 | 是 | 单位元,金额文本最多64字符;仅支持十进制整数或小数,超出两位的小数截断、不四舍五入,截断后须大于0。按传入金额原文签名,建单金额统一为两位小数;例如0012.349按原文签名、按12.34建单。金额及计费结果不得超出数据库字段容量,仍受通道金额限制 |
| 业务扩展参数 | params | 否 | 否 | 没有请留空,回调时将原样传入 |
| 设备类型 | device | 否 | 传入非空时参与 | 用户设备类型,详见设备类型列表 |
| 对接方式 | method | 是 | 是 | 对接方式,仅允许小写api或submit,详见对接方式列表 |
| 请求签名 | sign | 是 | 否 | 按签名规则生成的小写MD5字符串 |
| 签名方式 | sign_type | 是 | 否 | 签名方式,固定传入MD5 |
method=api 请求的实际HTTP来源IP;client_ip 始终表示付款用户IP,两者用途不同,接口无需传入额外的服务器IP字段响应说明
当 method=api 时,系统返回 JSON;自有协议统一使用 data.url 承载支付入口,并通过 data.method 标识链接类型,不返回彩虹协议中的 payurl、qrcode、urlscheme 字段
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 1 表示发起成功,0 表示发起失败 |
msg | string | 响应提示,成功时通常为 发起成功! |
time | int | 系统响应时间戳,单位秒 |
data.order_no | string | 平台订单号,后续查单、支付状态追踪可使用该字段 |
data.mch_order_no | string | 商户订单号,即请求中的 mch_order_no |
data.url | string | 支付入口地址可能是系统支付页、收银台,也可能是上游直连支付链接 |
data.method | string | 链接处理方式,当前支持 jump、qrcode、scheme |
链接处理方式
| 值 | 处理方式 | 说明 |
|---|---|---|
jump | 浏览器跳转 | 商户前端可直接跳转到 data.url系统支付页、收银台默认均为该类型 |
qrcode | 二维码展示 | 商户前端应根据 data.url 生成二维码给用户扫码支付 |
scheme | URL Scheme 拉起 | 商户前端可使用 data.url 拉起 App、小程序或钱包支付环境 |
成功示例
{
"code": 1,
"msg": "发起成功!",
"time": 1781260800,
"data": {
"order_no": "20260613123015000001",
"mch_order_no": "M202606130001",
"url": "https://pay.example.com/payment/order/20260613123015000001",
"method": "jump"
}
}
失败示例
{
"code": 0,
"msg": "签名错误",
"time": 1781260800,
"data": null
}
业务说明
1. mch_order_no 在同一商户内唯一,不同商户可以使用相同商户订单号 2. 重复请求仅在接入协议、金额、商品名、支付类型、请求方式、付款IP、通知地址、返回地址和设备类型均与首次提交一致时复用原订单 3. 未指定支付方式或未确定可用通道时,data.url 可能返回系统收银台地址,由用户选择支付方式 4. 当 method=submit 时,该接口用于浏览器前台提交,成功时直接跳转支付页面,失败时展示 HTML 提示页,不返回上述 JSON 结构
订单查询
https://ceshi.18w.org/payment/query
该接口只读取当前商户的本地订单,不请求上游、不修改订单状态;可查询本商户通过任意接入协议创建的订单
公共参数
| 参数 | 必传 | 参与签名 | 说明 |
|---|---|---|---|
api_id | 是 | 是 | 商户号 |
mode | 是 | 是 | single 查询单笔,list 查询历史列表 |
timestamp | 是 | 是 | 10位秒级 Unix 时间戳 |
sign | 是 | 否 | 请求签名 |
sign_type | 是 | 否 | 固定为 MD5 |
sign 和 sign_type 外,实际传入的所有查询参数都参与签名;查询响应不附加响应签名,请通过 HTTPS 调用单笔查询参数
| 参数 | 必传 | 参与签名 | 说明 |
|---|---|---|---|
order_no | 二选一 | 传入时参与 | 平台系统订单号 |
mch_order_no | 二选一 | 传入时参与 | 商户订单号 |
api_id=10001 mode=single mch_order_no=M202606130001 timestamp=1781260800 sign_type=MD5 sign=请求签名
历史列表参数
| 参数 | 必传 | 参与签名 | 说明 |
|---|---|---|---|
cursor | 否 | 传入时参与 | 下一页游标,最多256字符,原样使用上一次响应的 data.next_cursor,不可跨商户使用 |
limit | 否 | 传入时参与 | 每页1至100条,默认20条 |
status | 否 | 传入时参与 | 订单状态:0未付款、1已支付、2已退款、3已超时、5已冻结;仅支持这些值,省略则不筛选 |
notify_status | 否 | 传入时参与(含0) | 商户异步通知状态:0未通知、1通知成功、2通知失败、3多次通知失败且已停止自动重试;省略则不筛选,可与订单状态组合使用 |
start_time | 否 | 传入时参与 | 创建时间起点,10位 Unix 时间戳 |
end_time | 否 | 传入时参与 | 创建时间终点,10位 Unix 时间戳 |
end_time 时向前补24小时;仅传 start_time 时向后补24小时,终点不超过首次请求时刻。起点不得晚于终点,每个查询窗口最长7天(604800秒);可查询更早的历史窗口,不限于最近7天limit 可调整。每页使用新的 timestamp 重新签名。修改条件请移除游标重新查询;旧版游标会提示重新查询。游标不冻结订单的实时状态api_id=10001 mode=list limit=20 status=1 notify_status=2 start_time=1780656000 end_time=1781260800 timestamp=1781260800 sign_type=MD5 sign=请求签名
订单字段
| 字段 | 类型 | 说明 |
|---|---|---|
order_no | string | 平台系统订单号 |
mch_order_no | string | 商户订单号 |
upstream_order_no | string|null | 上游支付平台订单号 |
type | string|null | 支付方式 |
name | string|null | 商品名称 |
amount | string|null | 订单金额 |
real_amount | string|null | 实际支付金额 |
refund_amount | string|null | 退款金额 |
status | int | 0未付款、1已支付、2已退款、3已超时、5已冻结;单笔与列表语义一致 |
status_text | string | 订单状态文字 |
notify_status | int|null | 0未通知、1通知成功、2通知失败、3多次通知失败且已停止自动重试;与支付状态独立,已支付不代表通知成功 |
created_at | int|null | 订单创建时间 |
paid_at | int|null | 支付完成时间 |
pay_duration | int|null | 支付耗时,单位秒 |
响应结构
单笔:data.order 为订单字段对象 列表: data.items 当前页订单数组 data.next_cursor 下一页游标,无下一页时为 null data.has_more 是否还有下一页 data.limit 本次每页数量 列表接口不返回 total,也不执行总数统计
code=0异步通知
支付完成或自有协议订单最终退款成功后,系统会对商户下单时传入的
notify_url
进行POST请求。首次通知不计入补发次数;首次通知失败后最多自动补发10次,补发间隔依次为5秒、15秒、30秒、60秒,超过前四次后固定每60秒补发一次。首次通知、自动补发和后台人工重新通知的参数、签名与成功响应完全一致,商户处理成功时需返回 HTTP 2xx,并输出纯文本 succeed
支付成功通知
| 参数 | 是否必传 | 参与签名 | 说明 |
|---|---|---|---|
api_id |
是 | 是 | 商户号 |
notify_type |
是 | 是 | 通知类型,支付成功固定为 payment |
order_no |
是 | 是 | 平台订单号 |
mch_order_no |
是 | 是 | 商户订单号 |
type |
是 | 是 | 支付方式编码 |
name |
是 | 是 | 商品名称 |
amount |
是 | 是 | 商户提交订单金额,单位元,字符串格式 |
real_amount |
是 | 是 | 实际支付金额,单位元,字符串格式 |
status |
是 | 是 | 支付成功固定为 succeed |
params |
是 | 否 | 商户下单传入的扩展参数,没有则为空;该字段不参与签名 |
sign |
是 | 否 | 回调签名,按签名规则生成,但排除 params |
sign_type |
是 | 否 | 签名类型,固定为 MD5 |
支付通知处理建议
1. 商户系统收到通知后先校验 sign 2. 验签时移除 sign、sign_type、params,再按签名规则计算 3. 以 mch_order_no 做幂等处理,同一商户订单号重复通知时只处理一次 4. 业务处理成功后输出 succeed,不要输出 JSON、HTML 或其它字符
退款成功通知
notify_type=refund;仅自有协议订单发送,彩虹兼容协议订单不发送退款通知| 参数 | 是否必传 | 参与签名 | 说明 |
|---|---|---|---|
api_id |
是 | 是 | 商户号 |
notify_type |
是 | 是 | 通知类型,退款成功固定为 refund |
order_no |
是 | 是 | 平台原支付订单号 |
mch_order_no |
是 | 是 | 商户原支付订单号 |
refund_no |
是 | 是 | 退款单号,用于退款通知幂等;接口退款时由商户提交,后台或商户页面退款时由平台生成 |
order_amount |
是 | 是 | 原支付订单金额,单位元,两位小数字符串 |
refund_amount |
是 | 是 | 本次退款金额,单位元,两位小数字符串 |
status |
是 | 是 | 退款成功固定为 succeed |
complete_time |
是 | 是 | 退款最终完成时间,10位 Unix 时间戳字符串 |
sign |
是 | 否 | 退款通知签名,按签名规则生成 |
sign_type |
是 | 否 | 签名类型,固定为 MD5 |
sign、sign_type 外,其余字段全部参与签名;退款通知不包含 params退款通知处理建议
1. 退款接口成功响应与退款异步通知相互独立,接口响应不能替代异步通知
2. 收到通知后先移除 sign、sign_type,再使用其余全部字段验签
3. 核对 order_no、mch_order_no、order_amount 和 refund_amount 是否与本地记录一致
4. 以 refund_no 做幂等处理,同一退款单号重复通知时只完成一次退款业务
5. 通知采用至少一次投递,业务处理成功后返回 HTTP 2xx 和纯文本 succeed
接口退款
https://ceshi.18w.org/payment/refund
接口退款用于商户系统对自有协议创建的已支付订单发起退款
请求参数
| 参数 | 是否必传 | 参与签名 | 说明 |
|---|---|---|---|
api_id |
是 | 是 | 商户号 |
order_no |
二选一 | 传入时参与 | 系统订单号,即统一下单成功返回的 data.order_no |
mch_order_no |
二选一 | 传入时参与 | 商户订单号,即统一下单时传入的 mch_order_no |
refund_no |
是 | 是 | 商户退款单号,最长 64 个字符;用于幂等识别 |
amount |
是 | 是 | 退款金额,单位元,最多两位小数,必须大于 0 且不能大于订单实付金额 |
timestamp |
是 | 是 | 10 位 Unix 时间戳,默认允许 300 秒时间窗口,具体以系统配置为准 |
params |
否 | 否 | 商户扩展透传字段,不参与签名,无论是否传递或是否为空 |
sign |
是 | 否 | 请求签名,按签名规则生成 |
sign_type |
是 | 否 | 签名类型,固定为 MD5 |
order_no 和 mch_order_no 至少传入一个;如果两个都传入,系统会同时校验两个字段必须指向同一笔订单退款签名字段
参与签名字段: api_id order_no(传入时参与) mch_order_no(传入时参与) refund_no amount timestamp 不参与签名字段: sign sign_type params
请求示例
api_id=10001 order_no=20260613123015000001 mch_order_no=M202606130001 refund_no=R202606130001 amount=100.00 timestamp=1781260800 sign_type=MD5 params=merchant_refund_attach sign=42ed3b28194b6945c124a1661ba46acf
签名拼接示例
1. 移除 sign、sign_type、params
2. 移除空值字段
3. 按参数名升序排序
4. 拼接得到:
amount=100.00&api_id=10001&mch_order_no=M202606130001&order_no=20260613123015000001&refund_no=R202606130001×tamp=1781260800
5. 追加商户密钥后计算 MD5:
md5("amount=100.00&api_id=10001&mch_order_no=M202606130001&order_no=20260613123015000001&refund_no=R202606130001×tamp=1781260800test_key_123456")
6. 得到 sign:
2166b4e6dbea9efec1e1964bf495faa6
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 1 表示成功,0 表示失败 |
msg | string | 退款成功或失败原因 |
time | int | 响应时间戳 |
data.order_no | string | 系统订单号 |
data.mch_order_no | string | 商户订单号 |
data.refund_no | string | 商户退款单号 |
data.amount | string | 退款金额 |
data.status | string | 退款成功固定为 succeed |
成功示例
{
"code": 1,
"msg": "退款成功",
"time": 1781260800,
"data": {
"order_no": "20260613123015000001",
"mch_order_no": "M202606130001",
"refund_no": "R202606130001",
"amount": "100.00",
"status": "succeed"
}
}
失败示例
{
"code": 0,
"msg": "当前订单不是已支付状态,不能退款",
"time": 1781260800,
"data": null
}
业务说明
1. 该接口只处理本系统自有协议订单,不处理彩虹兼容协议订单 2. 同一订单当前只允许退款一次,支持全额退款或部分退款 3. 重复提交同一 order_no、refund_no、amount 且历史退款成功时,系统会按幂等成功返回 4. 如果订单已退款但 refund_no 或 amount 与历史退款不一致,系统会拒绝重复退款 5. 退款金额按字符串处理,建议固定保留两位小数 6. 支付插件支持自动退款时,系统会先请求上游支付平台;上游返回失败时本次退款失败 7. 支付插件不支持自动退款或通道信息缺失时,允许仅执行系统内退款,不会向上游支付平台发起退款 8. 上游支付平台退款成功后,系统会先记录上游成功状态,再完成系统内退款;系统内处理失败时重试不会再次请求上游 9. 退款接口成功响应与退款异步通知相互独立,商户应以退款成功通知完成本地最终业务
彩虹相关接口说明
本系统兼容彩虹易支付V1对接方式,涵盖以下功能:【页面跳转支付】、【API接口支付】、【支付结果通知】、【查询商户信息】、【查询结算记录】、【查询单个订单】、【查询单个订单】、【提交订单退款】
常见问题
如果接口返回参数错误、签名错误或回调失败,请优先核对请求入口、提交方式、签名字段、时间戳和回调响应内容
为什么提示“本系统接口请使用 /payment/submit 接入”?
通常是把彩虹兼容协议的 pid 参数提交到了自有协议入口自有协议统一下单必须使用 api_id,并通过 /payment/submit 以 POST 方式提交
- 自有协议字段:
api_id、mch_order_no、amount - 彩虹兼容协议字段:
pid、out_trade_no、money - 两个协议的字段不要混用
为什么一直返回签名错误?
签名错误多半不是密钥错误,而是参与签名的字段范围不一致系统会按字段名升序排序,排除 sign、sign_type 和空值字段后再追加商户密钥计算 MD5
- 自有协议中的
params永远不参与签名 - 统一下单的
method必填并参与签名,且只能使用小写api或submit type、device这类可选字段只要传入,就要参与签名- 金额建议固定为字符串,例如
10.00,不要用浮点数直接拼接
为什么提示 timestamp 格式错误或请求已过期?
timestamp 必须是 10 位秒级时间戳,并且会参与签名Java、JavaScript 等语言常见的毫秒时间戳是 13 位,不能直接提交
- 正确示例:
1718000000 - 错误示例:
1718000000000 - 如果时间戳正确仍提示过期,请校准商户服务器时间,建议开启 NTP
method 和 type 有什么区别?
method 是对接方式,决定系统如何返回支付拉起结果;type 是支付方式,决定使用支付宝、微信、QQ 等哪一种支付类型
method=api:适合服务端请求,返回 JSONmethod=submit:适合浏览器表单跳转,可能直接进入支付页面- 相同商户订单号不能在两种对接方式之间切换
type=alipay、wxpay、qqpay:表示支付方式
API 下单成功后应该读取哪个字段?
自有协议统一下单成功时,支付链接在 data.url,链接类型在 data.method不要按彩虹协议去读取 payurl、qrcode 或 urlscheme
data.method=jump:跳转链接data.method=qrcode:二维码内容或二维码链接data.method=scheme:客户端唤起链接
为什么异步回调一直重试?
自有协议支付回调处理成功后,商户系统必须原样返回 succeed不要返回 JSON、HTML、空格、换行或调试信息,否则系统会认为商户没有成功接收通知
- 先验签,再校验订单号和金额,最后更新本地订单
- 同一笔订单可能收到多次通知,商户系统必须按
mch_order_no做幂等 - 同步跳转只用于页面展示,不能作为入账依据
回调里的 amount 和 real_amount 应该如何使用?
amount 是商户下单金额,real_amount 是系统实际支付金额商户收到回调后,应结合本地订单记录做金额校验,确认订单号、金额和状态都匹配后再处理发货或入账
- 不要只判断
status=succeed就更新订单 - 不要跳过签名校验
- 金额比较时建议保留两位小数字符串比较
接口退款时 order_no 和 mch_order_no 应该传哪个?
order_no 是平台系统订单号,mch_order_no 是商户订单号接口退款时二者至少传入一个;如果两个都传入,系统会同时校验它们必须指向同一笔订单
- 商户系统更容易保存和使用
mch_order_no - 如果传入
order_no或mch_order_no,对应字段需要参与退款签名 - 同一笔退款请固定使用同一个
refund_no,不要每次重试都换新退款单号
为什么退款金额格式错误?
退款金额字段 amount 必须为大于 0 的金额字符串,最多保留两位小数,且不能大于原订单实际支付金额
- 正确示例:
1、1.00、10.50 - 错误示例:
1.001、0、-1.00 - 一个订单只能成功退款一次,请做好商户侧退款状态记录
测试支付成功是否代表正式商户一定可用?
测试支付只验证系统配置的测试商户或指定测试通道是否可用,不代表所有正式商户、所有支付方式、所有通道都可用
- 正式接入前仍需检查商户状态、支付方式配置、通道分配和回调地址
- 前台测试支付只展示测试商户可用的支付方式
- 正式环境请使用商户自己的
api_id和api_key
为什么复制 Demo 后仍然请求失败?
Demo 中的网关地址、商户号、密钥、回调地址都是占位配置,复制后必须先替换成商户后台中的真实信息
api_key只能保存在服务端,不能写入前端页面或 App 客户端- 回调地址必须能被平台服务器公网访问
- 如果本地调试无法接收回调,可先使用可公网访问的测试域名或内网穿透工具
DEMO 下载
Demo 包含 PHP 与 Java 版本的自有协议接入示例,覆盖统一下单、接口退款、异步回调验签和同步跳转展示