跳到正文
接口文档 接入文档 更新于 2026-09-29
DEVELOPER GUIDE

接入指南

两类账户,分别接入。先完成签名与请求,再通过查询或通知确认订单结果。

基础地址https://dy.dyzfzx.com

开始前,准备这三项

  1. 对应账户的 PID 和密钥。商户与核销商的凭证不能混用,密钥只保存在自己的服务器。
  2. 自己的业务订单号。为每笔业务保存唯一订单号;超时、重试、查询和通知必须对应同一笔订单。
  3. 通知接收地址。准备可被平台访问的 HTTP/HTTPS 地址,完成验签、金额核对和幂等保存;核销上传可不传通知地址,改用查询确认。

四个接口,一张速查表

账户用途方法与路径接下来
商户创建订单POST /payment/orders使用响应顶层的 payurl 打开付款页
商户查询订单POST /payment/orders/querydata.status=1 才是已支付
核销商上传订单POST /supplier/orders上传成功仅表示已受理
核销商查询订单POST /supplier/orders/querydata.status=2 才是处理成功
请求约定:接口仅接受 POST。推荐 application/x-www-form-urlencoded,也支持 JSON 对象。UTF-8 编码,金额单位人民币元(CNY)。金额建议按字符串发送,如 100.00,按实际字符串签名。所有请求均由服务端发起。

这些字段不要混用

字段 / 场景正确含义
创建订单的 type=1/2商户支付编码:1 订单支付、2 登录充值;不是通道 ID 或归属
查询商户订单的 type=1/2订单号种类:1 平台订单号、2 商户订单号
核销商的 order_code=hx上传和查询都固定传 hx,不能填入商户 type
trade_no / out_trade_no平台生成的号码 / 接入方自己的业务订单号
notify_url / return_url服务器通知 / 用户浏览器跳转;不能根据浏览器跳转记账
HTTP 200 / code=200 / successHTTP 正常 / 接口调用成功 / 通知已处理;都不能独立证明订单成功
付款短链为 /dy/{32位标识},直接使用返回的 payurl,不要自行拼接 token。时间字段采用北京时间(UTC+8),格式 YYYY-MM-DD HH:mm:ss,原始字符串不含时区后缀。

签名规则

商户下单与核销上传接口使用同一套签名规则。账户号和密钥由平台管理端分配。

  1. 移除 sign、sign_type,并忽略值为空字符串的参数。数值 0 必须保留;不要传入数组、对象或 null 作为业务字段值。
  2. 按参数名 ASCII 升序排列,以 key=value 形式使用 & 连接。
  3. 在拼接结果末尾直接追加商户密钥,不添加 &key=。
  4. 对完整字符串计算 MD5,输出 32 位小写签名。
注意:签名必须使用实际发送的原始参数值,在 URL 编码前计算;接收 GET 通知时使用 URL 解码后的参数。不要改变金额字符串格式或重复解码。回调验签时也应先移除 sign 和 sign_type,再按同样规则计算。

创建订单的支付编码 type=1、type=2 和核销上传/查询的 order_code=hx 均按实际提交值参与签名,不转换成其他编码再签名。

先用这个固定样例校验

以下 PID 和密钥仅用于本地计算,不是真实账户,不能用于请求平台。应得到完全相同的 32 位签名。

{
  "pid": "20001",
  "order_code": "hx",
  "out_trade_no": "UPLOAD_20260927_000001"
}

演示密钥:DOC_DEMO_KEY_NOT_FOR_PRODUCTION

按键排序并在末尾追加密钥后的完整原文:

order_code=hx&out_trade_no=UPLOAD_20260927_000001&pid=20001DOC_DEMO_KEY_NOT_FOR_PRODUCTION

期望 MD5:

6788a699a88b86a10c9fce9ba2c35994
易错点:100 与 100.00 的签名不同;空字符串不参与签名,但 0 要保留。URL、中文和 & 先以原始值参与签名,再统一做表单编码。额外提交的非空字段也参与签名。

四语言签名函数

PHP / Python 的独立请求和完整流程示例已包含签名函数;Java / C# 此处为签名函数参考,需自行放入项目并引入对应标准库。

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);
}

错误码与重试

接口成功不等于订单成功:上传订单查询返回 code=200、msg=查询成功,只表示查到了订单;必须继续读取 data.status。订单失败时仍可返回 code=200,此时 data.status=3。
场景 / 字段含义
HTTP 200HTTP 请求正常返回,不能据此判定订单结果
/supplier/orders/query 顶层 code200:接口调用成功;201:接口调用失败
/supplier/orders、/supplier/orders/query 的 data.status0:待处理;1:处理中;2:处理成功;3:处理失败
上传订单 GET 回调参数 status2:处理成功;3:处理失败
回调接收方回复 success已接收并处理这次通知;成功通知和失败通知都需要确认
支付商户 /payment/orders/query 的 status1 表示已支付。此接口与上传订单的状态定义不同,不能共用状态映射

