接口文档 V5 统一下单 / 订单上传 / 代付
PRIVATE PAYMENT API

接口文档

本文档提供统一下单、订单上传和代付三套相互独立的接口。所有示例均使用脱敏占位数据,金额单位为人民币元,字符编码统一为 UTF-8,生产接入必须使用 HTTPS。

统一下单地址
https://m.jctapay.com/mapi.php
代付下单地址
https://m.jctapay.com/PayoutApi/create
签名方式
MD5
回调成功响应
success

签名规则

统一下单、订单上传与代付接口使用同一套签名规则。账户号和密钥由平台管理端分配。

  1. 移除 signsign_type,并忽略值为空字符串的参数。
  2. 按参数名 ASCII 升序排列,以 key=value 形式使用 & 连接。
  3. 在拼接结果末尾直接追加商户密钥,不添加 &key=
  4. 对完整字符串计算 MD5,输出 32 位小写签名。
注意:签名必须使用实际发送的原始参数值。回调验签时也应先移除 signsign_type,再按同样规则计算。

四语言签名示例

function makeSign(array $params, string $merchantKey): string
{
    unset($params['sign'], $params['sign_type']);
    $params = array_filter($params, static fn($value) => $value !== '');
    ksort($params);

    $pairs = [];
    foreach ($params as $key => $value) {
        $pairs[] = $key . '=' . $value;
    }
    return md5(implode('&', $pairs) . $merchantKey);
}

错误码与重试

JSON 接口的业务失败统一保留 code=201,并增加稳定的 error_coderetryable。请依据错误码处理,不要依赖中文提示文本。

{
  "code": 201,
  "error_code": "AMOUNT_INVENTORY_EMPTY",
  "msg": "当前暂无匹配金额的可用订单,请稍后重试",
  "retryable": true,
  "data": null
}
error_code含义可重试处理建议
PID_REQUIRED账户号缺失补齐账户号后重新签名
SIGN_INVALID签名验证失败检查参数排序、空值处理和账户密钥
ACCOUNT_UNAVAILABLE账户不存在或已停用联系平台检查账户状态
ACCOUNT_ROLE_DENIED账户角色不能发起支付支付下单必须使用商户账户
UPLOAD_PERMISSION_DENIED账户无订单上传权限订单上传必须使用上传账户
ORDER_NO_REQUIRED商户或外部订单号缺失补齐订单号后重新签名
ORDER_CODE_INVALID订单上传编码无效使用平台指定的订单编码
TARGET_ACCOUNT_INVALID目标账号格式无效检查长度、空格和控制字符
PAYMENT_CODE_REQUIRED支付编码缺失提交后台授权的支付编码
PAYMENT_CODE_UNAVAILABLE支付编码无效或已停用检查编码与启用状态
PAYMENT_PERMISSION_DENIED商户未授权该支付编码联系平台配置收款权限
AMOUNT_INVALID金额格式或额度规则不符按支付编码金额规则修正请求
BALANCE_INSUFFICIENT商户余额不足补充余额后再提交
NOTIFY_URL_INVALID异步回调地址缺失或无效提交有效的 HTTP/HTTPS 地址
ACCOUNT_EXCLUSIVE_REQUIRED银行卡号与支付宝号未按二选一提交只保留一种收款账户参数后重新签名
BANK_CARD_INVALID银行卡号格式无效提交10至30位数字银行卡号
ALIPAY_ACCOUNT_INVALID支付宝号格式无效移除空格并检查长度
PAYEE_NAME_INVALID收款人姓名缺失或无效提交正确收款人姓名
RETURN_URL_INVALID同步跳转地址缺失或无效提交有效的 HTTP/HTTPS 地址
DUPLICATE_ORDER外部或商户订单号重复先查询原订单,不要生成重复业务单
AMOUNT_INVENTORY_EMPTY暂时没有匹配金额的可用订单稍后使用原业务订单号重试
CHANNEL_UNAVAILABLE当前没有可用支付通道建议 5 秒后重试
CHANNEL_BUSY支付通道暂时繁忙建议 5 至 10 秒后重试
ORDER_NOT_FOUND未找到当前账户的订单检查订单号类型及账户号
SYSTEM_BUSY系统暂时繁忙退避重试并保留请求日志
安全重试:只有 retryable=true 才建议自动重试。网络超时或未拿到明确响应时,应先调用查询接口确认订单是否已创建,再决定是否重试;不要直接更换业务订单号。
HTTP 状态:业务错误为兼容现有接入通常返回 HTTP 200 和 code=201;请求方法错误返回 HTTP 405。调用方应同时检查 HTTP 状态和响应体。

