通用规则

本文档描述了支付接口的通用规则,包括接口调用规范、签名算法、业务字典等内容。

接口规则

请求方式

所有接口必须使用 POST 方法进行请求。

签名算法

所有请求必须包含签名参数,签名算法如下:

签名步骤

  1. 将所有请求参数(除sign字段外)按照参数名的ASCII码升序排列
  2. 使用URL键值对的格式(即key1=value1&key2=value2...)拼接成字符串
  3. 使用HMAC-SHA256算法对拼接后的字符串进行签名
  4. 签名密钥为商户的merchantSecret
  5. 将签名结果作为sign参数的值

签名示例(Java)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

// 准备请求参数
Map<String, Object> params = new HashMap<>();
params.put("merchantNo", "M1001");
params.put("payMethod", "ALI_WAP");
params.put("outTradeNo", "20231229001");
params.put("amount", 100);
params.put("goodsName", "测试商品");

// 商户密钥
String merchantSecret = "商户密钥";

// 1. 按参数名ASCII升序排序
TreeMap<String, Object> sortedParams = new TreeMap<>(params);

// 2. 拼接成字符串
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : sortedParams.entrySet()) {
    if (sb.length() > 0) {
        sb.append("&");
    }
    sb.append(entry.getKey()).append("=").append(entry.getValue());
}
String data = sb.toString();

// 3. HMAC-SHA256签名
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(merchantSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(secretKey);
byte[] signBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));

// 4. 转换为十六进制字符串
StringBuilder hex = new StringBuilder();
for (byte b : signBytes) {
    hex.append(String.format("%02x", b));
}

// 5. 将sign添加到请求参数中
params.put("sign", hex.toString());

签名验证

服务端会验证请求签名的正确性,签名验证失败的请求将被拒绝。

注意:签名密钥(merchantSecret)是商户的重要安全凭证,请妥善保管,不要泄露给第三方。

响应格式

所有接口统一返回JSON格式数据,响应结构如下:

{
    "code": 1,
    "msg": "成功",
    "data": {},
    "sign": "签名值"
}

响应参数说明

参数名 类型 必填 说明
code Integer 响应码,1表示成功,其他表示失败
msg String 响应消息
data Object 响应数据
sign String 响应签名,使用相同的签名算法生成

业务字典

支付方式

枚举值 说明
WX_JSAPI 微信内部浏览器支付
WX_NATIVE 微信扫码支付
WX_APP 微信APP支付
WX_H5 微信H5支付
WX_APPLET 微信小程序支付
ALI_APP 支付宝APP支付
ALI_WAP 支付宝WAP支付
ALI_PC 支付宝PC支付
ALI_JSAPI 支付宝JSAPI支付

订单状态

状态码 状态说明
0 已创建
1 支付中
2 支付成功
3 支付失败
10 部分退款
11 全部退款
99 已关闭

支付接口

支付接口提供了统一的支付下单、订单查询、支付结果通知等功能。

统一支付下单

接口说明

统一支付下单接口用于发起支付请求,支持多种支付方式(微信、支付宝等)。

请求地址

POST /api/pay/order

请求参数

参数名 类型 必填 说明
merchantNo String 商户号,格式:M1000001
payMethod String 支付方式,详见 业务字典
outTradeNo String 商户订单号,长度不超过32字符
amount Integer 支付金额,单位:分
goodsName String 商品名称,长度不超过128字符
sign String 请求签名
extraParams String 扩展参数,JSON格式
expireSeconds Integer 订单过期时间,单位:秒
goodsDesc String 商品描述,长度不超过128字符
notifyUrl String 支付结果通知地址,长度不超过256字符
returnUrl String 支付返回地址,长度不超过256字符
channelParams String 渠道参数,JSON字符串

渠道参数(channelParams)说明

参数名 类型 必填 说明
wxOpenId String 子商户微信openId,微信小程序和JSAPI时必传
wxPayerClientIp String 微信支付者客户端IP,微信小程序、JSAPI、APP、H5时必传

请求示例

{
    "merchantNo": "M1001",
    "payMethod": "ALI_WAP",
    "outTradeNo": "20231229001",
    "amount": 100,
    "goodsName": "测试商品",
    "sign": "生成的签名值",
    "notifyUrl": "https://example.com/notify",
    "channelParams": {
        "wxPayerClientIp": "127.0.0.1"
    }
}
参数说明:

响应参数

参数名 类型 说明
tradeNo String 平台交易流水号
payMethod String 支付方式
payData String 支付数据,根据不同支付方式返回不同内容

响应示例

{
    "code": 1,
    "msg": "成功",
    "data": {
        "tradeNo": "2023122900000001",
        "payMethod": "ALI_WAP",
        "payData": "支付宝支付数据"
    },
    "sign": "响应签名值"
}
注意:
  • 商户订单号(outTradeNo)在同一商户下必须唯一
  • 支付金额单位为分,请确保金额正确
  • 签名验证失败会直接返回错误

支付结果查询

接口说明

支付结果查询接口用于查询订单的支付状态和详细信息。商户可以通过商户订单号(outTradeNo)或平台交易流水号(tradeNo)查询订单状态。

请求地址

POST /api/pay/query

请求参数