JSON 接口的接口调用失败(如验签失败、参数错误、订单不存在)使用 code=201,并增加稳定的 error_code 和 retryable。请依据错误码处理,不要依赖中文提示文本。

{
  "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_CODE_RETIRED请求使用了已替换的旧支付编码否改用 1 或 2,并按新编码重新签名
PAYMENT_CODE_CONFLICT数字编码与现有通道配置冲突否联系管理员核对配置,不自动切换通道
PAYMENT_PERMISSION_DENIED商户未授权该支付编码否联系平台配置收款权限
AMOUNT_INVALID金额格式或额度规则不符否按支付编码金额规则修正请求
BALANCE_INSUFFICIENT商户余额不足否补充余额后再提交
NOTIFY_URL_INVALID异步回调地址缺失或无效否提交有效的 HTTP/HTTPS 地址
RETURN_URL_INVALID同步跳转地址缺失或无效否提交有效的 HTTP/HTTPS 地址
DUPLICATE_ORDER外部或商户订单号重复否先查询原订单,不要生成重复业务单
AMOUNT_INVENTORY_EMPTY暂时没有匹配金额的可用订单是稍后使用原业务订单号重试
CHANNEL_UNAVAILABLE当前没有可用支付通道是建议 5 秒后重试
CHANNEL_BUSY支付通道暂时繁忙是建议 5 至 10 秒后重试
ORDER_NOT_FOUND未找到当前账户的订单否检查订单号类型及账户号
ORDER_NO_TYPE_INVALID商户查询的订单号类型不正确否type=1 查平台号;type=2 查自己的业务订单号
VALID_MINUTES_INVALID核销上传有效期不是 3–1440 的整数否修正 valid_minutes 后重新签名;不修改有效期业务范围
ORDER_NAME_REQUIRED商户下单缺少订单名称否补充 name 后重新签名
ORDER_NAME_REJECTED名称不符合平台规则否检查等号及禁用词
PAYMENT_ROUTE_DISABLED商户支付路由停用否联系平台核对授权及路由状态
VERIFICATION_TASK_UNAVAILABLE商户暂无可用核销任务是先核对原单,再退避重试
TARGET_RATE_WAIT同目标任务正在排队是先查询原单,不频繁更换业务订单号
UPSTREAM_CREATE_INCIDENT上游创建服务异常是保存错误码,查询原单后退避重试
TARGET_LOOKUP_INCIDENT目标账号查询服务异常是查询原单后退避重试
UPSTREAM_RESULT_UNCERTAIN上游创建结果未确认是先查原单;不要把结果未知当作失败或另起一笔业务
UPSTREAM_RESULT_RECOVERY_WAIT上游订单仍在确认是等待后查询原订单
METHOD_NOT_ALLOWED请求方法不正确否改用 POST;HTTP 和 code 都为 405
SYSTEM_BUSY系统暂时繁忙是退避重试并保留请求日志
安全重试:retryable=true 表示可在核对后重试,不表示应该立即重复提交。网络超时或未拿到明确响应时,应先调用查询接口确认订单是否已创建,再决定是否重试;不要直接更换业务订单号。
HTTP 状态:接口调用错误为兼容现有接入通常返回 HTTP 200 和 code=201;请求方法错误返回 HTTP 405。调用方应同时检查 HTTP 状态和响应体。

常见问题,按这个顺序排查

现象先检查下一步
签名失败是否混用了两类账户密钥;金额格式、空值、额外字段是否一致先跑固定签名样例,再对照自己的原始参数;日志不得记录密钥
请求超时或响应不是 JSON订单是否已被平台受理保留原业务订单号,先查询;不要更换订单号重新创建
DUPLICATE_ORDER此前同一订单号是否已提交查询已有订单,而不是重复创建
查询 code=200 但状态未成功读取的是商户状态还是核销状态按该角色的状态表处理;0/1 在两类业务里含义不同
没收到通知notify_url 是否公网可达;接收日志、HTTP 状态及响应正文先查询业务结果,再排查通知;通知失败不等于订单失败
到期后仍在处理中结果是否仍在确认保留原单并稍后查询,不以本地时间覆盖平台终态
提交排查信息时提供:接口路径、时间、PID、业务订单号、HTTP 状态、error_code 和脱敏请求。不要发送账户密钥或完整签名原文。

创建订单

创建支付订单,取得付款链接。

POST https://dy.dyzfzx.com/payment/orders

商户凭证 · 创建 → 打开 payurl → 等待通知或查询

请求参数

参数类型必填说明示例
pidInteger是商户号10001
typeString是1:订单支付;2:登录充值。以商户支付权限中展示的支付编码为准,不填写通道编码、通道 ID 或归属;其他支付模式使用后台分配的编码1
out_trade_noString是商户订单号;同一商户下必须唯一ORDER_20260927_000001
notify_urlString是服务器异步回调地址,必须为 HTTP/HTTPShttps://merchant.example/callback
return_urlString是支付完成后的页面跳转地址https://merchant.example/result
nameString是订单名称,不得包含等号或后台屏蔽词业务订单
moneyDecimal String是金额单位为元;建议字符串,如 100.00。精度、范围与可用面额受全局及支付编码额度规则限制100.00
sitenameString否商户站点或业务名称示例业务
signString是按统一签名规则生成32位小写MD5
sign_typeString否固定为 MD5MD5
结果确认:创建成功不代表支付成功。以验签后的通知或查询结果为准,不依据浏览器跳转发货。

成功返回字段

字段类型说明
codeInteger200 表示创建接口调用成功,不代表付款完成
msgString提示文本,仅用于展示
payurlString付款入口,位于响应顶层,不在 data 内;直接交给用户打开
trade_noString平台订单号;按原样保存,商户查询 type=1 使用此号
out_trade_noString请求中的商户订单号;商户查询 type=2 使用此号
typeString原始请求支付编码;额外字段 route_type 在路由编码不同时出现
moneyNumber / String订单金额,按精确金额核对,勿用浮点数直接比较
code_urlString二维码图片地址;二维码内容和付款方式以当前支付页为准
collection_mode / collection_mode_name / cashier_modeString收款模式、模式名称和收银展示模式;不要据此判定支付成功
payee_name / payee_accountString收款人及收款账户展示信息,非转账模式为空字符串

支付编码与时限

当前下单入口已停用被替换的旧支付编码。新订单的返回与通知使用对应的新编码;历史订单保留创建时的编码,请按实际返回参数验签,不要在接收端改写编码。

推荐使用表单格式提交参数;也支持 JSON 对象请求。响应中的 payurl 是付款入口,直接交给付款用户打开。示例均为虚构数据。cURL 用于展示报文;PHP / Python 示例会在你的服务器计算签名,运行前需配置真实凭证。

进阶:路由与支付时限
两级路由:调用方可以提交后台授权的二级支付编码,也可以提交已启用聚合下单的一级支付模式编码。提交一级编码时,系统先按一级额度规则和权重选择有权限的二级编码,再按该编码的码商/外部通道权重选择收款资源。
支付编码:type=1 为订单支付,先创建本地商户订单并返回付款地址,首次打开支付页后才准备上游支付信息;type=2 为登录充值,流程保持不变。按商户实际授权选择,不要把核销上传的 order_code=hx 当作支付编码。使用返回的 payurl 打开平台支付页。桌面端在本页显示付款二维码;移动端尝试打开支付页面,未自动跳转时可点击“去支付”。可用付款方式以实际支付页面为准。
前置订单时限:订单支付采用前置模式:创建商户订单后,付款用户须在 30 秒内首次打开平台支付页;未打开不拉起上游订单。打开后最多等待 60 秒完成匹配、复用或上游下单。准备时间单独计算,不扣除支付时间。符合复用条件时沿用有效支付会话,否则按原规则创建新的上游订单。取得可支付链接后,按当前支付流程提供 100 秒支付窗口,结束后继续查单 20 秒。已有官方订单、请求结果不明确或复用链接的场景,按现有查单确认、复用及占用保护规则处理;不能按固定总时长直接认定已释放核销订单。仍须有同金额、未到期且可分配的核销订单;返回付款入口不代表已完成支付,也不代表已取得上游支付信息。
不同有效期:上述为支付商户流程时限;核销商上传订单的 valid_minutes 是核销订单自身有效期,两者分别计算。

请求示例

curl -X POST 'https://dy.dyzfzx.com/payment/orders' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'pid=YOUR_MERCHANT_ID' \
  --data-urlencode 'type=1' \
  --data-urlencode 'out_trade_no=ORDER_20260927_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=SIGN_VALUE' \
  --data-urlencode 'sign_type=MD5'

下单成功响应

{
  "code": 200,
  "msg": "获取成功!",
  "trade_no": "P20260927000001",
  "type": "1",
  "out_trade_no": "ORDER_20260927_000001",
  "money": "100.00",
  "code_url": "LOCAL_QRCODE_URL",
  "payurl": "https://dy.dyzfzx.com/dy/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "collection_mode": "h5",
  "collection_mode_name": "H5支付",
  "cashier_mode": "h5"
}
编码字段:type 始终返回商户下单时提交的编码。仅当提交一级编码并发生聚合路由时返回 route_type,表示本次实际使用的二级支付编码;直接使用二级编码下单时不返回该字段。
JSON 响应:/payment/orders 返回原有商户响应结构,下单成功使用 code=200;失败使用 code=201,并返回 error_code、retryable 和 data=null。它不是 /mapi.php 的别名;后者仍保留原 code=1 响应。

查询订单

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

字段区别:本查询接口的 type=1/2 表示订单号种类,不是创建订单时的支付编码。请求参数按下表填写,返回的 data.type 才是订单的支付编码。
POST https://dy.dyzfzx.com/payment/orders/query
参数类型必填说明示例
pidInteger是商户号10001
order_noString是需要查询的订单号ORDER_20260927_000001
typeInteger是1 平台订单号;2 商户订单号2
signString是按统一签名规则生成32位小写MD5
sign_typeString否固定为 MD5MD5

标准请求

POST /payment/orders/query
Content-Type: application/x-www-form-urlencoded

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

返回字段

字段类型说明
code / msgInteger / String200 为查询接口成功;不能只看 msg 判定结果
data.idInteger / 数字字符串商户 ID,不是订单 ID
data.typeString创建时的支付编码,不是本次查询请求的 type
data.route_typeString,可选路由编码与请求编码不同时返回
data.trade_noString平台订单号
data.out_trade_noString商户业务订单号,务必与本地记录核对
data.nameString订单名称
data.moneyNumber / String订单金额,单位元;核对时按十进制定点金额比较
data.statusInteger0 未支付、1 已支付、2 支付超时、3 支付错误、4 订单风控
data.status_nameString状态中文说明,仅用于展示
data.failure_reasonString支付错误或订单风控时的原因,其他状态通常为空
只有 data.status=1 表示已支付。未知状态、网络异常、查不到订单,不等于已支付;通知和查询冲突时核对并告警,不直接覆盖已有结果。

成功响应

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

上传订单

授权账户可上传一笔待处理订单。当前订单编码固定为 hx,target_account 填写该笔业务实际需要处理的目标账号,不是核销商登录名、PID 或订单号;所有请求都必须显式传递 order_code=hx,并将该字段计入签名。

POST https://dy.dyzfzx.com/supplier/orders
参数类型必填说明示例
pidInteger是核销商账户号20001
order_codeString是订单编码,固定为 hx;该字段参与签名hx
out_trade_noString是外部订单号;同一账户下唯一,最长 100 字符且不能含空格UPLOAD_20260927_000001
target_accountString是实际业务目标账号,最长 64 字符,不能含空格或控制字符;不要填登录名/PID123456(虚构示例)
moneyInteger是订单金额,必须为大于 0 的整数100
valid_minutesInteger否订单有效期,范围 3–1440 分钟;不传时使用平台全局默认值,该字段传入后参与签名60
notify_urlString否HTTP/HTTPS 结果通知地址,最长 500 字符;不传则不通知,需主动查询https://uploader.example/callback
signString是使用核销商账户密钥签名32位小写MD5
sign_typeString否固定为 MD5MD5

受理成功后看什么

code=200 只代表受理成功。保存 data.trade_no 和 data.out_trade_no;data.status=0/1 等待,2 成功,3 失败。完整返回字段见下方与查询章节。
字段类型说明
code / msgInteger / String接口调用结果及提示文本
dataObject订单信息,与查询接口 data 的字段一致
data.trade_noString新核销订单为 J + 27 位数字;完整字符串保存,历史号码不改
data.statusInteger0 待处理、1 处理中、2 处理成功、3 处理失败
data.expires_atString订单有效期,不是付款倒计时
data.finished_atString完成时间,未完成为空字符串;通知字段名为 finish_time

请求示例

curl -X POST 'https://dy.dyzfzx.com/supplier/orders' \
  -H 'Content-Type: application/json' \
  -d '{
	    "pid": "YOUR_UPLOAD_ACCOUNT_ID",
	    "order_code": "hx",
	    "out_trade_no": "UPLOAD_20260927_000001",
	    "target_account": "123456",
	    "money": 100,
	    "valid_minutes": 60,
	    "notify_url": "https://uploader.example/callback",
    "sign": "SIGN_VALUE",
    "sign_type": "MD5"
  }'

