Developer Docs

支付 API 接入文档

从获取密钥到下单、查单、支付通知和退款,按完整流程完成接入。支持原生 JSON 网关与易支付兼容协议,地址自动使用当前站点域名。

1获取密钥

商户后台 → 接入中心,获取 AppKey 与 AppSecret。

2签名下单

使用 V2 签名后 POST /api/gateway/orders,跳转收银台。

3接收通知

验签并核对订单金额,持久化支付结果与发货任务后应答 SUCCESS。

接入地址与准备

用途 地址 / 配置
平台基础地址 {BASE}
商户登录 {BASE}/merchant/login
在线接口文档 {BASE}/docs
创建订单 POST {BASE}/api/gateway/orders
查询订单 POST {BASE}/api/gateway/orders/query
申请退款 POST {BASE}/api/gateway/refunds
支付收银台 使用下单响应中的 cashierUrl,不要自行拼接
异步回调地址 商户自行部署,例如 https://shop.example.com/payment/notify
支付完成跳转地址 商户自行部署,例如 https://shop.example.com/payment/return
  1. 开通商户账号,完成后台要求的账户验证。
  2. 进入商户后台「接入中心」,获取 AppKey 和 AppSecret。查看密钥需要再次验证商户登录密码。
  3. 确认支付方式、通道和本人子商户已经开通并开启收款;可用能力以实际授权为准。
  4. 在自己的服务器部署回调接口和结果页面。AppSecret 只保存在服务端配置中,不写入网页、小程序、公开代码或日志。

回调地址由接入方提供。 下单时把自己网站的通知接口填入 notifyUrl,平台支付成功后向该地址通知。它不是平台网关地址,也不是上游渠道通知平台的地址。

完整操作流程

  1. 商户网站创建本地订单,保存唯一商户订单号和应付金额。
  2. 商户服务端签名,调用平台下单接口。
  3. 检查响应 code,保存平台订单号 orderNo,引导用户进入返回的 cashierUrl。
  4. 用户完成支付,平台向 notifyUrl 发送异步通知。
  5. 商户验证签名、订单归属、订单号和金额,确认支付成功,持久化支付结果和发货任务。
  6. 成功处理后返回 HTTP 200 和纯文本 SUCCESS;重复通知不能重复发货。
  7. 用户回到 returnUrl 后,由商户服务端查单,再展示真实支付结果。
  8. 对漏通知或请求超时的订单主动查单补偿;售后退款按唯一商户退款编号申请并核对结果。

接口调用成功不等于付款成功。 code=0 只说明本次原生接口调用成功,订单付款结果需要检查 data.status。

请求与 V2 签名

原生下单、查询和退款统一使用 POST、UTF-8 JSON,请求头如下:

http
Content-Type: application/json
X-App-Key: 你的AppKey

原生接口金额均为整数「分」:1.00 元传 100,10.00 元传 1000。请求金额不要传小数、浮点元或数字字符串。时间字段通常为 RFC3339,时间戳使用 Unix 秒。

json
{"code": 0, "message": "ok", "data": {}}

非 0 时读取 message 并保留业务订单编号;不要只检查 HTTP 状态码。

V2 公共字段

新接入推荐统一使用 V2,退款必须使用 V2。以下字段要添加到每个接口的业务参数中:

字段 类型 要求
signType string 固定 HMAC-SHA256-V2
timestamp string 当前 Unix 秒级时间戳;允许过去 300 秒、未来 30 秒
nonce string 每次请求新生成的 16~64 位字母、数字、下划线或连字符
sign string 按下面规则计算的签名

同一商户跨接口也不能复用 nonce。请求重试时重新生成 timestamp、nonce 和 sign,业务订单号或退款编号按原业务保留。

