MERCHANT API DOCUMENTATION

商户 API 对接文档

接口版本 v1 · HTTPS · UTF-8 · application/json
生产环境

1. 快速接入

商户 API 域名和管理后台域名不同。所有程序接口必须使用 https://weiyi9991weiyipay.xyz,不要使用管理后台域名。
对接文档https://weiyi9991weiyipay.xyz/merchant-api-docs.html
创建订单POST https://weiyi9991weiyipay.xyz/openapi/v1/orders
查询订单GET https://weiyi9991weiyipay.xyz/openapi/v1/orders/{system_no}
查询费率GET https://weiyi9991weiyipay.xyz/openapi/v1/rates
平台回调出口 IP148.66.16.74(请加入商户回调服务器白名单)
  1. 向平台运营获取商户号、商户密钥和已开通的产品编码。
  2. 校准商户服务器时间,生成 Unix 秒级时间戳和每次唯一的 Nonce。
  3. 按本文规则签名,先调用费率接口验证认证是否正确。
  4. 创建订单,保存平台返回的 system_no,将客户跳转至 payment_url。
  5. 实现并公网开放 notify_url,验签、幂等处理通知并返回约定成功应答。

2. 请求认证与签名

创建订单、查询订单和查询费率都必须携带签名请求头。登录账号和登录密码不参与 API 认证。

请求头必填说明
X-Merchant-No是平台分配的商户号,不是登录账号
X-Timestamp是Unix 秒级时间戳,与平台时间误差不得超过 300 秒
X-Nonce是至少 12 个字符;每次请求重新生成,10 分钟内不可重复
X-Signature是HMAC-SHA256 结果的小写十六进制字符串
Content-TypePOST 必填application/json; charset=UTF-8
X-Client-User-Agent建议付款客户真实浏览器 User-Agent;不参与签名

签名原文

METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + NONCE + "\n" + RAW_BODY

创建订单签名原文示例

POST
/openapi/v1/orders
1792236170
43531248c0494b9aa2950230849f326b
{"merchant_order_no":"T10001","product_code":"203","amount":"100.00","client_device":"pc","notify_url":"https://merchant.example/callback"}
signature = hex_lowercase(HMAC_SHA256(商户密钥, UTF8(签名原文)))
重要:先生成最终 JSON 字符串,用同一份原始字节同时签名和发送。签名后如果框架重新排序字段、增加空格或改变换行,签名就会失败。GET 请求的 RAW_BODY 为空字符串,但 Nonce 后面的换行仍必须存在。

3. 可直接参考的签名代码

Java 11+

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.HexFormat;
import java.util.UUID;

String merchantNo = "平台分配的商户号";
String secret = "平台分配的商户密钥";
String path = "/openapi/v1/orders";
String body = "{\"merchant_order_no\":\"T10001\",\"product_code\":\"203\",\"amount\":\"100.00\",\"client_device\":\"pc\",\"notify_url\":\"https://merchant.example/callback\"}";
String timestamp = String.valueOf(Instant.now().getEpochSecond());
String nonce = UUID.randomUUID().toString().replace("-", "");
String text = "POST\n" + path + "\n" + timestamp + "\n" + nonce + "\n" + body;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = HexFormat.of().formatHex(mac.doFinal(text.getBytes(StandardCharsets.UTF_8)));
HttpRequest req = HttpRequest.newBuilder(URI.create("https://weiyi9991weiyipay.xyz" + path))
 .header("Content-Type","application/json; charset=UTF-8")
 .header("X-Merchant-No",merchantNo).header("X-Timestamp",timestamp)
 .header("X-Nonce",nonce).header("X-Signature",signature)
 .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8)).build();
HttpResponse<String> res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());

Node.js

const crypto = require('crypto');
const path = '/openapi/v1/orders';
const body = JSON.stringify(order);
const timestamp = Math.floor(Date.now()/1000).toString();
const nonce = crypto.randomUUID().replaceAll('-','');
const text = `POST\n${path}\n${timestamp}\n${nonce}\n${body}`;
const signature = crypto.createHmac('sha256', secret)
 .update(text,'utf8').digest('hex');

PHP