成功响应

{
  "code": 200,
  "msg": "订单上传成功,等待处理",
	  "data": {
	    "order_code": "hx",
	    "trade_no": "J202609271000001234567890123",
    "out_trade_no": "UPLOAD_20260927_000001",
    "target_account": "123456",
    "money": "100.00",
    "status": 0,
    "status_name": "待处理",
    "failure_reason": "",
    "created_at": "2026-09-27 10:00:00",
	    "expires_at": "2026-09-27 11:00:00",
    "finished_at": ""
  }
}

失败响应

{
  "code": 201,
  "error_code": "DUPLICATE_ORDER",
  "msg": "外部订单号已存在,请勿重复上传",
  "retryable": false,
  "data": null
}
有效期:valid_minutes 可按订单传入 3–1440 分钟;不传时使用平台全局默认有效期(初始值 1440 分钟)。到期后由平台执行超时处理;支付中或结果尚未确认时,可能继续核对。不能仅凭本地倒计时将订单判为失败,以查询或验签通知的终态为准。修改全局默认值只影响之后新建的订单。

查询核销订单

查询当前上传账户自己的订单。逐笔查单、对账和更新本地订单时,必须使用上传时的外部订单号 out_trade_no,并核对响应里的 data.out_trade_no。

查询请求必须使用 order_code=hx。本版本新订单返回 hx;历史订单的返回与通知保留原订单编码,不回写历史记录。