签名计算步骤

  1. 排除 sign,其余字段都参与签名,包括空字符串、signType、timestamp、nonce。不要传 null、数组或嵌套对象;extra 先序列化为 JSON 字符串。
  2. 定义 frame(s) 为「s 的 UTF-8 字节长度的十进制文本 + 冒号 + s」。中文按字节计算,不按字符数。
  3. 原文以 fourpay-gateway-v2\n 开头,其中 \n 为一个真实换行字符。
  4. 依次追加 frame("POST")、frame(接口路径)、frame(AppKey)。接口路径不含域名、查询串或末尾多余斜杠。
  5. 参数按字段名 ASCII 升序排列,逐个追加 frame(字段名) + frame(字段值),整数转为无小数点的十进制文本。不再添加其他分隔符。
  6. 使用 AppSecret 原始 UTF-8 字节作为密钥计算 HMAC-SHA256,结果用无填充 Base64URL 编码,得到 sign。
text
原文 = "fourpay-gateway-v2\n"
     + frame("POST") + frame("/api/gateway/orders") + frame(AppKey)
     + 按字段名排序追加 frame(字段名) + frame(字段值)
sign = Base64URL无填充(HMAC-SHA256(AppSecret, 原文))

旧 MD5 兼容说明

原生下单、查单仍兼容旧 MD5:排除 sign 和空值,按字段名排序拼接 k=v&k=v,追加 &key=AppSecret,取 MD5 大写。参数名不能包含 & 或 =,参数值不能包含 &。复杂回调 URL 或 extra 请用 V2。旧 MD5 不能申请退款。平台原生回调仍用 MD5 验签,与请求使用 V2 无关。

创建支付订单

http
POST {BASE}/api/gateway/orders
业务字段 必填 类型 / 说明
merchantOrderNo 是 string,商户系统内唯一订单号
amount 是 int,正整数分,10.00 元传 1000
subject 是 string,商品标题
notifyUrl 是 string,商户接收通知的公网 HTTPS 地址
returnUrl 否 string,商户支付结果页面,仅作展示
payMethod 否 string,alipay / wxpay / qqpay 等,以实际开通为准;留空由收银台选择
channelCode 否 string,通常留空;wxpay / alipay / qqpay 按对应品牌轮询本商户可用子商户并覆盖 payMethod;留空或 0 按 payMethod 路由;其他编码指定通道
expireMinutes 否 int,1~1440 分钟,未传或超出范围按 30 分钟处理
extra 否 string,JSON 字符串,可传 openid / buyerId / payer 等支付账号信息,按通道要求提供
clientIp 否 string,真实付款用户 IP;未传时使用请求 IP,服务端代下单时可传真实客户端 IP

以上字段还需加上 V2 公共字段。下方 timestamp、nonce、sign 是说明占位值,实际调用请使用示例函数生成:

json
{
  "merchantOrderNo": "SHOP202609140001",
  "amount": 1000,
  "subject": "测试商品",
  "notifyUrl": "https://shop.example.com/payment/notify",
  "returnUrl": "https://shop.example.com/payment/return",
  "payMethod": "alipay",
  "expireMinutes": 30,
  "signType": "HMAC-SHA256-V2",
  "timestamp": "发送时的Unix秒级时间戳",
  "nonce": "每次请求新生成的随机字符串",
  "sign": "计算得到的签名"
}

成功响应示例(部分通道还会返回 payUrl、payType):

json
{
  "code": 0,
  "message": "ok",
  "data": {
    "orderNo": "Pxxxxxxxx",
    "merchantOrderNo": "SHOP202609140001",
    "cashierUrl": "{BASE}/pay/Pxxxxxxxx"
  }
}

保存 orderNo 与本地订单的对应关系,然后跳转返回的 cashierUrl。同一 merchantOrderNo 原单待支付时可返回原单;已终态会报订单号已存在。下单超时先用原订单号查单,不要直接换号重复创建。

查询支付订单

http
POST {BASE}/api/gateway/orders/query
业务字段 要求
orderNo 平台订单号,与 merchantOrderNo 二选一
merchantOrderNo 商户订单号,与 orderNo 二选一

请求体除下面的业务字段外,还需带 V2 公共字段,并携带 X-App-Key:

json
{"merchantOrderNo": "SHOP202609140001"}
json
{
  "code": 0,
  "message": "ok",
  "data": {
    "orderNo": "Pxxxxxxxx",
    "merchantOrderNo": "SHOP202609140001",
    "amount": 1000,
    "fee": 0,
    "actualAmount": 1000,
    "refundedAmount": 0,
    "status": "SUCCESS",
    "channelCode": "实际通道编码",
    "payMethod": "alipay",
    "createdAt": "2026-09-14T02:00:00Z",
    "paidAt": "2026-09-14T02:01:00Z"
  }
}

amount 是原订单金额,actualAmount 是商户实收金额,refundedAmount 是累计已退款金额,均为分。手续费不改变回调中用于核验的原订单金额。未付款时 paidAt 可能为 null。

原生查询对待支付订单会尝试向上游核实;是否更新取决于通道查询能力及响应。不要把待支付或查询失败解释为支付成功。

订单状态

状态 含义
PENDING 待支付
SUCCESS 支付成功
FAILED 支付失败
CLOSED 已关闭
REFUNDING 退款中
REFUNDED 已全额退款
PARTIAL_REFUND 部分退款

支付、退款结果均以实际返回的订单状态和金额核对,不能仅凭接口 code 判断。

异步回调与验签

回调地址由商户自行部署,通过下单字段 notifyUrl 传入,例如 https://shop.example.com/payment/notify。使用可公网访问的最终 HTTPS 地址,避免 301/302 跳转、登录校验或页面验证码阻断通知。

平台向该地址发送 POST JSON:

json
{
  "orderNo": "Pxxxxxxxx",
  "merchantOrderNo": "SHOP202609140001",
  "amount": "1000",
  "status": "SUCCESS",
  "channelCode": "实际通道编码",
  "payTime": "2026-09-14T02:01:00Z",
  "sign": "平台生成的MD5大写签名"
}

amount 为原订单金额(分的字符串);payTime 可能为空字符串。部分订单还带 payerOpenid、payerAppid。验签使用实际收到的全部字段,不能只选择示例里的字段。

回调签名规则(MD5 大写)

  1. 从收到的完整 JSON 中取出 sign;其余字段仅排除空字符串。
  2. 按字段名 ASCII 升序排列,拼接 k=v&k=v。
  3. 末尾追加 &key=你的AppSecret,计算 MD5 并转成 32 位大写十六进制。
  4. 使用常量时间比较函数与收到的 sign 比较。

下单用 V2,回调仍用 MD5。易支付兼容回调又是另一套格式,见对应章节。

商户处理顺序

  1. 验证请求格式和签名。
  2. 根据商户订单号查找本地订单,核对平台订单号、订单归属和金额。
  3. 仅在 status=SUCCESS 且检查全部通过后,更新订单并持久化发货或到账任务。
  4. 使用数据库事务或等价机制去重,确保同一订单只产生一次业务到账/发货任务。
  5. 持久化成功后应答;已成功处理的合法重复通知也应答成功。
http
HTTP/1.1 200 OK
Content-Type: text/plain

SUCCESS

正文直接返回 SUCCESS,不要返回 JSON、HTML、NOT SUCCESS 或带解释的句子。处理失败时不能先应答成功再丢弃业务。

启用持久化通知队列后,新支付成功订单每轮最多尝试 3 次,最多自动执行 5 轮,后续间隔依次为 1 分钟、5 分钟、30 分钟、2 小时。网络结果不确定、发送中断或结果保存失败时停止自动重试,需核实后人工补发;商户必须按订单号幂等处理。历史未加入队列的订单保留每次最多 3 次的原规则,不自动补发。未确认订单可主动查单核对;后台通知记录显示队列状态及下次执行时间。

同步跳转与上游回调

returnUrl:商户支付结果页

例如 https://shop.example.com/payment/return,由商户自行部署并在下单时传入。页面应调用自己的服务端查单后展示真实结果。用户访问此页面、页面参数或浏览器跳转都不能作为发货依据。

上游渠道通知平台

