首页 费率方案 接入文档 收银台演示 关于我们 申请接入
首页/接入文档

接入文档

标准 HTTPS + JSON 接口,签名算法为 HMAC-SHA256。本文档覆盖从获取密钥到上线收款的完整流程,示例代码可直接复制使用。

接口概览

OPENX 支付网关把不同支付方式的差异收敛为统一接口。业务系统只需要对接一次,通过 channel 参数选择具体支付方式,无需分别对接各家通道的 SDK、签名与回调。

接口方法路径说明
统一下单POST/api/v1/order/create创建支付订单,返回二维码或跳转参数
订单查询POST/api/v1/order/query按商户订单号或平台流水号查询
申请退款POST/api/v1/refund/create全额或部分退款,可多次
退款查询POST/api/v1/refund/query查询退款处理结果
关闭订单POST/api/v1/order/close关闭未支付的订单
对账单GET/api/v1/bill/daily按日拉取交易明细
所有接口均为 application/json,字符集 UTF-8。金额单位为分,128000 表示 1280.00 元。

环境与域名

生产与沙箱使用同一套接口定义,仅域名与密钥不同。沙箱环境的交易不产生真实资金流转,可放心用于联调。

环境接口域名用途
生产https://pay.openx.net.cn正式收款
沙箱https://pay.openx.net.cn/sandbox联调测试(开通后分配)
沙箱密钥与生产密钥完全隔离。请勿在生产环境使用沙箱 app_id,也请勿将密钥写入前端代码或提交到代码仓库。

密钥与鉴权

开通后你会拿到两个值:

  • app_id:应用标识,可公开出现在请求中。
  • app_secret:签名密钥,只能保存在服务端,用于计算与校验签名。

每个请求都需要携带 app_id、timestamp(10 位秒级时间戳)、nonce(随机字符串,32 位以内)与 sign。网关会校验时间戳偏差(±5 分钟)与 nonce 唯一性,防止重放。

签名规则

签名算法为 HMAC-SHA256,步骤如下:

  • 取所有非空请求参数(sign 本身除外),按参数名 ASCII 码升序排序;
  • 拼接为 key=value 形式,用 & 连接,末尾追加 &key=app_secret;
  • 对拼接串以 app_secret 为密钥做 HMAC-SHA256;
  • 输出大写十六进制字符串,即为 sign。
拼接时使用原始值,不做 URL 编码;布尔与数字按字符串处理;嵌套对象不参与签名(本接口均为扁平参数)。
sign.php
function ox_sign(array $params, string $secret): string {
    unset($params['sign']);
    $params = array_filter($params, fn($v) => $v !== '' && $v !== null);
    ksort($params, SORT_STRING);
    $pairs = [];
    foreach ($params as $k => $v) {
        $pairs[] = $k . '=' . $v;
    }
    $raw = implode('&', $pairs) . '&key=' . $secret;
    return strtoupper(hash_hmac('sha256', $raw, $secret));
}
sign.js
const crypto = require('crypto');

function oxSign(params, secret) {
  const raw = Object.keys(params)
    .filter(k => k !== 'sign' && params[k] !== '' && params[k] != null)
    .sort()
    .map(k => `${k}=${params[k]}`)
    .join('&') + `&key=${secret}`;

  return crypto.createHmac('sha256', secret)
    .update(raw, 'utf8')
    .digest('hex')
    .toUpperCase();
}
Sign.java
public static String sign(Map<String, String> params, String secret) throws Exception {
    params.remove("sign");
    String raw = params.entrySet().stream()
        .filter(e -> e.getValue() != null && !e.getValue().isEmpty())
        .sorted(Map.Entry.comparingByKey())
        .map(e -> e.getKey() + "=" + e.getValue())
        .collect(Collectors.joining("&")) + "&key=" + secret;

    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
    byte[] digest = mac.doFinal(raw.getBytes(StandardCharsets.UTF_8));
    return HexFormat.of().withUpperCase().formatHex(digest);
}
sign.py
import hmac, hashlib

def ox_sign(params: dict, secret: str) -> str:
    params = {k: v for k, v in params.items()
              if k != "sign" and v not in ("", None)}
    raw = "&".join(f"{k}={params[k]}" for k in sorted(params))
    raw += f"&key={secret}"
    return hmac.new(secret.encode(), raw.encode(),
                    hashlib.sha256).hexdigest().upper()

统一下单

POST https://pay.openx.net.cn/api/v1/order/create

请求参数

参数类型必填说明
app_idstring是应用标识
out_trade_nostring是商户订单号,32 位以内,全局唯一,作为幂等键
amountint是金额,单位分,最小 1
channelstring是支付方式,见下方取值
subjectstring是商品或服务标题,128 字以内
notify_urlstring是异步通知地址,必须为公网可访问的 HTTPS 地址
return_urlstring否同步跳转地址(H5 / 收银台场景)
client_ipstring否用户下单 IP,用于风控
attachstring否附加数据,回调时原样返回
expire_minutesint否订单有效期(分钟),默认 30,最大 120
timestampint是秒级时间戳
noncestring是随机串,32 位以内
signstring是签名,见「签名规则」

channel 取值