统一下单接口

创建支付订单并返回 JSON 结果。调用方必须使用表单格式向 /mapi.php 提交参数,成功后将响应中的 payurl 交给付款用户打开。

两级路由:调用方可以提交后台授权的二级支付编码,也可以提交已启用聚合下单的一级支付模式编码。提交一级编码时,系统先按一级额度规则和权重选择有权限的二级编码,再按该编码的码商/外部通道权重选择收款资源。
抖音支付:统一使用支付编码 dyzf。系统先打开平台支付页,再跳转到抖音 PC 综合收银台,由付款人选择支付宝或微信。支付入口有效期为3分钟;入口关闭后系统继续查询1分30秒,第4分30秒仍未确认付款才标记为支付超时并释放核销任务。
POST https://m.jctapay.com/mapi.php
参数类型必填说明示例
pidInteger商户号100***
typeString后台可用的一级支付模式编码或已授权的二级支付编码,支持纯数字、字母或组合PAY_MODE_OR_CODE
out_trade_noString商户订单号;同一商户下必须唯一ORDER_20260721_******
notify_urlString服务器异步回调地址,必须为 HTTP/HTTPShttps://merchant.example/callback
return_urlString支付完成后的页面跳转地址https://merchant.example/result
nameString订单名称,不得包含等号或后台屏蔽词业务订单
moneyDecimal订单金额;同时受全局额度和支付编码额度规则限制***.00
sitenameString商户站点或业务名称示例业务
signString按统一签名规则生成32位小写MD5
sign_typeString固定为 MD5MD5

请求示例

curl -X POST 'https://m.jctapay.com/mapi.php' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'pid=YOUR_MERCHANT_ID' \
  --data-urlencode 'type=YOUR_PAY_CODE' \
  --data-urlencode 'out_trade_no=ORDER_20260721_000001' \
  --data-urlencode 'notify_url=https://merchant.example/callback' \
  --data-urlencode 'return_url=https://merchant.example/result' \
  --data-urlencode 'name=业务订单' \
  --data-urlencode 'money=100.00' \
  --data-urlencode 'sign=请替换为计算后的签名' \
  --data-urlencode 'sign_type=MD5'

标准请求参数

pid=YOUR_MERCHANT_ID&type=YOUR_PAY_CODE&out_trade_no=ORDER_20260721_000001&notify_url=https%3A%2F%2Fmerchant.example%2Fcallback&return_url=https%3A%2F%2Fmerchant.example%2Fresult&name=%E4%B8%9A%E5%8A%A1%E8%AE%A2%E5%8D%95&money=100.00&sign=SIGN_VALUE&sign_type=MD5

下单成功响应

{
  "code": 1,
  "msg": "获取成功!",
  "trade_no": "PLATFORM_20260721_********",
  "type": "PAY_MODE_OR_CODE",
  "route_type": "ACTUAL_PAY_CODE",
  "qrcode": "PAYMENT_CONTENT",
  "payurl": "https://pay.example/checkout/******",
  "collection_mode": "h5",
  "collection_mode_name": "H5支付",
  "cashier_mode": "h5"
}
编码字段:type 始终返回商户下单时提交的编码。仅当提交一级编码并发生聚合路由时返回 route_type,表示本次实际使用的二级支付编码;直接使用二级编码下单时不返回该字段。
JSON 响应:/mapi.php 当前为兼容既有客户端,下单成功使用 code=1;失败使用 code=201,并返回 error_coderetryabledata=null

订单查询

按平台订单号或商户订单号查询当前商户自己的订单,其他商户的订单不会返回。

POST https://m.jctapay.com/Api/findorder
参数类型必填说明示例
pidInteger商户号100***
order_noString需要查询的订单号ORDER_20260721_******
typeInteger1 平台订单号;2 商户订单号2
signString按统一签名规则生成32位小写MD5
sign_typeString固定为 MD5MD5

标准请求

POST /Api/findorder
Content-Type: application/x-www-form-urlencoded