这类地址供配置微信、支付宝或其他支付插件使用,普通接入商户不需要填到自己的 notifyUrl。具体使用插件配置页或平台下单时生成的地址,不要自行猜测替换。

用途 路由说明
插件支付通知入口 {BASE}/api/plugin/notify/{pluginCode},pluginCode 使用实际插件编码
旧通道通知入口 {BASE}/api/gateway/notify/{channelCode},仅按对应旧通道配置使用
商户收款通知 商户自行提供的 notifyUrl,不能填写上述平台内部入口

申请退款与结果核对

http
POST {BASE}/api/gateway/refunds
业务字段 要求
orderNo / merchantOrderNo 二选一,目标支付订单
merchantRefundNo 必填,本次退款唯一编号,1~64 位字母、数字、下划线或连字符
refundAmount 必填,正整数分;支持部分退款,累计不能超过原单金额
reason 可选,退款原因

退款必须加上 V2 公共字段并携带 X-App-Key,旧 MD5 退款会被拒绝。业务参数示例:

json
{
  "merchantOrderNo": "SHOP202609140001",
  "merchantRefundNo": "REFUND202609140001",
  "refundAmount": 1000,
  "reason": "用户申请退款"
}

保存响应中的 refundNo 和 merchantRefundNo,核对订单状态及 refundedAmount。通道是否支持退款、是否即时完成,以实际能力和响应为准。

同一次退款超时、失败或结果未知时,保留原 merchantRefundNo、原订单、金额和原因,只更新 timestamp、nonce、sign。不能更换退款编号重试未知结果,更换编号代表一笔新的退款。

重复成功请求会返回原退款编号;处理中或未知结果需要继续核对,不能仅因接口返回就认定资金已退。当前公开网关没有单独的退款单查询端点,可通过订单查询核对累计已退款金额,异常退款到平台后台进一步核查。

易支付兼容接入

系统同时提供原生 JSON 网关和易支付兼容接口。两种协议的字段、金额单位、成功码和签名规则不能混用。

用途 地址
页面跳转下单 GET/POST {BASE}/submit.php,成功后跳转收银台
API 下单 GET/POST {BASE}/mapi.php,返回 JSON
查询订单 GET/POST {BASE}/api.php,act=order
平台 RSA 公钥 {BASE}/api/epay/platform-public-key

请求使用 Query 或 application/x-www-form-urlencoded 表单,不是 JSON。

配置项 / 下单字段 内容
网关地址 {BASE},配置基础地址时不要附带 /docs 或接口路径
pid 本平台商户号(也支持商户 ID),不是原生 X-App-Key
商户密钥 经典 MD5 模式使用本商户 AppSecret
type alipay / wxpay / qqpay 等已开通方式
out_trade_no 商户唯一订单号
name 商品标题
money 金额单位为元,例如 10.00,不是 1000 分
notify_url 商户自己的公网 HTTPS 通知地址
return_url 可选,商户支付结果页
sign_type / sign 签名类型与签名

经典 MD5 签名

排除 sign、sign_type 和空值,按字段名 ASCII 升序拼接 k=v&k=v,直接追加 AppSecret,计算 MD5 小写。不要混用原生接口的 &key= 规则。参数值不能包含 &;需要复杂 URL 或附加参数时改用原生 V2。

text
sign = MD5小写(排序后的参数原文 + AppSecret)
sign_type = MD5

RSA/RSA2 模式需先配置商户公钥,由商户私钥签请求;通知签名可能采用 RSA,应按实际 sign_type 使用平台公钥验签,不能把 RSA 通知当成 MD5。

下单、查询和通知

mapi.php 成功返回 code=1、trade_no、payurl,使用 payurl 跳转;失败返回 code=-1 和 msg。

查单传 pid、act=order,以及 trade_no / out_trade_no 二选一,并计算签名。act=order 也要参与签名,不支持只传 pid + key 查单。 当前 api.php 只支持 order,不支持退款等其他 act。status=1 表示已有支付时间,status=0 表示尚无支付时间;它不是原生接口完整的支付/退款状态枚举,也不执行原生查询的上游补单流程。