取值支付方式返回
wechat_native微信扫码qr_code 二维码链接
wechat_jsapi微信 JSAPI(公众号 / 小程序)pay_params 唤起参数
wechat_h5微信 H5pay_url 跳转地址
alipay_qr支付宝当面付qr_code 二维码链接
alipay_wap支付宝手机网站pay_url 跳转地址
unionpay_qr云闪付二维码qr_code 二维码链接
ecny_qr数字人民币qr_code 二维码链接
cashier聚合收银台pay_url 收银台页面地址

响应示例

200 OK
{
  "code": 0,
  "msg": "OK",
  "request_id": "req_8f2c19d4a7",
  "data": {
    "trade_no": "OX20261108A73F91",
    "out_trade_no": "ORDER20261108001",
    "amount": 128000,
    "channel": "wechat_native",
    "qr_code": "weixin://wxpay/bizpayurl?pr=***",
    "expire_at": 1762588800
  }
}

订单查询

POST https://pay.openx.net.cn/api/v1/order/query

传 out_trade_no 或 trade_no 之一即可。trade_status 取值:

状态含义建议处理
NOTPAY未支付继续等待或引导用户支付
PAYING支付中稍后再次查询,勿重复下单
SUCCESS支付成功发货 / 开通服务
CLOSED已关闭订单失效,需重新下单
REFUND已退款按退款结果处理

申请退款

POST https://pay.openx.net.cn/api/v1/refund/create

支持全额与部分退款。refund_amount 累计不得超过订单金额,同一订单可发起多次部分退款。退款为异步处理,最终结果以退款查询或回调为准。

参数类型必填说明
out_trade_nostring是原商户订单号
out_refund_nostring是商户退款单号,同一订单下唯一
refund_amountint是退款金额,单位分
reasonstring否退款原因,64 字以内

关闭订单

POST https://pay.openx.net.cn/api/v1/order/close

关闭未支付的订单,关闭后用户无法再支付。已支付订单请使用退款接口。

回调通知

支付成功后,网关向 notify_url 以 POST application/json 推送通知。你需要在 5 秒 内返回 HTTP 200 且响应体为 success,否则视为失败。

notify payload
{
  "app_id": "ox_live_8f2c****",
  "trade_no": "OX20261108A73F91",
  "out_trade_no": "ORDER20261108001",
  "trade_status": "SUCCESS",
  "amount": 128000,
  "channel": "wechat_native",
  "pay_time": "2026-11-08 14:32:07",
  "attach": "uid=10086",
  "timestamp": 1762589527,
  "nonce": "k3j5h2g4f1",
  "sign": "6B1E9A..."
}

处理回调的要点:

  • 先验签,再处理业务。用同一套签名规则校验 sign,验签失败直接返回失败,不要发货。
  • 做幂等。同一笔订单可能收到多次通知,请以 out_trade_no 为幂等键,已处理过的直接返回 success。
  • 以查询为准。若通知内容与本地记录不一致,主动调用订单查询接口复核。
  • 重试策略。失败后网关按 15s / 30s / 3min / 10min / 30min 重试共 5 次,仍失败则停止,需靠主动查询补偿。

对账单

GET https://pay.openx.net.cn/api/v1/bill/daily?date=2026-11-07

返回该日所有交易明细(含支付、退款、手续费、结算金额),支持分页。也可在商户后台下载 CSV,或配置每日邮件推送。

错误码

code说明处理建议
0成功—
1001参数缺失或格式错误检查必填字段与类型,金额单位为分
1002签名校验失败检查排序、空值过滤、密钥与大小写
1003时间戳过期校准服务器时间,偏差需在 ±5 分钟内
1004nonce 重复每次请求生成新的随机串
2001应用不存在或已停用确认 app_id 与环境是否匹配
2002通道未开通联系技术支持开通对应 channel
3001商户订单号重复复用原订单结果,勿重复下单
3002订单不存在检查 out_trade_no / trade_no
3003订单状态不允许该操作如对已支付订单调用关单
4001退款金额超限累计退款不得超过订单金额
5001上游通道异常建议按指数退避重试,仍失败联系技术支持
5002风控拦截查看后台风险提示,必要时提交材料申诉

所有响应都带 request_id,遇到 5001 等异常时请把它一并提供给技术支持,可快速定位上游报文。

对接常见问题

按出现频率排序:① 没有过滤空值参数;② 排序用了本地化排序而非 ASCII 升序;③ 拼接时多加了 & 或漏了 &key=;④ 输出未转大写;⑤ 误把 sign 本身也参与签名;⑥ 密钥末尾有空格或换行。
先确认 notify_url 是公网可访问的 HTTPS 地址,且未被防火墙、鉴权中间件拦截;再用订单查询接口确认交易状态。回调失败会自动重试 5 次,但生产环境建议额外加一个定时补偿任务。
不会。out_trade_no 是幂等键,同一订单号重复提交会返回同一笔订单,不会生成新交易。请确保你的订单号生成规则稳定且唯一。
可以。用 channel=cashier 下单后跳转到收银台地址即可,用户在收银台内自行选择支付方式。这种方式接入成本最低,适合快速上线或非技术团队。