同号多笔订单:同一目标账号下的订单,须分别使用各自的 out_trade_no 查询,并按该订单号处理返回结果。

此处 out_trade_no 是上传方提交的外部订单号;平台返回的任务号 trade_no 不是本接口的查询参数。

POST https://dy.dyzfzx.com/supplier/orders/query
参数类型必填说明
pidInteger是核销商账户号
order_codeString是订单编码,固定为 hx;该字段参与签名
out_trade_noString对账必填上传时提交的外部订单号;逐笔对账使用此字段
signString是使用核销商账户密钥签名
sign_typeString否固定为 MD5

标准请求

POST /supplier/orders/query
Content-Type: application/json

{
  "pid": "YOUR_UPLOAD_ACCOUNT_ID",
  "order_code": "hx",
  "out_trade_no": "UPLOAD_20260927_000001",
  "sign": "SIGN_VALUE",
  "sign_type": "MD5"
}
补充查询方式:按目标账号查询最近一笔

接口也允许仅传 target_account 而不传 out_trade_no,此时返回当前账户该目标账号最近一笔订单。两者都传时必须同时匹配。该方式不能用于逐笔对账,主流程始终传 out_trade_no。

返回字段(上传成功也使用同一 data 结构)

字段类型说明
code / msgInteger / String接口调用结果及提示;200 不代表订单处理成功
data.order_codeString新订单 hx;历史订单按实际值验签
data.trade_noString平台核销订单号;新号为 J + 27 位数字,不作为本接口查询参数
data.out_trade_noString上传时的外部订单号
data.target_accountString业务目标账号
data.moneyString金额,单位元、两位小数,如 100.00
data.statusInteger0 待处理、1 处理中、2 处理成功、3 处理失败
data.status_nameString中文状态名称,仅用于展示
data.failure_reasonString失败原因,仅 status=3 有值,其他状态为空字符串
data.created_atString创建时间,北京时间,YYYY-MM-DD HH:mm:ss
data.expires_atString订单有效期;到期不一定立即返回失败终态
data.finished_atString完成时间,未完成为空字符串;通知中的同义字段为 finish_time