查单响应附带的签名只覆盖 trade_no、out_trade_no、type、pid、name、money、status,按此字段集合验签,不把 code、msg、addtime、endtime 加入响应验签原文。

易支付异步通知采用 GET,包含 pid、type、name、money、trade_no、out_trade_no、trade_status、sign、sign_type。验签并核对订单、元金额及 trade_status=TRADE_SUCCESS 后持久化处理,返回 HTTP 200 和纯文本 success。同步回跳仅作展示,不是发货依据。

易支付 Pro 插件安装

  1. 从本页插件目录下载适用版本,按包内说明安装。常见 Pro 目录为 plugins/payment/xypay;自定义发行版以实际插件目录为准。
  2. 在易支付后台刷新插件列表,启用插件并配置通道。
  3. 网关地址填 {BASE},原生 xypay 插件填写本商户 AppKey / AppSecret,通道编码通常留空。
  4. 启用对应支付方式和商户分组,确认易支付站点使用公网 HTTPS。
  5. 使用新订单联调下单、查单和通知。需要退款时确认插件版本已实现 V2 和 merchantRefundNo;旧插件不能仅靠改地址获得退款能力。

服务端示例代码

以下示例只负责请求签名和发送,不会在浏览器中运行。替换商户通知地址,并从服务端环境变量读取 FOURPAY_APP_KEY、FOURPAY_APP_SECRET。订单号由业务生成并持久化,重试时不要生成新订单号。退款示例仅展示调用方式,不会自动执行。

Python:下单、查询、退款共用客户端

python
import base64, hashlib, hmac, json, os, secrets, time, urllib.request

BASE = '{BASE}'
APP_KEY = os.environ['FOURPAY_APP_KEY']
SECRET = os.environ['FOURPAY_APP_SECRET']

def frame(value):
    data = str(value).encode('utf-8')
    return str(len(data)).encode('ascii') + b':' + data

def sign_v2(params, path):
    raw = b'fourpay-gateway-v2\n' + frame('POST') + frame(path) + frame(APP_KEY)
    for key in sorted(k for k in params if k != 'sign'):
        raw += frame(key) + frame(params[key])
    digest = hmac.new(SECRET.encode('utf-8'), raw, hashlib.sha256).digest()
    return base64.urlsafe_b64encode(digest).decode('ascii').rstrip('=')

def request(path, business):
    params = dict(business)
    params.update(signType='HMAC-SHA256-V2',
                  timestamp=str(int(time.time())), nonce=secrets.token_hex(16))
    params['sign'] = sign_v2(params, path)
    req = urllib.request.Request(BASE + path,
        data=json.dumps(params, ensure_ascii=False).encode('utf-8'),
        headers={'Content-Type': 'application/json', 'X-App-Key': APP_KEY},
        method='POST')
    with urllib.request.urlopen(req, timeout=30) as response:
        result = json.load(response)
    if result.get('code') != 0:
        raise RuntimeError(result.get('message', '网关请求失败;请核对订单状态'))
    return result['data']

# 以下调用放入你的服务端业务处理函数,不要反复执行固定示例订单。
# data = request('/api/gateway/orders', {
#     'merchantOrderNo': persisted_order_no, 'amount': 1000, 'subject': '测试商品',
#     'notifyUrl': 'https://shop.example.com/payment/notify', 'payMethod': 'alipay'})
# 保存 data['orderNo'],跳转 data['cashierUrl']。
# result = request('/api/gateway/orders/query', {'merchantOrderNo': persisted_order_no})
# refund = request('/api/gateway/refunds', {
#     'merchantOrderNo': persisted_order_no, 'merchantRefundNo': persisted_refund_no,
#     'refundAmount': 1000, 'reason': '用户申请退款'})
# 超时/异常时保留原业务编号,先核对状态,不能换退款编号重试。

PHP:通用 V2 请求函数(PHP 7.4+,cURL)