pid=YOUR_MERCHANT_ID&order_no=ORDER_20260721_000001&type=2&sign=SIGN_VALUE&sign_type=MD5

成功响应

{
  "code": 200,
  "msg": "获取成功!",
  "data": {
    "id": "******",
    "type": "PAY_CODE",
    "route_type": "ACTUAL_PAY_CODE",
    "trade_no": "PLATFORM_20260721_********",
    "out_trade_no": "ORDER_20260721_000001",
    "name": "业务订单",
    "money": "100.00",
    "status": 1,
    "status_name": "已支付",
    "failure_reason": ""
  }
}
编码字段:type 为原始请求编码;聚合下单时额外返回 route_type。老订单或直接二级编码下单不会返回 route_type

失败响应

{
  "code": 201,
  "error_code": "ORDER_NOT_FOUND",
  "msg": "未找到该商户订单",
  "retryable": false,
  "data": null
}
状态口径:0 未支付,1 已支付,2 支付超时,3 支付错误,4 订单风控。status_name 返回对应中文状态;支付错误或订单风控时,failure_reason 返回可公开的失败原因。

支付回调

订单支付成功后,平台以 GET 查询参数请求下单时提交的 notify_url。商户应先验签,再依据商户订单号幂等更新业务状态。

GET 下单参数 notify_url
参数类型说明
pidInteger商户号
trade_noString平台订单号
out_trade_noString商户订单号
typeString商户下单时提交的一级或二级编码
route_typeString聚合下单实际使用的二级支付编码;直接二级编码下单时不发送
nameString订单名称;后台启用隐藏名称时不发送
moneyDecimal订单金额
trade_statusString支付成功固定为 TRADE_SUCCESS
signString回调签名
sign_typeString固定为 MD5

标准回调示例

GET /callback?pid=YOUR_MERCHANT_ID&trade_no=PLATFORM_20260721_********&out_trade_no=ORDER_20260721_000001&type=PAY_MODE&route_type=ACTUAL_PAY_CODE&name=%E4%B8%9A%E5%8A%A1%E8%AE%A2%E5%8D%95&money=100.00&trade_status=TRADE_SUCCESS&sign=SIGN_VALUE&sign_type=MD5

HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8

success

处理要求

  1. 验签通过,并确认 pidout_trade_nomoney 与本地订单一致。
  2. 仅当 trade_status=TRADE_SUCCESS 时更新订单,重复通知必须幂等返回成功。
  3. 处理完成后返回 HTTP 2xx,响应正文必须且只能为小写 success
回调验签:聚合下单回调中的 route_type 参与签名。请对实际收到的全部非空业务字段排序验签,不要使用写死字段列表。
自动重试:首次立即通知;失败后依次间隔 5 秒、10 秒、20 秒、30 秒、60 秒重试,最多共 6 次。非 2xx、连接失败、超时或响应正文不是 success 均视为失败。

订单上传接口

授权账户可上传一笔待处理订单。当前订单编码固定为 dy,目标账号填写需要处理的抖音号;旧版请求未传订单编码时仍兼容,新接入必须显式传递。

POST https://m.jctapay.com/OrderApi/upload
参数类型必填说明示例
pidInteger订单上传账户号200***
order_codeString订单编码,固定为 dy;该字段参与签名dy
out_trade_noString外部订单号;同一账户下唯一,最长 100 字符且不能含空格UPLOAD_20260721_******
target_accountString需要处理的抖音号,最长 64 字符且不能含空格ACCOUNT_******
moneyInteger订单金额,必须为大于 0 的整数***
valid_minutesInteger订单有效期,范围 3–1440 分钟;不传时使用平台全局默认值,该字段传入后参与签名60
notify_urlString处理成功或失败的结果回调地址,最长 500 字符https://uploader.example/callback
signString使用订单上传账户密钥签名32位小写MD5
sign_typeString固定为 MD5MD5

请求示例

curl -X POST 'https://m.jctapay.com/OrderApi/upload' \
  -H 'Content-Type: application/json' \
  -d '{
	    "pid": "YOUR_UPLOAD_ACCOUNT_ID",
	    "order_code": "dy",
	    "out_trade_no": "UPLOAD_20260721_000001",
	    "target_account": "ACCOUNT_000001",
	    "money": 100,
	    "valid_minutes": 60,
	    "notify_url": "https://uploader.example/callback",
    "sign": "请替换为计算后的签名",
    "sign_type": "MD5"
  }'