data.status 状态说明

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

查询成功,且订单处理成功

{
  "code": 200,
  "msg": "查询成功",
	  "data": {
	    "order_code": "hx",
	    "trade_no": "J202609271000001234567890123",
    "out_trade_no": "UPLOAD_20260927_000001",
    "target_account": "123456",
    "money": "100.00",
    "status": 2,
    "status_name": "处理成功",
    "failure_reason": "",
    "created_at": "2026-09-27 10:00:00",
    "expires_at": "2026-09-27 10:30:00",
    "finished_at": "2026-09-27 10:06:18"
  }
}

查询成功,但订单处理失败

{
  "code": 200,
  "msg": "查询成功",
  "data": {
    "order_code": "hx",
    "trade_no": "J202609271000001234567890124",
    "out_trade_no": "UPLOAD_EXAMPLE_000002",
    "target_account": "123456",
    "money": "100.00",
    "status": 3,
    "status_name": "处理失败",
    "failure_reason": "订单已过期",
    "created_at": "2026-09-27 10:00:00",
    "expires_at": "2026-09-27 11:00:00",
    "finished_at": "2026-09-27 11:00:00"
  }
}

查询成功,订单仍在处理中

{
  "code": 200,
  "msg": "查询成功",
  "data": {
    "order_code": "hx",
    "trade_no": "J202609271000001234567890124",
    "out_trade_no": "UPLOAD_EXAMPLE_000002",
    "target_account": "123456",
    "money": "100.00",
    "status": 1,
    "status_name": "处理中",
    "failure_reason": "",
    "created_at": "2026-09-27 10:00:00",
    "expires_at": "2026-09-27 11:00:00",
    "finished_at": ""
  }
}