$path = '/openapi/v1/orders';
$body = json_encode($order, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$text = "POST\n{$path}\n{$timestamp}\n{$nonce}\n{$body}";
$signature = hash_hmac('sha256', $text, $secret);

4. 创建订单

POST/openapi/v1/orders
字段类型必填说明
merchant_order_nostring是商户订单号,同一商户内唯一,最长 64 位
product_codestring是平台为该商户开通并启用的产品编码
amountstring/number是大于 0,建议两位小数字符串,如 100.00
client_devicestring建议ios、android 或 pc
notify_urlstring强烈建议本笔订单的公网异步通知地址,优先于后台默认回调地址

请求正文

{
  "merchant_order_no": "T202610170001",
  "product_code": "203",
  "amount": "100.00",
  "client_device": "android",
  "notify_url": "https://merchant.example.com/payment/callback"
}

成功响应(HTTP 200)

{
  "code": 0,
  "message": "success",
  "data": {
    "system_no": "WEIYI202610170001...",
    "merchant_order_no": "T202610170001",
    "status": "pending",
    "payment_url": "https://..."
  }
}

商户必须保存 system_no,并将付款客户跳转到 payment_url。HTTP 超时或连接中断时,先查单确认,不要直接换订单号重复下单。

5. 查询订单与费率

GET/openapi/v1/orders/{system_no}

签名 PATH 必须替换成真实系统单号路径;GET 请求体为空。

{"code":0,"message":"success","data":{"system_no":"WEIYI...","merchant_order_no":"T...","amount":"100.00","status":"paid","created_at":"2026-10-17T09:30:00+00:00"}}
状态说明商户处理
pending等待支付继续等待回调或定时查单
paid支付成功成功入账,必须幂等
failed订单失败停止支付流程
closed订单关闭停止支付流程
reversed已冲正撤销此前成功结果,必须幂等
GET/openapi/v1/rates

使用相同签名请求头,只返回该商户已开通且启用的产品。

{"code":0,"message":"success","data":[{"code":"203","name":"产品名称","fee_rate":"13.000"}]}

fee_rate 单位是百分比,13.000 表示 13%。

6. 支付结果与冲正异步回调

平台向订单 notify_url 发送 POST application/json。同一事件可能重试,商户必须以 event_id 幂等;同一订单可能先收到 paid,之后收到独立的 reversed。

请求头说明
X-Merchant-No商户号
X-Callback-Event事件 ID,与正文 event_id 相同
X-SignatureHMAC-SHA256(原始回调 JSON 字节, 商户密钥) 的小写十六进制结果

支付成功事件

{"event_id":"uuid","system_no":"WEIYI...","merchant_order_no":"T...","amount":"100.00","status":"paid","confirmation_source":"upstream_callback","paid_at":"2026-10-17T11:26:45+00:00"}

冲正事件

{"event_id":"另一个uuid","system_no":"WEIYI...","merchant_order_no":"T...","amount":"100.00","status":"reversed","confirmation_source":"reversal","paid_at":"2026-10-17T11:26:45+00:00"}
  1. 读取原始请求体字节,不要解析后重新序列化再验签。
  2. 用商户密钥计算 HMAC-SHA256,与 X-Signature 安全比较。
  3. 核对商户号、事件 ID、订单号、金额和状态。
  4. 以 event_id 建唯一约束;重复事件直接返回成功,不重复入账或扣账。
  5. paid 执行成功入账;reversed 撤销此前成功入账。
  6. 本地事务提交成功后再返回成功应答。

平台认可的成功应答

HTTP 200
success
HTTP 200
ok

也支持:

{"success":true}
{"code":0}
{"code":200}
只返回 HTTP 200 但正文为空,或返回未约定的 JSON,平台仍会判定回调失败并重试。不能把 reversed 当成未知状态拒绝,它是合法冲正事件。

7. HTTP 状态码与常见问题

HTTP原因处理
400JSON 缺字段、金额或参数格式错误检查请求体和字段类型
401商户号、时间戳或签名无效校时;确认 PATH 和原始 JSON 完全一致
403来源 IP 不在白名单向平台登记商户服务器固定出口 IP
404域名/路径错误,或订单、产品不存在确认使用商户 API 域名及正确编码
409Nonce/订单号重复、无可用通道或终端不匹配重新生成 Nonce;重复订单先查单;联系运营检查绑定
422必要请求头或参数结构缺失检查四个签名头及 JSON
429请求超过频率限制降低并发并做指数退避
502全部上游通道拉单失败保留响应并提供商户订单号给平台
503平台临时暂停进单或服务不可用指数退避重试,并先查原订单

上线前自检