成功响应

{
  "code": 200,
  "msg": "订单上传成功,等待处理",
	  "data": {
	    "order_code": "dy",
	    "trade_no": "TASK_20260721_********",
    "out_trade_no": "UPLOAD_20260721_000001",
    "target_account": "ACCOUNT_000001",
    "money": "100.00",
    "status": 0,
    "status_name": "待处理",
    "failure_reason": "",
    "created_at": "2026-07-21 10:00:00",
	    "expires_at": "2026-07-21 11:00:00",
    "finished_at": ""
  }
}

失败响应

{
  "code": 201,
  "error_code": "DUPLICATE_ORDER",
  "msg": "外部订单号已存在,请勿重复上传",
  "retryable": false,
  "data": null
}
有效期:valid_minutes 可按订单传入 3–1440 分钟;不传时使用平台全局默认有效期(初始值 1440 分钟)。到期仍未处理成功的订单会转为处理失败,并按 notify_url 通知上传方。修改全局默认值只影响之后新建的订单。

上传订单查询

查询当前上传账户自己的订单。可按外部订单号、目标账号或两者同时查询;同时提交时必须匹配同一笔订单。

POST https://m.jctapay.com/OrderApi/query
参数类型必填说明
pidInteger订单上传账户号
order_codeString订单编码,固定为 dy;该字段参与签名
out_trade_noString条件必填外部订单号;与目标账号至少填写一项
target_accountString条件必填目标账号;与外部订单号至少填写一项
signString使用订单上传账户密钥签名
sign_typeString固定为 MD5

标准请求

POST /OrderApi/query
Content-Type: application/json

{
  "pid": "YOUR_UPLOAD_ACCOUNT_ID",
  "order_code": "dy",
  "out_trade_no": "UPLOAD_20260721_000001",
  "sign": "SIGN_VALUE",
  "sign_type": "MD5"
}

返回字段

字段说明
order_code订单编码,固定为 dy
trade_no平台任务订单号
out_trade_no外部订单号
target_account订单目标账号
money订单金额
status处理状态码
status_name处理状态名称
failure_reason失败原因,仅失败状态有值
created_at创建时间
expires_at订单最终有效期
finished_at成功或失败完成时间

状态说明

0待处理
1处理中
2处理成功
3处理失败

标准成功响应

{
  "code": 200,
  "msg": "查询成功",
	  "data": {
	    "order_code": "dy",
	    "trade_no": "TASK_20260721_********",
    "out_trade_no": "UPLOAD_20260721_000001",
    "target_account": "ACCOUNT_000001",
    "money": "100.00",
    "status": 2,
    "status_name": "处理成功",
    "failure_reason": "",
    "created_at": "2026-07-21 10:00:00",
    "expires_at": "2026-07-21 10:30:00",
    "finished_at": "2026-07-21 10:06:18"
  }
}

处理结果回调

上传订单变为处理成功或处理失败时,平台以 GET 查询参数请求上传时提交的 notify_url。未传回调地址时不发送。

GET 订单上传参数 notify_url
参数类型说明
pidInteger订单上传账户号
order_codeString订单编码,固定为 dy
trade_noString平台任务订单号
out_trade_noString外部订单号
target_accountString订单目标账号
moneyDecimal订单金额
statusInteger2 处理成功;3 处理失败
status_nameString处理成功或处理失败
failure_reasonString失败原因,成功时为空
finish_timeString完成时间,格式 YYYY-MM-DD HH:mm:ss
signString使用订单上传账户密钥生成的签名
sign_typeString固定为 MD5

标准回调示例

GET /callback?pid=YOUR_UPLOAD_ACCOUNT_ID&order_code=dy&trade_no=TASK_20260721_********&out_trade_no=UPLOAD_20260721_000001&target_account=ACCOUNT_000001&money=100.00&status=2&status_name=%E5%A4%84%E7%90%86%E6%88%90%E5%8A%9F&failure_reason=&finish_time=2026-07-21%2010%3A06%3A18&sign=SIGN_VALUE&sign_type=MD5

HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8

success

处理要求

  1. 按统一规则验签,并核对外部订单号、目标账号和金额。
  2. status=2 更新为处理成功;status=3 更新为处理失败并记录原因。
  3. 处理完成后返回 HTTP 2xx,响应正文必须且只能为小写 success