参数名 类型 必填 说明
merchantNo String 商户号,格式:M1000001
outTradeNo String 商户订单号,与tradeNo至少填写一个
tradeNo String 平台交易流水号,与outTradeNo至少填写一个
sign String 请求签名
注意:
  • outTradeNotradeNo至少填写一个,如果同时填写,优先使用tradeNo
  • 商户订单号(outTradeNo)在同一商户下必须唯一

请求示例

{
    "merchantNo": "M1001",
    "outTradeNo": "20231229001",
    "sign": "生成的签名值"
}

响应参数

参数名 类型 说明
merchantNo String 商户号
outTradeNo String 商户订单号
payMethod String 支付方式,详见 业务字典
tradeNo String 平台交易流水号
amount Integer 支付金额,单位:分
goodsName String 商品名称
extraParams String 扩展参数
status Integer 订单状态,详见 业务字典

响应示例

{
    "code": 1,
    "msg": "成功",
    "data": {
        "merchantNo": "M1001",
        "outTradeNo": "20231229001",
        "payMethod": "ALI_WAP",
        "tradeNo": "2023122900000001",
        "amount": 100,
        "goodsName": "测试商品",
        "extraParams": "",
        "status": 2
    },
    "sign": "响应签名值"
}
参数说明:

使用场景

  • 用户支付完成后,主动查询订单状态
  • 未收到支付结果通知时,主动查询订单状态
  • 对账时查询订单详细信息
重要提示:
  • 建议商户在支付完成后主动查询订单状态,以确保订单状态准确
  • 查询接口不应频繁调用,建议间隔至少5秒
  • 如果订单不存在,接口会返回错误信息

支付结果通知

接口说明

支付结果通知接口用于将支付结果通知到商户指定的回调地址。系统会定时扫描需要通知的订单,并通过POST方式将支付结果发送到商户的notifyUrl。

通知机制

  • 系统每10秒扫描一次需要通知的订单
  • 每次通知间隔至少30秒
  • 最多通知6次
  • 通知成功后不再重复通知
  • 6次通知失败后,订单通知状态标记为失败

通知地址

商户在发起支付时通过notifyUrl参数指定通知地址。

通知方式

系统使用POST方式发送通知,Content-Type为application/json。

通知参数

参数名 类型 说明
merchantNo String 商户号
outTradeNo String 商户订单号
payMethod String 支付方式
tradeNo String 平台交易流水号
amount Integer 支付金额,单位:分
goodsName String 商品名称
extraParams String 扩展参数
status Integer 订单状态,详见 业务字典
sign String 通知签名

通知示例

{
    "merchantNo": "M1001",
    "outTradeNo": "20231229001",
    "payMethod": "ALI_WAP",
    "tradeNo": "2023122900000001",
    "amount": 100,
    "goodsName": "测试商品",
    "extraParams": "",
    "status": 2,
    "sign": "通知签名值"
}
参数说明:

响应要求

商户在接收到通知后,需要验证签名,并返回固定字符串表示处理成功。

成功响应

success

失败响应

返回除"success"以外的任何内容,系统将认为通知失败,并在30秒后重试。

处理流程

  1. 接收通知请求
  2. 验证通知签名(使用相同的签名算法)
  3. 根据outTradeNo查询本地订单
  4. 检查订单状态,避免重复处理
  5. 更新本地订单状态
  6. 返回"success"表示处理成功

签名验证示例(Java)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

// 接收到的通知数据
Map<String, Object> notifyData = new HashMap<>();
notifyData.put("merchantNo", "M1001");
notifyData.put("outTradeNo", "20231229001");
notifyData.put("payMethod", "ALI_WAP");
notifyData.put("tradeNo", "2023122900000001");
notifyData.put("amount", 100);
notifyData.put("goodsName", "测试商品");
notifyData.put("status", 2);
String receivedSign = notifyData.remove("sign");

// 商户密钥(Base64编码)
String merchantSecret = "商户密钥Base64字符串";

// 1. 按参数名ASCII升序排序
TreeMap<String, Object> sortedParams = new TreeMap<>(notifyData);

// 2. 拼接成字符串
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : sortedParams.entrySet()) {
    if (sb.length() > 0) {
        sb.append("&");
    }
    sb.append(entry.getKey()).append("=").append(entry.getValue());
}
String data = sb.toString();

// 3. HMAC-SHA256签名
byte[] secretBytes = Base64.getDecoder().decode(merchantSecret);
SecretKeySpec secretKey = new SecretKeySpec(secretBytes, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);
byte[] signBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
String calculatedSign = Base64.getEncoder().encodeToString(signBytes);

// 4. 验证签名
boolean isValid = calculatedSign.equals(receivedSign);

if (isValid) {
    // 签名验证成功,处理业务逻辑
    // ...
    return "success";
} else {
    // 签名验证失败
    return "签名验证失败";
}
重要提示:
  • 必须验证通知签名,防止伪造通知
  • 必须检查订单状态,避免重复处理
  • 必须返回"success"字符串,不要返回JSON或其他格式
  • 建议记录所有通知日志,便于问题排查
通知时机:
  • 订单支付成功后立即触发通知
  • 支付失败或关闭也会触发通知
  • 通知包含最终的订单状态