只有 data.status=2 才能将对应上传订单记为成功;0 和 1 应继续等待,3 记为失败并保存 failure_reason。接口调用失败、网络异常或未知状态不能当作订单成功,也不能直接当作订单处理失败。

核销结果通知

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

新订单通知使用 order_code=hx 并参与签名;历史订单沿用原编码。请直接对收到的原始字段验签,不要先替换编码。

GET 订单上传参数 notify_url
参数类型说明
pidInteger核销商账户号
order_codeString新订单为 hx;历史订单保留原编码
trade_noString平台任务订单号
out_trade_noString外部订单号
target_accountString订单目标账号
moneyDecimal订单金额
statusInteger2 处理成功;3 处理失败
status_nameString处理成功或处理失败
failure_reasonString失败原因,成功时为空
finish_timeString完成时间,北京时间 YYYY-MM-DD HH:mm:ss;查询接口字段名为 finished_at
signString使用核销商账户密钥生成的签名
sign_typeString固定为 MD5

处理成功通知示例(status=2)

GET /callback?pid=YOUR_UPLOAD_ACCOUNT_ID&order_code=hx&trade_no=J202609271000001234567890123&out_trade_no=UPLOAD_20260927_000001&target_account=123456&money=100.00&status=2&status_name=%E5%A4%84%E7%90%86%E6%88%90%E5%8A%9F&failure_reason=&finish_time=2026-09-27%2010%3A06%3A18&sign=SIGN_VALUE&sign_type=MD5

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

success

处理失败通知示例(status=3)

GET /callback?pid=YOUR_UPLOAD_ACCOUNT_ID&order_code=hx&trade_no=J202609271000001234567890124&out_trade_no=UPLOAD_EXAMPLE_000002&target_account=123456&money=100.00&status=3&status_name=%E5%A4%84%E7%90%86%E5%A4%B1%E8%B4%A5&failure_reason=%E8%AE%A2%E5%8D%95%E5%B7%B2%E8%BF%87%E6%9C%9F&finish_time=2026-09-27%2011%3A00%3A00&sign=SIGN_VALUE&sign_type=MD5

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

success
失败通知也回复 success:上例订单结果为失败,接收方在验证并保存失败结果后回复 success,仅确认通知已处理。不得据此将订单记为成功。示例中的 SIGN_VALUE 是占位符,实际签名按收到并 URL 解码后的全部非空业务参数计算。

处理要求

  1. 使用核销商账户密钥验签;按当前账户及 out_trade_no 定位本地订单,核对 pid、order_code=hx、目标账号、金额及已保存的平台任务号。不要只按目标账号更新订单。
  2. 依据 GET 参数 status 分支:2 更新为处理成功;3 更新为处理失败并记录原因。不要依据 status_name 文本或验签成功判断订单成功。
  3. 业务结果须先事务落库并完成幂等处理,再返回 HTTP 2xx,接入端规范响应正文为小写 success;相同通知重复到达时不能重复结算。
  4. 验签失败、订单信息不符、未知状态或本地保存失败时,不返回 success。如本地已有相冲突的终态,应按外部订单号核对并告警,不直接覆盖。
自动重试:结果回调首次立即发送;失败后依次间隔 5 秒、10 秒、20 秒、30 秒、60 秒重试,最多共 6 次。接收方是否成功接收回调不会改变平台内最终处理状态。

完整流程示例

按账户类型选择一份脚本。每份都包含签名、表单请求、响应检查,以及使用同一个业务订单号查询一次。PHP 8 需启用 cURL;Python 3 仅使用标准库。