自动重试:结果回调首次立即发送;失败后依次间隔 5 秒、10 秒、20 秒、30 秒、60 秒重试,最多共 6 次。接收方是否成功接收回调不会改变平台内最终处理状态。

代付下单接口

商户请求平台向指定银行卡或支付宝账户付款。代付使用现有商户号和商户密钥,但订单、状态和统计与收款订单完全独立。订单创建后先进入待发布状态,由平台发布到码商任务大厅;默认有效期为30分钟,过期未完成将按代付失败通知商户。

账户二选一:bank_cardalipay_account 必须且只能填写一个;payee_namemoneynotify_url 均为必填。不要在日志中记录完整收款账户或商户密钥。
POSThttps://m.jctapay.com/PayoutApi/create
参数类型必填说明示例
pidInteger现有商户号100***
out_trade_noString商户代付订单号,同一商户下唯一PAYOUT_20260729_000001
bank_cardString二选一银行卡号,允许输入空格或短横线,签名使用原始请求值6222********1234
alipay_accountString二选一支付宝账号user***@example.com
payee_nameString收款人姓名,最长100字符张*
moneyDecimal代付金额,最多两位小数100.00
notify_urlString代付结果异步通知地址https://merchant.example/payout-notify
signString按统一签名规则生成32位小写MD5
sign_typeString固定为MD5MD5

请求示例

curl -X POST 'https://m.jctapay.com/PayoutApi/create' \
  --data-urlencode 'pid=YOUR_MERCHANT_ID' \
  --data-urlencode 'out_trade_no=PAYOUT_20260729_000001' \
  --data-urlencode 'alipay_account=user@example.com' \
  --data-urlencode 'payee_name=张三' \
  --data-urlencode 'money=100.00' \
  --data-urlencode 'notify_url=https://merchant.example/payout-notify' \
  --data-urlencode 'sign=SIGN_VALUE' \
  --data-urlencode 'sign_type=MD5'

成功响应

{
  "code": 200,
  "msg": "代付订单提交成功,等待平台发布",
  "data": {
    "payout_no": "DF20260729************",
    "out_trade_no": "PAYOUT_20260729_000001",
    "account_type": "alipay",
    "account_masked": "use********mple.com",
    "payee_name": "张三",
    "money": "100.00",
    "status": 0,
    "status_name": "待处理",
    "failure_reason": "",
    "created_at": "2026-07-29 10:00:00",
    "expires_at": "2026-07-29 10:30:00",
    "finished_at": ""
  }
}
幂等:同一商户重复提交相同 out_trade_no 和相同业务内容时返回原订单;若金额、姓名或收款账户不同,则返回 DUPLICATE_ORDER

代付查询接口

使用平台代付订单号或商户代付订单号查询当前商户自己的代付订单,至少填写一个订单号。

POSThttps://m.jctapay.com/PayoutApi/query
参数必填说明
pid商户号
payout_no二选一平台代付订单号
out_trade_no二选一商户代付订单号
sign按统一签名规则生成
sign_type固定为MD5
POST /PayoutApi/query
Content-Type: application/x-www-form-urlencoded

pid=YOUR_MERCHANT_ID&out_trade_no=PAYOUT_20260729_000001&sign=SIGN_VALUE&sign_type=MD5
状态:0 待处理,1 处理中,2 代付成功,3 代付失败。失败时读取 failure_reason;调用方应按商户代付订单号幂等更新业务。

代付结果回调

码商完成付款并上传凭证后,平台通过 GET 请求创建订单时提交的 notify_url。平台也会对后台明确失败的订单发送失败通知。

GET代付下单参数 notify_url
参数说明
pid商户号
payout_no平台代付订单号
out_trade_no商户代付订单号
money代付金额
status2成功,3失败
status_name代付成功或代付失败
failure_reason失败原因;成功时为空且不参与签名
finish_time平台完成时间
sign回调签名
sign_type固定为MD5
GET /payout-notify?pid=YOUR_MERCHANT_ID&payout_no=DF20260729XXXXXXXXXXXX&out_trade_no=PAYOUT_20260729_000001&money=100.00&status=2&status_name=%E4%BB%A3%E4%BB%98%E6%88%90%E5%8A%9F&finish_time=2026-07-29%2010%3A10%3A00&sign=SIGN_VALUE&sign_type=MD5
成功响应:验签并幂等落库后,响应纯文本小写 success,不要返回JSON或HTML。
重试:首次立即发送,失败后依次等待5秒、10秒、20秒、30秒和60秒,最多尝试6次。管理端“代付回调”可对最终失败记录单独补发。

