商户支付接口对接文档
HTML 对接版

商户支付接口对接文档

用于平台商户创建订单、查询订单并接收支付结果通知。接口地址、商户号、密钥、已授权产品编码及通知出口 IP 均由平台单独提供,以商户后台“对接信息”为准。

数据格式UTF-8 JSON
签名算法HMAC-SHA256
调用来源必须命中商户 IP 白名单
产品编码平台单独提供,本文档不预置固定值

1. 对接准备

平台会通过安全渠道向商户提供以下信息:

信息用途
merchant_no商户唯一编号。
API Key签名密钥,只在开户或重置时展示一次。
接口基址下单、查单和通知补发接口的域名与前缀。
已授权产品编码下单必填。只允许使用平台为当前商户单独授权的编码,本文档不写入任何固定产品编码。
通知出口 IP商户防火墙辅助放行;不能代替通知验签。
调用前必须配置请求 IP 白名单。商户需向平台提供调用服务器的固定公网出口 IP。白名单为空或来源不匹配时,下单、查单、通知补发等商户接口统一拒绝,签名正确也不会放行。

所有请求必须使用 HTTPS。商户请求 JSON 采用严格字段校验:未在本文档列出的字段会返回 HTTP 400,请勿自行增加平台内部字段。

2. 请求签名

2.1 请求头

请求头必填说明
X-Merchant-No平台提供的商户号。
X-Timestamp当前 Unix 秒,允许与平台时间相差不超过 300 秒。
X-Nonce16~64 位字母、数字、下划线或短横线;同一商户 5 分钟内不得重复。
X-Signature规范串的 HMAC-SHA256 小写十六进制结果。

2.2 规范串

HTTP_METHOD
REQUEST_PATH_AND_QUERY
X-Timestamp
X-Nonce
SHA256_HEX(RAW_BODY)
  • 每部分使用单个换行符 \n 分隔,末尾不追加换行。
  • 第二行只使用路径和查询串,不包含协议、域名和端口。
  • GET 请求的原始请求体为空字节串。
  • POST 必须对实际发送的原始 UTF-8 JSON 字节计算摘要,不能签名后重新序列化 JSON。
  • HMAC 密钥是 SHA-256(api_key) 的 32 字节结果,不是该结果的十六进制文本。

2.3 Java 签名示例

byte[] bodyBytes = json.getBytes(StandardCharsets.UTF_8);
String bodyHash = hex(MessageDigest.getInstance("SHA-256").digest(bodyBytes));
String canonical = method + "\n" + pathAndQuery + "\n" + timestamp
        + "\n" + nonce + "\n" + bodyHash;

byte[] signingKey = MessageDigest.getInstance("SHA-256")
        .digest(apiKey.getBytes(StandardCharsets.UTF_8));
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(signingKey, "HmacSHA256"));
String signature = hex(mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8)));

2.4 密钥重置

重置后平台返回新密钥及旧密钥验签截止时间。截止时间前,商户调用平台接口可使用当前密钥或紧邻的上一把密钥;平台发给商户的新通知会从重置完成时起立即使用新密钥签名。商户应在过渡期内同时保留两把密钥用于通知验签,完成切换后删除旧密钥。

连续重置只保留“当前密钥+紧邻的上一把密钥”。如果怀疑密钥泄露,应先停用商户并联系平台处理,不要依赖过渡期继续放行已泄露密钥。

3. 商户下单

POST /api/v1/gateway/orders

请求头另加 Content-Type: application/json; charset=UTF-8,请求体最大 1MB。

字段必填限制说明
merchant_order_no1~64 位;字母、数字、_-商户订单号,在当前商户下永久唯一。
product_code2~64 位;大写字母、数字、_-使用平台单独提供且已授权给当前商户的产品编码。
total_amount金额字符串,最多 2 位小数人民币元,范围 0.01100000000.00
notify_url最多 1000 字符本订单通知地址;必须是可解析的公网 HTTPS 443 地址,不允许内网、本机、用户信息或片段。

下单请求示例

{
  "merchant_order_no": "M202608040001",
  "product_code": "<平台单独提供的产品编码>",
  "total_amount": "10.00",
  "notify_url": "https://merchant.example.com/payment/notify"
}
产品编码决定完整支付流程。平台自动决定产品类型、支付宝订单标题、订单超时关闭时间、付款授权流程、产品主体和收款路由。商户只提交表格中的业务订单字段,无需选择支付方式或提供付款人信息;订单超时关闭由平台统一处理。产品不存在、已停用、未授权给当前商户或当前无可收款路由时,下单会被拒绝。

