四方支付商户接口对接文档

本文档提供给商户服务端系统对接,用于接入平台下单、查单和订单状态通知。所有示例中的商户号、密钥、产品编码和域名均为占位内容,实际值以平台后台提供为准。

提交方式:POST Form Content-Type:application/x-www-form-urlencoded 字符集:UTF-8 金额单位:分 签名方式:MD5 大写 时间格式:yyyyMMddHHmmss

1. 对接资料

平台运营会给商户技术或对接系统提供以下资料:

名称说明
mchId商户号。每个商户唯一,下单、查单和验签都需要使用。
secretKey商户签名密钥。只在开户或重置密钥时展示一次,请只保存在服务端。
productId产品编码。商户只能使用平台已绑定并启用的产品编码下单。
下单地址{平台域名}/api/v1/pay/create_order
查单地址{平台域名}/api/v1/pay/query_order
回调IP平台通知商户时的出口 IP。商户如果做通知来源白名单,应放行该 IP。
商户API白名单商户调用下单/查单接口的服务器公网 IP,需要提前提供给平台配置。
商户通知地址不在平台后台固定配置。商户每次下单时通过 notifyUrl 传入本笔订单的通知地址。
商户下单、查单、接收支付通知均统一使用 POST Form。商户服务端只需要按本文档这一套格式对接,不需要适配其它提交方式。

2. 编码、URL 解码与字段约束

请求和通知均使用 application/x-www-form-urlencoded; charset=UTF-8。签名与业务处理都以服务端完成一次 Form URL 解码后的字段名和值为准,不以 HTTP 原始编码串为准。

字段最大值补充约束
mchId64 UTF-8 字节必填标识,不得有首尾空白。
mchOrderNo128 UTF-8 字节必填标识,不得有首尾空白。
productId64 UTF-8 字节必填标识,不得有首尾空白。
amount1 至 9,999,999,999,999,999 分仅 ASCII 十进制正整数;该上限保证可写入平台 NUMERIC(18,4) 金额列,实际通道金额规则仍可能更小。
notifyUrl2048 UTF-8 字节可选;生产环境必须是公网 https 地址,平台会在每次通知前重新校验解析结果。
subject128 UTF-8 字节可选订单标题。
body512 UTF-8 字节可选订单描述。
param1 / param2各 256 UTF-8 字节可选透传字段。
reqTime14 UTF-8 字节固定为 14 位数字 yyyyMMddHHmmss
sign64 UTF-8 字节必填;推荐传 32 位大写 MD5 十六进制值。
整个 HTTP 请求体最大为 1 MiB,超过会返回 HTTP 413。字段长度按 UTF-8 编码后的字节数计算,不是中文字符数;商户应在发送前自行校验。

3. 签名规则

所有商户请求和平台支付通知均使用同一套 MD5 签名规则。

  1. 取一次 Form URL 解码后的全部参数,排除小写 sign 字段。
  2. 排除值恰好为空字符串的参数,例如 param1= 不参与签名;仅含空格的值不是空字符串,仍会参与签名。
  3. 按参数名 ASCII 从小到大排序。
  4. 拼接为 key=value&key2=value2
  5. 末尾追加 &key=商户密钥
  6. 按 UTF-8 字节对完整字符串做 MD5,结果转为大写 32 位十六进制。
reqTime 默认必传并参与签名,格式为 yyyyMMddHHmmss。服务器允许时间误差为 5 分钟,超时会拒绝,防止重放攻击。

签名示例

import crypto from 'node:crypto';

function makeSign(params, secretKey) {
  const pairs = Object.keys(params)
    .filter((key) => key !== 'sign' && params[key] !== undefined && params[key] !== null && String(params[key]) !== '')
    .sort()
    .map((key) => `${key}=${String(params[key])}`);
  return crypto.createHash('md5').update(`${pairs.join('&')}&key=${secretKey}`).digest('hex').toUpperCase();
}

4. 下单接口

地址:POST {平台域名}/api/v1/pay/create_order

字段必填最大值说明
mchId64 字节商户号;不得有前导或尾随空白。
mchOrderNo128 字节商户订单号。同一商户下必须唯一,且不得有前导或尾随空白。重复提交相同金额和产品会返回原订单;金额或产品不一致会被拒绝。
productId64 字节平台提供的产品编码;不得有前导或尾随空白。
amount1 至 9,999,999,999,999,999 分订单金额,单位为分,必须为十进制正整数。例如 100.00 元传 10000
notifyUrl建议必填2048 字节本笔订单状态通知地址。生产环境必须使用公网 https,且不允许内网、localhost、链路本地地址;本地联调只有在服务器显式开启测试开关时才允许 HTTP。
subject128 字节订单标题。为空时平台会使用默认标题。
body512 字节订单描述。
param1256 字节商户自定义透传字段,订单状态通知时原样返回。
param2256 字节商户自定义透传字段,订单状态通知时原样返回。
reqTime14 字节请求时间,固定为 14 位数字格式 yyyyMMddHHmmss,参与签名。
sign64 字节按第 3 节规则生成;推荐传 32 位大写 MD5 值。

请求示例

curl -X POST 'https://pay.example.com/api/v1/pay/create_order' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'mchId=M10001' \
  --data-urlencode 'mchOrderNo=TEST202607090001' \
  --data-urlencode 'productId=8007' \
  --data-urlencode 'amount=10000' \
  --data-urlencode 'notifyUrl=https://merchant.example.com/pay/notify' \
  --data-urlencode 'subject=商品订单' \
  --data-urlencode 'param1=merchant-extra-1' \
  --data-urlencode 'reqTime=20260709213000' \
  --data-urlencode 'sign=按签名规则计算后的MD5大写值'