运行前须知:脚本在你自己的服务器运行后会真实提交订单;本网页不会发送请求。先设置环境变量,不要把密钥写进网页、APP 或公开仓库。脚本不是完整记账系统,仍需实现通知接收、金额核对、事务与幂等。
环境变量商户脚本核销商脚本
MERCHANT_PID / MERCHANT_KEY商户 PID / 密钥不使用
SUPPLIER_PID / SUPPLIER_KEY不使用核销商 PID / 密钥
BUSINESS_ORDER_NO你已保存的唯一商户订单号你已保存的唯一外部订单号
NOTIFY_URL服务器支付通知地址服务器结果通知地址;示例选择传入
RETURN_URL用户返回页面地址不使用
TARGET_ACCOUNT不使用真实业务目标账号

示例金额为 100 元,核销有效期为 60 分钟。运行前按你的真实业务调整。每次重试保留 BUSINESS_ORDER_NO;超时后先使用查询接口,不要重新运行整段创建流程。

商户:创建订单 → 查询付款结果

<?php
// PHP 8 + cURL。请先在服务端配置本页列出的环境变量。
function requiredEnv(string $name): string {
    $value = getenv($name);
    if ($value === false || $value === '') throw new RuntimeException('缺少环境变量: ' . $name);
    return $value;
}
function makeSign(array $params, string $key): string {
    unset($params['sign'], $params['sign_type']);
    $params = array_filter($params, static fn($value) => $value !== '');
    ksort($params, SORT_STRING);
    $pairs = [];
    foreach ($params as $name => $value) $pairs[] = $name . '=' . $value;
    return md5(implode('&', $pairs) . $key);
}
function postForm(string $url, array $params, string $key): array {
    $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_CONNECTTIMEOUT => 5, CURLOPT_TIMEOUT => 15,
        CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2,
    ]);
    $body = curl_exec($ch);
    $http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $error = curl_error($ch);
    curl_close($ch);
    // 超时/非 JSON/HTTP 异常都不能证明下单失败;先按原业务订单号查询。
    if ($body === false) throw new RuntimeException('结果未确认,请先查单: ' . $error);
    if ($http < 200 || $http >= 300) throw new RuntimeException('HTTP ' . $http . ': ' . $body);
    $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
    if (!is_array($result) || (int)($result['code'] ?? 0) !== 200) {
        throw new RuntimeException(json_encode($result, JSON_UNESCAPED_UNICODE));
    }
    return $result;
}

$baseUrl = 'https://dy.dyzfzx.com';
$key = requiredEnv('MERCHANT_KEY');
$outTradeNo = requiredEnv('BUSINESS_ORDER_NO'); // 保存后重用,不随重试更换
$params = [
    'pid' => requiredEnv('MERCHANT_PID'),
    'type' => '1',
    'out_trade_no' => $outTradeNo,
    'notify_url' => requiredEnv('NOTIFY_URL'),
    'return_url' => requiredEnv('RETURN_URL'),
    'name' => '业务订单',
    'money' => '100.00',
];
$result = postForm($baseUrl . '/payment/orders', $params, $key);
echo '付款链接: ' . $result['payurl'] . PHP_EOL;
// 将 payurl 交给付款用户;此处不会自动打开或完成付款。
// 演示用同一个业务订单号查询一次;未完成时等待通知或稍后查询。
$query = postForm($baseUrl . '/payment/orders/query', [
    'pid' => $params['pid'],
    'type' => '2', 'order_no' => $outTradeNo,
], $key);
$order = $query['data'];
if ((string)$order['out_trade_no'] !== $outTradeNo) throw new RuntimeException('订单号不一致');
$status = (int)$order['status'];
$states = [0=>'未支付', 1=>'已支付', 2=>'支付超时', 3=>'支付错误', 4=>'订单风控'];
if (!isset($states[$status])) throw new RuntimeException('未知状态,请人工核对');
echo $states[$status] . PHP_EOL;
// 实际记账前还需核对账户、金额等字段,并在事务中幂等保存。

核销商:上传订单 → 查询处理结果

<?php
// PHP 8 + cURL。请先在服务端配置本页列出的环境变量。
function requiredEnv(string $name): string {
    $value = getenv($name);
    if ($value === false || $value === '') throw new RuntimeException('缺少环境变量: ' . $name);
    return $value;
}
function makeSign(array $params, string $key): string {
    unset($params['sign'], $params['sign_type']);
    $params = array_filter($params, static fn($value) => $value !== '');
    ksort($params, SORT_STRING);
    $pairs = [];
    foreach ($params as $name => $value) $pairs[] = $name . '=' . $value;
    return md5(implode('&', $pairs) . $key);
}
function postForm(string $url, array $params, string $key): array {
    $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_CONNECTTIMEOUT => 5, CURLOPT_TIMEOUT => 15,
        CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2,
    ]);
    $body = curl_exec($ch);
    $http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $error = curl_error($ch);
    curl_close($ch);
    // 超时/非 JSON/HTTP 异常都不能证明下单失败;先按原业务订单号查询。
    if ($body === false) throw new RuntimeException('结果未确认,请先查单: ' . $error);
    if ($http < 200 || $http >= 300) throw new RuntimeException('HTTP ' . $http . ': ' . $body);
    $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
    if (!is_array($result) || (int)($result['code'] ?? 0) !== 200) {
        throw new RuntimeException(json_encode($result, JSON_UNESCAPED_UNICODE));
    }
    return $result;
}