当前支持的产品流程

产品流程平台内部行为
普通手机网站调用 alipay.trade.wap.pay,不发送直付通二级商户或结算字段。
普通 JSAPI平台取得付款人支付宝 UID 后调用 alipay.trade.create,不发送直付通字段。
直付通手机网站平台路由已审核二级商户,再调用 alipay.trade.wap.pay 并携带直付通结算字段。
直付通 JSAPI平台完成付款人授权和二级商户路由后调用 alipay.trade.create 并携带直付通结算字段。

具体产品流程绑定在平台提供的 product_code 上,商户不能通过请求字段临时切换。

3.1 幂等规则

  • 首次创建成功返回 HTTP 201;完全相同的请求重试返回原订单和 HTTP 200。
  • merchant_order_no 相同但金额、产品编码或通知地址不同,返回 HTTP 409。
  • 请求超时、网络中断、502 或 503 时禁止换新订单号重下。应先查原单,或使用相同订单号和完全相同的请求重试。

3.2 响应字段

字段说明
merchant_order_no商户订单号。
platform_trade_no平台订单号。
alipay_trade_no支付宝交易号;尚未生成时为空。
trade_status当前交易状态。
total_amount订单金额。
product_code本订单创建时使用的产品编码快照。
cashier_url / pay_url平台收银台地址;两个字段值相同。
notify_status通知状态,与交易状态相互独立。
idempotent_replay本次下单是否返回已存在的同一订单。

4. 商户查单

GET /api/v1/gateway/orders/{merchant_order_no}

按第 2 节对空请求体签名。只能查询当前鉴权商户自己的订单。已经生成支付宝交易号的非终态订单会主动查询支付宝并更新状态;付款授权尚未完成时返回平台当前状态;已确认成功、完成或关闭的终态订单直接返回本地结果。

{
  "merchant_order_no": "M202608040001",
  "platform_trade_no": "P...",
  "alipay_trade_no": "2026...",
  "trade_status": "TRADE_SUCCESS",
  "total_amount": "10.00",
  "product_code": "<本订单创建时使用的产品编码>",
  "cashier_url": "https://pay.example.com/pay?...",
  "pay_url": "https://pay.example.com/pay?...",
  "notify_status": "SUCCESS",
  "idempotent_replay": false
}

商户应定时查单兜底,不能把“尚未收到通知”当作“订单未支付”。

5. 异步通知

平台向下单时保存的完整 notify_url 发起 POST JSON。请求头仍为第 2 节的四个签名头,但规范串第二行使用完整通知 URL(包括其查询串),请求体摘要使用平台实际发送的原始 JSON 字节。

支付宝通知验签及交易事实落库的同一事务提交后,平台立即唤醒首发投递;200ms 周期扫描仅作为进程恢复和信号丢失兜底。公网传输、DNS、TLS 和商户处理时间不由平台控制,因此协议不承诺物理意义上的 0 毫秒,但平台没有人为等待窗口。

字段说明
merchant_no平台商户号。
merchant_order_no商户订单号。
platform_trade_no平台订单号。
alipay_trade_no支付宝交易号,部分失败状态可能为空。
trade_status本次通知的交易状态。
total_amount订单金额。
notify_timeUTC ISO-8601 通知生成时间。
{
  "merchant_no": "M...",
  "merchant_order_no": "M202608040001",
  "platform_trade_no": "P...",
  "alipay_trade_no": "2026...",
  "trade_status": "TRADE_SUCCESS",
  "total_amount": "10.00",
  "notify_time": "2026-08-04T07:00:00Z"
}

5.1 接收与确认

  1. 先验签,再校验 merchant_no、商户订单号和金额。
  2. 使用数据库唯一键实现幂等入账,不能只依赖进程内缓存。
  3. 处理完成后返回 HTTP 2xx,正文只返回纯文本 success。大小写不敏感,首尾空白会被忽略;建议固定返回小写。
  4. 超时、网络错误、非 2xx、响应超过 4KB 或正文不是 success 均视为失败。

5.2 乱序与状态修正

  • 同一订单的不同交易状态分别通知,可能重复或乱序到达。建议以 merchant_order_no + trade_status 作为通知幂等键。
  • TRADE_FINISHED 可能是商户收到的第一条成功通知,必须与 TRADE_SUCCESS 一样触发幂等入账。
  • 极端并发下可能先收到 TRADE_TIMEOUT,随后收到支付宝确认的 TRADE_SUCCESS;最终必须以真实成功状态入账。