代付下单代码示例

以下示例使用支付宝账户。改用银行卡时,删除 alipay_account 并设置 bank_card。签名函数直接复用本文“签名规则”章节。

$params = [
    'pid' => 'YOUR_MERCHANT_ID',
    'out_trade_no' => 'PAYOUT_' . date('YmdHis'),
    'alipay_account' => 'user@example.com',
    'payee_name' => '张三',
    'money' => '100.00',
    'notify_url' => 'https://merchant.example/payout-notify',
];
$params['sign'] = makeSign($params, 'YOUR_MERCHANT_KEY');
$params['sign_type'] = 'MD5';
$ch = curl_init('https://m.jctapay.com/PayoutApi/create');
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $params, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15]);
$response = curl_exec($ch);

完整接入示例

以下四套示例包含统一签名、统一下单、支付订单查询、订单上传、上传订单查询和回调验签。示例账户、密钥、订单号、目标账号和地址均为脱敏占位值。

安全要求:商户密钥只能保存在服务器端,不得写入网页 JavaScript、APP 客户端或小程序。生产环境必须校验 HTTPS 证书。
<?php
$baseUrl = 'https://m.jctapay.com';
$merchantPid = 'YOUR_MERCHANT_ID';
$merchantKey = 'YOUR_MERCHANT_KEY';
$uploadPid = 'YOUR_UPLOAD_ACCOUNT_ID';
$uploadKey = 'YOUR_UPLOAD_ACCOUNT_KEY';

function makeSign(array $params, string $key): string {
    unset($params['sign'], $params['sign_type']);
    $params = array_filter($params, static fn($value) => $value !== '');
    ksort($params);
    $pairs = [];
    foreach ($params as $name => $value) $pairs[] = $name . '=' . $value;
    return md5(implode('&', $pairs) . $key);
}

function postForm(string $url, array $params, string $key): string {
    $params['sign'] = makeSign($params, $key);
    $params['sign_type'] = 'MD5';
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($params),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 15,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
    ]);
    $body = curl_exec($ch);
    if ($body === false) throw new RuntimeException(curl_error($ch));
    curl_close($ch);
    return $body;
}

// 1. 统一下单:服务端接入使用统一下单地址。
$payJson = postForm($baseUrl . '/mapi.php', [
    'pid' => $merchantPid, 'type' => 'YOUR_PAY_CODE',
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'notify_url' => 'https://merchant.example/callback',
    'return_url' => 'https://merchant.example/result',
    'name' => '业务订单', 'money' => '100.00',
], $merchantKey);

// 2. 商户订单查询。
$queryJson = postForm($baseUrl . '/Api/findorder', [
    'pid' => $merchantPid, 'order_no' => 'ORDER_20260721_000001', 'type' => '2',
], $merchantKey);
$query = json_decode($queryJson, true, 512, JSON_THROW_ON_ERROR);

// 3. 订单上传。
$uploadJson = postForm($baseUrl . '/OrderApi/upload', [
    'pid' => $uploadPid,
    'order_code' => 'dy',
    'out_trade_no' => 'UPLOAD_' . date('YmdHis'),
    'target_account' => 'ACCOUNT_000001', 'money' => '100',
    'notify_url' => 'https://uploader.example/callback',
], $uploadKey);

// 4. 上传订单查询。
$uploadQueryJson = postForm($baseUrl . '/OrderApi/query', [
    'pid' => $uploadPid,
    'order_code' => 'dy',
    'out_trade_no' => 'UPLOAD_20260721_000001',
], $uploadKey);

// 5. 回调验签:支付回调使用 merchantKey,上传订单回调使用 uploadKey。
function verifyCallback(array $query, string $key): bool {
    return isset($query['sign']) && hash_equals(makeSign($query, $key), $query['sign']);
}
if (verifyCallback($_GET, $merchantKey)) {
    // 校验订单号和金额,并幂等更新本地订单。
    echo 'success';
}