php
<?php
function fourpay_request(string $path, array $business): array {
    $base = '{BASE}';
    $appKey = getenv('FOURPAY_APP_KEY');
    $secret = getenv('FOURPAY_APP_SECRET');
    if (!$appKey || !$secret) throw new RuntimeException('请配置商户凭据');
    $p = $business;
    unset($p['sign']);
    $p['signType'] = 'HMAC-SHA256-V2';
    $p['timestamp'] = (string) time();
    $p['nonce'] = bin2hex(random_bytes(16));
    $frame = static function ($v): string {
        $s = (string) $v;
        return strlen($s) . ':' . $s; // PHP 字符串使用 UTF-8 字节
    };
    ksort($p, SORT_STRING);
    $raw = "fourpay-gateway-v2\n" . $frame('POST') . $frame($path) . $frame($appKey);
    foreach ($p as $k => $v) $raw .= $frame($k) . $frame($v);
    $p['sign'] = rtrim(strtr(base64_encode(hash_hmac('sha256', $raw, $secret, true)), '+/', '-_'), '=');
    $ch = curl_init($base . $path);
    curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => [
            'Content-Type: application/json', 'X-App-Key: ' . $appKey],
        CURLOPT_POSTFIELDS => json_encode($p, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)]);
    $body = curl_exec($ch);
    $error = curl_error($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($body === false || $status !== 200) throw new RuntimeException('请求结果未知,请查单:' . $error);
    $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
    if (($result['code'] ?? -1) !== 0) throw new RuntimeException($result['message'] ?? '请求失败');
    return $result['data'];
}
// fourpay_request('/api/gateway/orders/query', ['merchantOrderNo' => $persistedOrderNo]);
// 下单/退款替换 path 和业务参数;amount/refundAmount 使用 PHP 整数。

Node.js:通用 V2 请求函数(Node.js 18+)

javascript
const crypto = require('node:crypto');
const BASE = '{BASE}';
async function fourpayRequest(path, business) {
  const appKey = process.env.FOURPAY_APP_KEY, secret = process.env.FOURPAY_APP_SECRET;
  if (!appKey || !secret) throw new Error('请配置商户凭据');
  const p = { ...business, signType: 'HMAC-SHA256-V2',
    timestamp: String(Math.floor(Date.now() / 1000)), nonce: crypto.randomBytes(16).toString('hex') };
  delete p.sign;
  const frame = v => { const s = String(v); return Buffer.byteLength(s, 'utf8') + ':' + s; };
  let raw = 'fourpay-gateway-v2\n' + frame('POST') + frame(path) + frame(appKey);
  for (const k of Object.keys(p).sort()) raw += frame(k) + frame(p[k]);
  p.sign = crypto.createHmac('sha256', secret).update(raw, 'utf8').digest('base64url');
  const response = await fetch(BASE + path, { method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-App-Key': appKey },
    body: JSON.stringify(p), signal: AbortSignal.timeout(30000) });
  if (!response.ok) throw new Error('请求结果未知,请查单:HTTP ' + response.status);
  const result = await response.json();
  if (result.code !== 0) throw new Error(result.message || '网关请求失败');
  return result.data;
}
// await fourpayRequest('/api/gateway/orders/query', { merchantOrderNo: persistedOrderNo });
// 在 async 业务函数内调用。amount/refundAmount 使用安全范围内的整数,extra 使用 JSON.stringify。

原生回调验签示例(Python)

python
import hashlib, hmac

def verify_notify(payload, secret):
    if not isinstance(payload, dict):
        return False
    received = payload.get('sign')
    if not isinstance(received, str) or len(received) != 32 or not received.isascii():
        return False
    # 当前平台通知字段都是字符串,拒绝异常类型。
    if any(not isinstance(v, str) for v in payload.values()):
        return False
    items = sorted((k, v) for k, v in payload.items() if k != 'sign' and v != '')
    raw = '&'.join(k + '=' + v for k, v in items) + '&key=' + secret
    expected = hashlib.md5(raw.encode('utf-8')).hexdigest().upper()
    return hmac.compare_digest(expected, received)