$baseUrl = 'https://dy.dyzfzx.com';
$key = requiredEnv('SUPPLIER_KEY');
$outTradeNo = requiredEnv('BUSINESS_ORDER_NO'); // 保存后重用,不随重试更换
$params = [
    'pid' => requiredEnv('SUPPLIER_PID'),
    'order_code' => 'hx',
    'out_trade_no' => $outTradeNo,
    'target_account' => requiredEnv('TARGET_ACCOUNT'),
    'money' => '100',
    'valid_minutes' => '60',
    'notify_url' => requiredEnv('NOTIFY_URL'),
];
$result = postForm($baseUrl . '/supplier/orders', $params, $key);
// 演示用同一个业务订单号查询一次;未完成时等待通知或稍后查询。
$query = postForm($baseUrl . '/supplier/orders/query', [
    'pid' => $params['pid'],
    'order_code' => 'hx', 'out_trade_no' => $outTradeNo,
], $key);
$order = $query['data'];
if ((string)$order['out_trade_no'] !== $outTradeNo) throw new RuntimeException('订单号不一致');
$status = (int)$order['status'];
$states = [0=>'待处理', 1=>'处理中', 2=>'处理成功', 3=>'处理失败'];
if (!isset($states[$status])) throw new RuntimeException('未知状态,请人工核对');
echo $states[$status] . PHP_EOL;
// 实际记账前还需核对账户、金额等字段,并在事务中幂等保存。

通知接收指南

平台向创建订单时提交的 notify_url 发送 GET 请求。它是接入方自己的服务器地址,不是平台新增的固定回调路径,也不是 JSON POST 通知。

  1. 验证来源使用对应账户密钥验签。
  2. 保存结果核对订单,事务落库且幂等。
  3. 确认接收返回 HTTP 2xx 和 success。

按业务分别判断结果

通知类型成功条件失败通知
商户支付通知trade_status=TRADE_SUCCESS该通知用于支付成功结果
核销结果通知status=2status=3,保存失败原因

接收方必须做到

  1. 对实际收到的全部非空业务参数验签,移除 sign、sign_type;新增的可选参数不能漏签。
  2. 按当前账户和业务订单号定位本地记录,核对金额、账号及该业务的相关字段,不能只凭验签成功就更新任意订单。
  3. 重复通知不得重复记账;终态冲突时先查询核对,不直接覆盖已保存的结果。
  4. 仅在验证和保存完成后确认接收。验签失败、未知状态或落库失败时,不返回 success。
重试:首次立即通知;失败后依次间隔 5、10、20、30、60 秒,最多共 6 次。通知失败不等于平台订单失败,请结合查询接口核对。

确认响应

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

success

实现会对正文去首尾空白并忽略大小写,但接入端请统一输出小写纯文本 success,不要依赖宽松兼容。

避免这些确认方式

{ "success": true }  // 不是纯文本
SUCCESS              // 请统一输出规范的小写 success
<html>success</html> // 不是纯文本

失败订单的通知也需要在保存后
回复 success;这不会把订单改成成功。

最小验签参考(PHP)

下例只做验签,不处理业务、不确认接收。复用签名章节或完整示例中的 makeSign 函数,密钥选用本回调对应的商户或核销商密钥。验签后还必须核对订单并完成事务保存。

$params = $_GET; // PHP 已完成一次 URL 解码,不要再次 urldecode
$received = $params['sign'] ?? '';
foreach ($params as $value) {
    if (!is_string($value)) throw new RuntimeException('非法参数类型');
}
if (!is_string($received) || !preg_match('/^[a-f0-9]{32}$/D', $received)
    || !hash_equals(makeSign($params, $key), $received)) {
    http_response_code(400);
    exit('invalid signature');
}
// 此后必须按账户和 out_trade_no 读本地订单,核对金额、目标及状态。
// 在事务内幂等保存;成功提交后才输出 success。
// 尚未实现业务保存时不要返回 success,此参考不能直接作为完整回调上线。