成功响应

{
  "code": 0,
  "message": "ok",
  "data": {
    "order_no": "SF20260709213000A1B2C3D4",
    "merchant_order_no": "TEST202607090001",
    "pay_url": "https://pay-provider.example/pay?token=xxx",
    "amount": 10000,
    "status": 0
  },
  "request_id": "..."
}
下单成功返回 code=0status=0 只表示平台已受理,不代表付款成功。商户入账必须以后续通知或查单 status=1 为准。

5. 查单接口

地址:POST {平台域名}/api/v1/pay/query_order

字段必填最大值说明
mchId64 字节商户号;不得有前导或尾随空白。
mchOrderNo128 字节商户订单号;不得有前导或尾随空白。
reqTime14 字节请求时间,固定为 14 位数字格式 yyyyMMddHHmmss,参与签名。
sign64 字节按第 3 节规则生成;推荐传 32 位大写 MD5 值。

成功响应

{
  "code": 0,
  "message": "ok",
  "data": {
    "order_no": "SF20260709213000A1B2C3D4",
    "merchant_order_no": "TEST202607090001",
    "amount": 10000,
    "status": 1,
    "pay_url": "https://pay-provider.example/pay?token=xxx"
  },
  "request_id": "..."
}

6. 订单状态通知

订单变为交易成功(status=1)后,平台会向商户下单时传入的 notifyUrl 发送 UTF-8 POST Form 通知;如果成功订单之后被交易冲正,平台会再发送 status=3 更正通知。Content-Type 为 application/x-www-form-urlencoded。商户收到每次通知后都必须按第 3 节对解码后的字段验签、校验金额和订单归属,并按订单号幂等更新最终状态;处理成功后响应纯文本 success

字段说明
mchId商户号。
payOrderId平台订单号。
mchOrderNo商户订单号。
amount订单金额,单位为分。
status1 表示交易成功;3 表示该成功状态已被冲正,是必须处理的更正通知。状态含义见第 7 节。
param1下单时传入的自定义字段,可能为空。
param2下单时传入的自定义字段,可能为空。
reqTime通知时间,格式 yyyyMMddHHmmss
sign平台按商户密钥生成的签名。

通知示例

mchId=M10001
payOrderId=SF20260709213000A1B2C3D4
mchOrderNo=TEST202607090001
amount=10000
status=1
param1=merchant-extra-1
param2=
reqTime=20260709213120
sign=平台签名

同一订单发生冲正时,平台会再次发送上述字段,其中 status=3,并使用新的 reqTime 和对应签名。商户不得因为该订单曾处理过 status=1 而忽略这次更正。

商户处理要求

平台会分别记录当前目标状态的通知结果。无论 status=1 还是 status=3,商户未返回 success 时平台都会自动重试;重启或队列短暂故障后也会从数据库恢复,后台同时支持人工补发通知。

超时与自动重试

每次通知 HTTP 请求最长等待 10 秒。首次通知立即发起;只有收到 HTTP 2xx 且响应体去除首尾空白后不区分大小写等于 success 才视为成功。请固定返回小写纯文本 success。网络错误、超时、非 2xx 或其他响应体均会重试。

发送次数计划时间说明
1订单成功或冲正后立即当前状态的首次通知。
2第 1 次失败后约 5 秒自动重试。
3第 2 次失败后约 10 秒自动重试。
4第 3 次失败后约 20 秒自动重试。
5第 4 次失败后约 30 秒自动重试。
6第 5 次失败后约 60 秒自动重试。
7第 6 次失败后约 120 秒自动重试。
8第 7 次失败后约 180 秒自动重试。
9第 8 次失败后约 300 秒最后一次自动通知;仍失败后标记为失败,可由运营人工补发。

7. 状态与错误

订单状态

status含义商户处理建议
0待支付不要入账,等待通知或继续查单。
1交易成功验签、校验金额后入账。
2交易失败不要入账,可提示用户重新发起支付。
3交易冲正平台会发送可靠的更正通知。商户侧如已按状态 1 入账,验签并幂等校验后必须冲账或扣回,并将订单最终状态更新为冲正。

通用响应结构

{
  "code": 0,
  "message": "ok",
  "data": {},
  "request_id": "..."
}
HTTPcode常见含义
40040000参数错误、金额不合法、reqTime 超出允许范围。
40140100签名验证失败。
40340300请求 IP 不在白名单、商户停用、产品不可用或未绑定产品费率。
40440400订单不存在。
40940900商户订单号重复,但本次金额或产品与原订单不一致。
50250200请求尚未发送给上游时即发现转发配置不可用;该请求未形成可支付链接。
50350200当前金额没有可用通道。
50050000系统繁忙,请稍后重试。

8. 上线检查

商户侧必须完成
  • 商户号和密钥只保存在商户服务端,不放到任何公开页面、客户端代码或日志中。
  • 下单、查单都带 reqTimesign,并按解码后的 UTF-8 字段签名。
  • 金额按分传整数,不传元、不传小数。
  • 提交前校验字段 UTF-8 字节长度、控制字符和标识字段首尾空白。
  • 通知接口先验签,再校验金额,最后幂等入账。
  • 通知处理成功只返回 success
联调前确认
  • 商户调用服务器公网 IP 已提供给平台加入 API 白名单。
  • 平台提供的产品编码已绑定给该商户且状态启用。
  • 商户 notifyUrl 是公网可访问地址。
  • 商户服务器已放行平台提供的回调 IP。
  • 服务器时间已同步,和北京时间误差不超过 5 分钟。