# payload = 框架解析的请求JSON
# 验签通过后,必须查本地订单并核对 orderNo、merchantOrderNo、amount、status。
# 事务内去重,保存支付结果和可靠发货任务,提交成功后返回 HTTP 200 / SUCCESS。
# 本函数仅验签,不能替代业务金额核对、持久化和防重复发货。

Java / Go / C# / Ruby 接入要点

各语言按同一 V2 规则实现,重点核对 UTF-8 字节长度、HMAC 原始摘要和 Base64URL 无填充编码。发送 JSON 时金额必须保持整数类型;签名时才转为十进制文本。

语言 UTF-8 字节长度 HMAC-SHA256 / 编码
Java s.getBytes(StandardCharsets.UTF_8).length Mac.getInstance("HmacSHA256");Base64.getUrlEncoder().withoutPadding()
Go len(s),字符串内容须为 UTF-8 hmac.New(sha256.New, key);base64.RawURLEncoding
C# Encoding.UTF8.GetByteCount(s) HMACSHA256;Base64 后替换 + 为 -、/ 为 _ 并移除末尾 =
Ruby s.encode('UTF-8').bytesize OpenSSL::HMAC.digest('SHA256', secret, raw);Base64.urlsafe_encode64(..., padding: false)

联调验收与常见问题

先检查商户开通状态和请求签名,再用本人已开通的通道做小额联调。接入中心的自检不创建真实订单,也不证明支付、退款或回调已经可用。

  1. 下单成功,平台订单号保存正确,用户能够打开收银台。
  2. 实际付款后,回调验签、订单号及金额核对通过,本地订单只更新一次。
  3. 重复发送同一合法通知,不重复发货,仍返回 SUCCESS。
  4. 篡改金额、订单号或签名的通知不能触发发货。
  5. 用户先返回结果页、通知稍后到达时,页面能通过服务端查单展示结果。
  6. 下单超时后按原订单号核对;退款超时后保留同一退款编号,避免重复退款。
  7. 如需退款,核对部分/全额退款结果与累计退款金额。
现象 排查方式
商户鉴权失败 检查 X-App-Key、商户启用状态及账户验证;不要把商户号当作 AppKey
V2 签名失败 检查 POST/路径/AppKey 是否一致,是否按 UTF-8 字节计数,是否包含空值及公共字段,是否使用无填充 Base64URL
请求过期 / nonce 已使用 校准服务器时间;每次请求和跨接口调用生成新的 nonce
MD5 参数存在歧义 参数中包含 &,改用原生 V2;不要自行替换字符后仍按旧值发送
notifyUrl 无效 / 回调 301 使用可公网访问的最终 HTTPS 地址,检查证书、重定向、登录和站点防护规则
HTTP 200 仍显示通知失败 正文直接返回 SUCCESS;不返回 JSON、HTML 或附加说明
已付款却未发货 查看通知记录、商户处理日志并主动查单;排查签名、订单金额和事务去重
merchantOrderNo 已存在 按原订单号查询核对;仅新的业务订单使用新编号
无可用支付通道 检查通道启用、支付品牌、商户分组授权、本人子商户收款开关和额度
风控拦截 检查单笔/单日限额、付款 IP、支付账号限制和下单频率
退款要求 V2 升级调用方或插件,增加 merchantRefundNo 和 V2 公共字段
退款超时 / 状态未知 保留原退款编号核对订单与后台退款记录,不用新编号再次退款
Plugin Catalog

插件目录

为现有系统接入支付服务,选择对应插件,下载后按说明配置。

v1.0.2

易支付 pro

适用于 易支付 pro。下载后请参照包内说明安装配置。

Download

文档下载

📄平台 API 完整文档(Markdown)自动带入当前站点地址,包含完整流程、接口参数、V2 示例与回调说明→ 📦xypay-epay-pro-v1.0.2.zip易支付 pro 插件 · 6993 字节 · 2026-09-11 05:24→