5.3 自动重试

通知类型总尝试次数失败后的重试间隔
TRADE_SUCCESS / TRADE_FINISHED首次立即+10 次重试,共 11 次5 秒、30 秒、2 分钟、10 分钟、1 小时,随后每 6 小时一次,共 5 次。
TRADE_CLOSED / TRADE_TIMEOUT首次立即+5 次重试,共 6 次5 秒、30 秒、2 分钟、10 分钟、1 小时。

平台可按商户关闭失败类订单通知;此时成功类通知不受影响,失败订单的 notify_statusNOT_REQUIRED,商户应使用查单接口兜底。

5.4 商户自助补发

POST /api/v1/gateway/orders/{merchant_order_no}/notify-retry

使用与查单相同的签名方式和空请求体,只允许当前商户自己的终态订单。接口会重新入队最近状态的通知,已在队列中的任务则如实返回当前状态。

返回字段说明
merchant_order_no当前商户提交的订单号。
platform_trade_no平台订单号。
status补发任务当前状态,常见为 PENDINGRETRYINGPROCESSING
{
  "merchant_order_no": "M202608040001",
  "platform_trade_no": "P...",
  "status": "RETRYING"
}

6. 状态说明

6.1 交易状态

状态含义商户处理
CREATING订单创建处理中。稍后查原单,不要换单号。
CREATE_UNKNOWN上游结果暂时未知。查原单或以完全相同参数重试。
CREATE_FAILED上游明确创建失败。修正原因后再按业务决定是否创建新订单。
WAIT_BUYER_PAY等待付款。继续展示有效收银台。
TRADE_SUCCESS支付成功。幂等入账。
TRADE_FINISHED交易完成。按成功状态处理并保持已入账。
TRADE_CLOSED支付宝交易已关闭。不入账。
TRADE_TIMEOUT平台已超时关闭订单且当时未确认成交。不入账;若后续收到真实成功修正,按成功入账。

6.2 通知状态

状态含义
NOT_REQUIRED当前无需通知,或该商户的失败类通知已关闭。
PENDING等待首次通知。
RETRYING此前未确认,正在等待自动或人工重试。
SUCCESS商户已按协议返回 success
FAILED自动重试已耗尽,仍可自助或由平台人工补发。

交易状态表示订单资金结果,通知状态只表示消息是否被商户确认;通知失败不会把成功交易改成失败。

7. 错误处理

签名、IP 白名单、限流、请求校验及业务处理产生的 JSON 错误均使用以下统一结构:

{
  "timestamp": "2026-08-04T07:00:00Z",
  "code": "INVALID_REQUEST",
  "message": "具体错误原因"
}
HTTP常见代码处理建议
400INVALID_REQUEST检查必填项、字段格式和是否传入未声明字段。
401UNAUTHORIZED / INVALID_SIGNATURE / SIGNATURE_EXPIRED / REPLAY_REQUEST检查商户状态、密钥、服务器时间、原始请求体和 Nonce。
403MERCHANT_IP_NOT_ALLOWED白名单为空或实际公网出口 IP 未配置;自行伪造转发头无效。
404NOT_FOUND当前商户下不存在该订单。
409IDEMPOTENCY_CONFLICT / DATA_CONFLICT同一商户订单号的参数不一致,禁止覆盖原单。
413REQUEST_TOO_LARGE请求体超过 1MB。
415UNSUPPORTED_MEDIA_TYPEPOST 应使用 JSON Content-Type。
429RATE_LIMITEDRetry-After 等待,不要换订单号。
502ALIPAY_API_ERROR上游处理异常;先查原单,或以原订单号和完全相同参数重试。
503SERVICE_BUSYRetry-After: 1 短暂退避后查单或重试原请求。

8. 上线检查清单

  1. 已安全保存商户号和 API Key,并配置服务器时钟同步。
  2. 已向平台提交全部调用服务器的固定公网出口 IP,确认白名单生效。
  3. 使用平台单独提供且已授权给当前商户的产品编码,不在代码中猜测或共用他人编码。
  4. 下单超时或非 2xx 时先查原单,不生成新商户订单号盲目重试。
  5. 通知入口完成验签、金额核对、数据库幂等和纯文本 success 应答。
  6. 已验证重复通知、乱序通知、成功状态修正、自动重试和自助补发。
  7. 密钥重置期间可同时验证新旧密钥,并确认新通知立即使用新密钥。