本文面向编写接入代码的开发者。已有商城的使用者只需按业务网站接入填写地址、PID、KEY,无需手算签名或编写下单代码。
在 MPAY 后台为业务网站创建应用,取得 PID 和 KEY,按需限制通知及返回域名。新应用已自动使用默认路由;启用通道并核对可用路由后,即可发起收款请求。
请求发送至 MPAY 站点域名。以下路径均相对于站点根目录。
快速开始
- 向本站管理员取得应用的 PID 和 KEY,确认应用已经启用且有可用收款通道。
- 已有商城、发卡或订单系统支持“易支付”时,选择 易支付 V1,网关填写本站根地址(例如
https://pay.example.com/),再填写 PID、KEY。若软件单独要求“提交地址”,才填写https://pay.example.com/submit.php。 - 自行开发时,优先使用页面下单;需要服务端创建订单并取得收银台链接时使用 API 下单。
- 在业务网站实现异步通知,验签并核对订单金额后,幂等完成业务,再返回纯文本
success。 - 用自己的订单核对“下单 → 付款 → 通知 → 业务订单完成”全过程;请求超时先查原订单,不换订单号重复提交。
PID 是应用编号;KEY 是该应用的签名和查询密钥,只保存在业务服务器。out_trade_no 是你的业务订单号;trade_no 是 MPAY 支付单号,两者不能混用。示例中的 PID、订单号、KEY 和域名均为占位示例,需要替换为自己的配置。
本页只介绍本站对外的易支付 V1 接口,不包含支付插件回调或后台管理 API。公开页面不会读取或展示任何应用的真实凭证。
接口一览
| 路径 | 方法 | 用途 |
|---|---|---|
| /submit.php | GET / POST | 页面下单,跳转收银台 |
| /mapi.php | POST | API 下单,返回收银台地址 |
| /api.php | GET / POST | 查询应用/订单 |
| /api.php?act=refund | POST | 按原订单插件申请退款 |
参数使用 UTF-8。POST 使用 application/x-www-form-urlencoded,不接受 JSON、数组或重复参数名。推荐使用 POST,避免在 URL 中传递查询密钥。
返回码遵循易支付 V1 文档,按动作区分:
| JSON 接口 | 成功 code | 未成功或异常 code |
|---|---|---|
| /mapi.php | 1(订单受理) | 0 |
| /api.php 查询操作 | 1 | -1 |
| /api.php?act=refund | 0(已确认退款成功) | -1 |
/api.php 的失败统一使用 -1,包括参数解析、认证、业务拒绝、限流及程序异常;即使尚未解析 act,也不会返回可能被退款调用方误认为成功的 0。错误提示仍为 msg,HTTP 状态按原错误类型返回。
请求频率与失败重试
入口先按连接 IP 限流,验签或 KEY 认证成功后再检查应用额度;submit.php 和 mapi.php 共用该应用的下单额度。默认频率及配置位置见请求频率表。
超限返回 HTTP 429,响应头 Retry-After 表示至少等待多少秒;/mapi.php 返回 code=0,/api.php 返回 code=-1,提示字段均为 msg。接入程序应读取 HTTP 状态及等待时间,避免立即循环重试。限流不代表原订单支付或退款失败,不应因此更换外部订单号或退款号重新申请。
访问保护明确拒绝、业务尚未执行时,响应包含 X-Request-Rejected: 1。一般网络超时、连接中断或其他 5xx 不能据此推断未受理,应先查询原订单。限流不改变原有签名、外部单号幂等和通知验签规则。
下单参数
submit 与 mapi 使用下表的共同字段;必填差异在同一行说明。金额和 PID 均按字符串提交。
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 应用编号 | pid | 是 | string | 1001 | 1~32 位正整数字符串 |
| 商户订单号 | out_trade_no | 是 | string | SHOP202610220001 | 1~64 个字符,同一应用内唯一 |
| 商品名称 | name | 是 | string | 示例商品 | 保存时按 UTF-8 截取至最多 127 字节;签名仍使用提交原值 |
| 订单金额 | money | 是 | string | 12.50 | 大于零的元金额,最多两位小数 |
| 支付方式 | type | submit 否 / mapi 是 | string | alipay | 必须已启用并有可用路由;submit 省略时由付款人选择 |
| 异步通知地址 | notify_url | 是 | string | https://shop.example.com/notify.php |
业务网站接收服务端付款通知的地址,最长 2048 字符 |
| 返回地址 | return_url | submit 是 / mapi 否 | string | https://shop.example.com/return.php |
付款后返回业务网站的地址,最长 2048 字符 |
| 付款人 IP | clientip | submit 否 / mapi 是 | string | 203.0.113.10 | 付款端 IPv4 或 IPv6,不要固定填写你的业务服务器 IP |
| 设备类型 | device | 否 | string | pc | mapi 缺省 pc;submit 按付款浏览器 UA 识别 |
| 附加参数 | param | 否 | string | member_123 | 最多 2048 字节;非空时在成功通知中返回 |
| 签名 | sign | 是 | string | 计算得到的签名 | 32 位小写 MD5,使用全部非空参数计算 |
| 签名算法 | sign_type | 是 | string | MD5 | 固定为大写 MD5 |
submit/mapi 使用原始参数验签,商品名仅在保存时截断。mapi 的 device 缺省或空串使用 pc,非法枚举值拒绝;mapi 请求通常来自业务服务器,不通过该请求的 UA 推断付款设备。device 只控制呈现方式,不证明真实身份;支付插件的内部动作参数不是商户接入字段。
通知及返回地址须为合法 HTTP(S) URL,最多 2048 个字符。应用允许域名留空或包含 * 时不限制域名;填写具体名单时按完整域名精确匹配,每行一个,不支持子域通配。通知由 MPAY 服务器发送,默认允许端口为 80 和 443,正式通知目标须通过公网地址检查。返回地址由浏览器打开,不限制其标准 URL 中的端口或 IP 网段,也不由 MPAY 服务器查询 DNS。成功回跳仍须满足订单访问凭证、付款成功及商户确认条件,并使用原订单冻结的目标和签名凭证。
页面下单
地址: https://pay.example.com/submit.php · 方法: GET / POST · 结果: 跳转收银台。
使用下单参数表。推荐由业务服务器计算签名,输出表单交给付款浏览器提交。KEY 不能放进 HTML、前端 JavaScript 或表单隐藏字段。下面是完整的 PHP 页面示例,需与下载的 epay-sign.php 放在一起,且在输出任何内容之前读取服务器配置:
<?php
declare(strict_types=1);
require __DIR__ . '/epay-sign.php';
use Mpay\Docs\Example\EpaySignature;
$key = getenv('MPAY_KEY') ?: ''; // 在业务服务器配置,不传给浏览器
$parameters = [
'pid' => getenv('MPAY_PID') ?: '',
'out_trade_no' => 'SHOP202610220001', // 换成你的业务订单号,重试沿用原号
'name' => '示例商品',
'money' => '12.50',
'type' => 'alipay',
'notify_url' => 'https://shop.example.com/notify.php',
'return_url' => 'https://shop.example.com/return.php',
];
$parameters['sign'] = EpaySignature::sign($parameters, $key);
$parameters['sign_type'] = 'MD5';
header('Content-Type: text/html; charset=utf-8');
echo '<form method="post" action="https://pay.example.com/submit.php">';
foreach ($parameters as $name => $value) {
echo '<input type="hidden" name="' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8')
. '" value="' . htmlspecialchars($value, ENT_QUOTES, 'UTF-8') . '">';
}
echo '<button type="submit">前往支付</button></form>';请求成功返回 HTTP 303,浏览器跟随 Location 打开本站收银台;此入口不返回 JSON。请求有误时显示错误页面,应按提示修正参数或配置。
API 下单
地址: https://pay.example.com/mapi.php · 方法: POST · 结果: JSON。
使用下单参数表,其中 type、clientip 必填,return_url 可省略。以下代码在业务服务器执行,需要 PHP cURL 扩展和 epay-sign.php,请求正文为表单字符串:
<?php
declare(strict_types=1);
require __DIR__ . '/epay-sign.php';
use Mpay\Docs\Example\EpaySignature;
$parameters = [
'pid' => getenv('MPAY_PID') ?: '',
'out_trade_no' => 'SHOP202610220001',
'name' => '示例商品',
'money' => '12.50',
'type' => 'alipay',
'clientip' => '203.0.113.10', // 换成你获取的真实付款人 IP
'device' => 'pc',
'notify_url' => 'https://shop.example.com/notify.php',
'return_url' => 'https://shop.example.com/return.php',
];
$parameters['sign'] = EpaySignature::sign($parameters, getenv('MPAY_KEY') ?: '');
$parameters['sign_type'] = 'MD5';
$curl = curl_init('https://pay.example.com/mapi.php');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($parameters, '', '&', PHP_QUERY_RFC3986),
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 15,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);
if ($body === false) {
throw new RuntimeException('请求结果待核对,请先查原订单:' . $error);
}
$result = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
if ($status < 200 || $status >= 300 || ($result['code'] ?? null) !== 1) {
throw new RuntimeException('HTTP ' . $status . ':' . ($result['msg'] ?? '响应异常'));
}
// 将此地址交给付款浏览器打开;受理成功不代表已经付款。
echo $result['payurl'];本接口仅返回本站收银台链接 payurl,不返回 qrcode 或 urlscheme。不要根据第三方易支付实现猜测本站响应字段。异常或超时的处理见失败重试。
MD5 签名
- 移除
sign、sign_type以及值为 null 或空字符串的参数;字符串"0"保留。 - 按参数名的 ASCII 字符串顺序升序排列。
- 使用原始值拼接为
key1=value1&key2=value2,拼接时不做 URL 编码、不去除空格、不转换金额格式。 - 在字符串末尾直接追加应用 KEY,计算小写 MD5。
- 将结果作为
sign,并添加sign_type=MD5,然后按表单规则编码发送。
额外提交的非空参数也参与签名。回调验签使用收到的全部参数按相同规则计算。后台“应用管理”的签名计算工具可用于核对。
PHP 示例:
function epaySign(array $parameters, string $key): string
{
unset($parameters['sign'], $parameters['sign_type']);
$parameters = array_filter($parameters, static fn ($value) => $value !== null && $value !== '');
ksort($parameters, SORT_STRING);
$parts = [];
foreach ($parameters as $name => $value) {
$parts[] = $name . '=' . $value;
}
return md5(implode('&', $parts) . $key);
}参数值应为字符串。签名验证后仍需检查订单号、金额、PID 和交易状态。
验签应使用 hash_equals() 比较结果,同时核对 sign_type=MD5 和签名格式;可直接使用签名与验签辅助文件 查看文本 ↗。不要对收到的值再次 URL 解码或格式化金额,否则 12.50 变成 12.5 会造成签名不一致。
支付方式与设备类型
支付方式是否能使用取决于本站启用的插件、通道和应用路由;仅知道编码不代表已开通。
| 编码 | 含义 | 使用条件 |
|---|---|---|
| alipay | 支付宝 | 应用有可用支付宝通道 |
| wxpay | 微信支付 | 应用有可用微信通道 |
mapi.php 的 device 接受下表值,缺省或空串为 pc;submit.php 按付款浏览器识别环境,不使用传入值覆盖识别结果。
| 编码 | 付款端环境 |
|---|---|
| pc | 电脑浏览器 |
| mobile | 手机浏览器 |
| QQ 内置浏览器 | |
| 微信内置浏览器 | |
| alipay | 支付宝内置浏览器 |
| jump | 跳转环境标识;实际呈现由插件决定 |
下单响应
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|
| 受理状态 | code | integer | 1 | 1 受理;0 未成功,不是付款状态 |
| 提示 | msg | string | 订单已受理 | 重复下单成功时为“返回原订单” |
| 支付单号 | trade_no | string | P2026102212000012345 | MPAY 支付单号 |
| 收银台地址 | payurl | string | https://pay.example.com/cashier/pay?biz_no=...&token=... |
交给付款浏览器打开,含订单访问凭证 |
/submit.php 成功后返回 HTTP 303,跳转至收银台。/mapi.php 成功响应示例:
{
"code": 1,
"msg": "订单已受理",
"trade_no": "P2026102212000012345",
"payurl": "https://pay.example.com/cashier/pay?biz_no=...&token=..."
}code=1 表示订单已受理,尚不代表付款成功。拒绝请求时返回 code=0 及 msg;程序或基础设施故障也可能返回非 2xx HTTP 状态。
同一应用重复提交相同订单号和相同业务参数时返回原订单;参数冲突时返回错误。不同应用的同名订单互不影响。请求超时后应先查询原订单,避免用新订单号重复下单。
失败示例(具体 msg 随错误而不同):
{"code":0,"msg":"签名验证失败"}订单查询
向 /api.php 提交当前应用的 pid、key 和 act。查询接口使用 KEY 认证,无需提交 MD5 签名。
地址: https://pay.example.com/api.php · 方法: GET / POST(推荐 POST)。下面的 cURL 示例为 Linux / macOS shell 语法,Windows PowerShell 可用 curl.exe 并将命令合成一行。YOUR_PID、YOUR_KEY 必须替换;KEY 只用于业务服务器,不在前端请求。
| act | 附加参数 | 结果 |
|---|---|---|
| order | trade_no 或 out_trade_no;同时提供时优先 trade_no | 单笔订单 |
| orders | page 默认 1,limit 默认 20,最大 50 | 本应用订单列表,保存在 data 数组 |
| query | 无;省略 act 时使用此操作 | 应用启用状态及订单统计 |
| settle | 无 | 空结算记录;MPAY 不提供平台结算 |
| refund | trade_no 或 out_trade_no、money,选填 refund_no | 调用原订单插件;具体约定见下节 |
查询单笔订单
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 操作 | act | 是 | string | order | 固定 order |
| 应用编号 | pid | 是 | string | 1001 | 当前应用 PID |
| 应用密钥 | key | 是 | string | YOUR_KEY | 当前应用 KEY,不传 sign |
| 支付单号 | trade_no | 二选一 | string | P2026102212000012345 | 与 out_trade_no 同时提供时优先使用 |
| 商户订单号 | out_trade_no | 二选一 | string | SHOP202610220001 | 尚未选择方式、还没有支付单号时使用 |
curl 'https://pay.example.com/api.php' \
--data-urlencode 'act=order' \
--data-urlencode 'pid=YOUR_PID' \
--data-urlencode 'key=YOUR_KEY' \
--data-urlencode 'out_trade_no=SHOP202610220001'{
"code": 1,
"msg": "查询成功",
"trade_no": "P2026102212000012345",
"out_trade_no": "SHOP202610220001",
"api_trade_no": "",
"type": "alipay",
"pid": "1001",
"addtime": "2026-10-22 12:00:00",
"endtime": "2026-10-22 12:01:00",
"name": "示例商品",
"money": "12.50",
"status": 1,
"param": "",
"buyer": ""
}订单响应字段
| 字段名 | 变量名 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|
| 查询状态 | code | integer | 1 | 1 查询成功;失败 -1 |
| 提示 | msg | string | 查询成功 | 状态说明 |
| 支付单号 | trade_no | string | P2026102212000012345 | 未选择方式时可为空 |
| 商户订单号 | out_trade_no | string | SHOP202610220001 | 你的业务订单号 |
| 接口方交易号 | api_trade_no | string | 空字符串 | 有真实交易号时返回,否则为空 |
| 支付方式 | type | string | alipay | 未选择方式时为空 |
| 应用编号 | pid | string | 1001 | 原订单保存的 PID,应用修改 PID 后也不改写旧单 |
| 创建时间 | addtime | string | 2026-10-22 12:00:00 | 上海时区,YYYY-MM-DD HH:mm:ss |
| 付款时间 | endtime | string | 2026-10-22 12:01:00 | 未付款时为空 |
| 商品名称 | name | string | 示例商品 | 已保存的商品名称 |
| 原订单金额 | money | string | 12.50 | 元,固定两位小数 |
| 付款状态 | status | integer | 1 | 1 已确认成功;0 尚未确认成功 |
| 附加参数 | param | string | member_123 | 未提供时为空 |
| 买家信息 | buyer | string | 空字符串 | 本站不提供付款人账户 |
单笔订单在外层 code/msg 后返回 trade_no、out_trade_no、api_trade_no、type、pid、addtime、endtime、name、money、status、param 和 buyer。列表响应为 code/msg/data,data 中每项使用相同订单字段,不重复包含 code/msg。其中:
status=1表示已确认支付成功,status=0表示尚未确认成功。money为下单原金额;付款识别时的金额调整不改变此字段。trade_no在尚未选择支付方式时可为空;api_trade_no只返回已记录的支付宝、微信等接口方真实交易号,没有时为空。手机/PC 监听合成的事件编号不作为平台交易号返回,也不以平台受理单号代替。buyer为空字符串,系统不提供付款人账户信息。
code/msg 只在响应最外层出现一次,分页中的每个订单对象没有单独的成功码。
查询范围仅限当前应用。停用应用或重置 KEY 后,旧凭证无法发起新查询。act=query 只提供应用状态及订单统计,不返回 KEY 或平台结算账户字段 type/account/username;余额固定为 0.00,act=settle 返回空列表。这是单收款主体的接入范围,MPAY 不托管资金、不提供平台结算。
订单列表
地址: https://pay.example.com/api.php · 方法: GET / POST。
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 操作 | act | 是 | string | orders | 固定 orders |
| 应用编号 | pid | 是 | string | 1001 | 当前应用 PID |
| 应用密钥 | key | 是 | string | YOUR_KEY | 当前应用 KEY |
| 页码 | page | 否 | integer | 1 | 默认 1,范围 1~1000000 |
| 每页条数 | limit | 否 | integer | 20 | 默认 20,最大 50 |
curl 'https://pay.example.com/api.php' \
--data-urlencode 'act=orders' \
--data-urlencode 'pid=YOUR_PID' \
--data-urlencode 'key=YOUR_KEY' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20'返回 code/msg/data,data 中每项使用订单响应字段,不重复包含 code/msg。没有订单或已到列表末尾时返回空数组;没有 total 字段。
{"code":1,"msg":"查询成功","data":[]}应用查询
地址: https://pay.example.com/api.php · 方法: GET / POST。
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 操作 | act | 否 | string | query | 省略时默认 query |
| 应用编号 | pid | 是 | string | 1001 | 当前应用 PID |
| 应用密钥 | key | 是 | string | YOUR_KEY | 当前应用 KEY |
curl 'https://pay.example.com/api.php' \
--data-urlencode 'act=query' \
--data-urlencode 'pid=YOUR_PID' \
--data-urlencode 'key=YOUR_KEY'{
"code": 1,
"msg": "本系统不托管资金,余额固定为 0.00",
"pid": "1001",
"active": 1,
"money": "0.00",
"orders": 12,
"order_today": 2,
"order_lastday": 3
}| 字段名 | 变量名 | 类型 | 描述 |
|---|---|---|---|
| 查询状态 | code / msg | integer / string | 成功 1,失败 -1;msg 为说明 |
| 应用编号 | pid | string | 当前应用 PID |
| 启用状态 | active | integer | 1 启用;停用应用无法通过当前认证 |
| 余额 | money | string | 固定 0.00,不表示钱包或可提现余额 |
| 订单总数 | orders | integer | 当前应用订单数 |
| 今日订单数 | order_today | integer | 按系统业务日期统计 |
| 昨日订单数 | order_lastday | integer | 按系统业务日期统计 |
结算记录
地址: https://pay.example.com/api.php · 方法: GET / POST。此接口仅保留易支付接入兼容应答,本站没有平台结算流程。
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 操作 | act | 是 | string | settle | 固定 settle |
| 应用编号 | pid | 是 | string | 1001 | 当前应用 PID |
| 应用密钥 | key | 是 | string | YOUR_KEY | 当前应用 KEY |
curl 'https://pay.example.com/api.php' \
--data-urlencode 'act=settle' \
--data-urlencode 'pid=YOUR_PID' \
--data-urlencode 'key=YOUR_KEY'code 为整数、msg 为说明字符串、data 始终为空数组:
{"code":1,"msg":"本系统自收,无平台结算记录","data":[]}退款申请
地址: https://pay.example.com/api.php?act=refund · 方法: 仅 POST。
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 操作 | act | 是 | string | refund | 只放 URL query,正文不要重复 |
| 应用编号 | pid | 是 | string | 1001 | POST 正文,当前应用 PID |
| 应用密钥 | key | 是 | string | YOUR_KEY | POST 正文,当前应用 KEY |
| 支付单号 | trade_no | 二选一 | string | P2026102212000012345 | 同时提供时优先使用 |
| 商户订单号 | out_trade_no | 二选一 | string | SHOP202610220001 | 原商户订单号 |
| 本次退款金额 | money | 是 | string | 1.00 | 正数元金额,最多两位小数 |
| 商户退款号 | refund_no | 否 | string | REFUND202610220001 | 建议提供;网络重试必须沿用原号 |
以下请求会申请真实退款,请只在确认原订单和退款金额后由业务服务器调用:
curl 'https://pay.example.com/api.php?act=refund' \
--data-urlencode 'pid=YOUR_PID' \
--data-urlencode 'key=YOUR_KEY' \
--data-urlencode 'out_trade_no=SHOP202610220001' \
--data-urlencode 'money=1.00' \
--data-urlencode 'refund_no=REFUND202610220001'明确成功的响应示例:
{
"code": 0,
"msg": "退款成功",
"refund_no": "R2026102212100012345",
"trade_no": "P2026102212000012345",
"money": "1.00",
"refund_status": 1
}| 字段名 | 变量名 | 类型 | 描述 |
|---|---|---|---|
| 协议结果 | code | integer | 0 明确成功;-1 未成功或异常 |
| 说明 | msg | string | 状态或错误说明 |
| 系统退款号 | refund_no | string | MPAY 内部退款号,不是提交的商户退款号 |
| 支付单号 | trade_no | string | 原 MPAY 支付单号 |
| 退款金额 | money | string | 本次退款的元金额 |
| 内部状态 | refund_status | integer | 0 处理中、1 成功、2 拒绝或未执行、3 未知 |
向 /api.php?act=refund 发送 POST 表单,正文提供当前应用 pid/key、trade_no(或 out_trade_no)和 money,不重复提交 URL 中的 act。money 为本次退款的正数元字符串,最多两位小数。只能退款本应用已支付成功的订单,累计成功/处理中/未知退款不得超过原业务金额。
退款要求实际 HTTP 方法为 POST。GET 退款请求在进入认证和退款服务前返回 HTTP 405、Allow: POST,JSON 为 code=-1 和错误说明,不认领退款或调用插件;查询接口继续支持 GET/POST。
refund_no 为业务网站自定的幂等号,1~64 位字母、数字、下划线或连字符。同一支付单内,同号重试返回原退款结果,金额不同则拒绝。省略时使用原支付单唯一默认请求;需要多次部分退款时,每次业务退款必须有不同的 refund_no,网络重试继续使用原号。
退款处理结果含 code/msg/refund_no/trade_no/money/refund_status。返回的 refund_no 是 MPAY 的 20 位内部退款号;只有已明确退款成功才返回 code=0,处理中、拒绝或未执行、结果未知均返回 code=-1。参数、认证或程序异常同样返回非零的 code=-1 和 msg,可能不含退款记录字段。
refund_status 独立表示内部状态,保持 0 处理中、1 成功、2 拒绝或未执行、3 结果未知;不能与协议 code 混用。code=-1 不证明平台一定未退款,处理中和结果未知仍保留金额占用,不能换号重复申请,也不会自动重发。
明确拒绝与结果未知的异常说明分别记录在私有技术日志 payment.refund_rejected 和 payment.refund_unknown,可按 pay_no、refund_no、plugin_code 和 request_id 排查。说明由插件/SDK 脱敏,最多 2048 个 UTF-8 字节,超长带 message_truncated 标记;不将完整报文或凭证写入日志、数据库原因码或公开响应。
读取订单快照、校验配置或初始化插件失败时,尚未进入 refund(),记录为状态 2,原因为“退款准备失败,尚未执行”,不占用可退额度。原退款号再次请求仍返回该失败记录;排除原因后发起新的退款申请应使用新的商户退款号。进入退款动作后发生超时、异常或无法验证返回结果,仍按结果未知保留额度,不能据此判断平台没有退款。
当前微信/支付宝手机、支付宝账单、收钱吧码牌四个监听插件没有原路退款接口,由插件返回不支持,后台订单详情可查看请求记录。通用退款入口并不承诺任何未来平台的异步结果查询;引入该类插件前须补齐平台查询/回调及结果核对。原支付成功和已发送的付款通知不会因退款改成未支付。
易支付 V1 插件会调用其配置平台的退款接口,是否成功以原订单插件及平台实际结果为准。不要将查询的成功码 1 用于判断退款成功。
异步通知与返回页面
确认到账后,MPAY 默认向订单的 notify_url 发送付款结果。管理员人工确认时可以选择暂不发送异步通知,之后在后台放行;期间查询接口仍返回支付成功,但收银台须等商户有效确认后才允许返回。通知使用 GET 参数,包含:
pid、trade_no、out_trade_no、type、name、money、trade_status=TRADE_SUCCESS、sign、sign_type=MD5,以及下单时非空的 param。
| 字段名 | 变量名 | 必有 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 应用编号 | pid | 是 | string | 1001 | 原订单 PID |
| 支付单号 | trade_no | 是 | string | P2026102212000012345 | MPAY 支付单号 |
| 商户订单号 | out_trade_no | 是 | string | SHOP202610220001 | 用于定位你的业务订单 |
| 支付方式 | type | 是 | string | alipay | 该订单的支付方式 |
| 商品名称 | name | 是 | string | 示例商品 | 已保存的名称 |
| 订单金额 | money | 是 | string | 12.50 | 原订单金额,需与业务订单核对 |
| 交易状态 | trade_status | 是 | string | TRADE_SUCCESS | 成功通知固定值 |
| 附加参数 | param | 否 | string | member_123 | 仅原订单非空时提供 |
| 签名 | sign | 是 | string | 32 位小写 MD5 | 必须用完整参数验签 |
| 签名算法 | sign_type | 是 | string | MD5 | 固定 MD5 |
通知形态示例(签名为占位说明,不能直接通过验签):
GET https://shop.example.com/notify.php?pid=1001&trade_no=P2026102212000012345&out_trade_no=SHOP202610220001&type=alipay&name=%E7%A4%BA%E4%BE%8B%E5%95%86%E5%93%81&money=12.50&trade_status=TRADE_SUCCESS&sign=YOUR_SIGNATURE&sign_type=MD5通知金额为订单原金额。通知地址原有的其他查询参数会一并参与签名;与支付协议字段重名的参数以系统生成值为准。
业务网站收到通知后应验证签名、应用、订单号、金额和交易状态,幂等完成业务处理,再以 HTTP 2xx 返回小写纯文本 success。允许首尾空白,不应返回 HTML 或 JSON。未获得有效应答时系统会按通知策略重试,发送记录可在后台查询。
处理顺序:使用完整 GET 参数验签 → 按 out_trade_no 读取业务订单 → 核对 PID、原金额和 TRADE_SUCCESS → 在业务事务中仅处理一次发货或入账 → 提交成功后返回应答。验证失败或事务失败不能返回 success;已经完成的重复通知应再次返回 success。不要只验签就直接发货,也不要在业务尚未落库时提前应答。
完成以上业务处理后的应答正文只有:
success支付成功且商户通知取得有效 success 应答并落库后,有 return_url 自动签名回跳;无返回地址则进入默认成功页。确认超时不改变付款结果,付款者可以重新检查。浏览器跳转用于展示结果,业务网站仍应通过服务端通知或订单查询确认支付,不能仅凭访问返回页面发货。
凭证变更
修改应用 PID 或 KEY 后,新请求使用新值。已创建订单的通知和返回签名仍使用下单时的 PID、KEY 和目标地址。
业务网站切换凭证时,应保留核验未处理完旧订单所需的旧 KEY。重复通知应返回相同成功应答,不能重复发货或记账。
可下载示例
- PHP 签名与验签函数 查看文本 ↗:提供
EpaySignature::sign()、verify(),供上面的下单示例使用。 - PHP 页面下单链接生成示例 查看文本 ↗:在命令行生成签名链接,本身不发送请求。
如果浏览器取消下载,本地文档还提供“查看文本”入口,可以直接打开源码、复制并保存为对应文件名。
本地文档下载的是 .php.txt 文本文件,保存后移除末尾 .txt,将两个 .php 文件放在同一目录。下载文件中的 pay.example.com 是占位域名,不会自动写入本站配置。以下命令用于 Linux / macOS 的 Bash 终端,读取 KEY 时不显示输入;先将 PID、两个域名和业务订单号替换为自己的值,再整段复制到业务服务器终端:
export MPAY_GATEWAY='https://pay.example.com'
export MPAY_PID='YOUR_PID'
export SHOP_URL='https://shop.example.com'
read -r -s -p '请输入应用 KEY: ' MPAY_KEY; printf '\n'
export MPAY_KEY
php epay-create.php SHOP202610220001
unset MPAY_KEY打开输出的签名链接才会创建订单。SHOP_URL 的 notify.php 和 return.php 必须由你的业务网站实现;示例不会替你创建业务订单或发货。完整通知处理说明见验签与联调排障。
常见接入问题
| 现象 | 先检查什么 | 处理方式 |
|---|---|---|
| 签名验证失败 | PID/KEY、sign_type、空值规则、金额原文、URL 编码 | 签名用原始字符串,编码放在签名之后;不要漏掉额外非空参数 |
| 表单格式错误 | Content-Type、JSON、数组或重复字段 | 用 UTF-8 application/x-www-form-urlencoded;URL 与正文不重复传同一字段 |
| 无可用通道 | 应用、支付方式、通道启用状态及路由策略 | 请管理员核对金额、限额、时段和插件配置 |
| API 返回受理成功但业务未完成 | 是否实际付款、通知是否成功 | 受理码不是支付状态;查单并在后台看商户通知应答 |
| 付款成功却一直不返回商户 | notify_url 是否公网可达,是否返回纯文本 success | 先修复通知处理并核对后台通知记录,不在返回页直接发货 |
| HTTP 429 | Retry-After 响应头 | 等待后再查原单;不要立即循环重试 |
| 超时或连接断开 | 原商户订单号的查询结果 | 结果不明时保持原号,不能当作失败另建订单 |
| 退款 code=-1 | refund_status、原退款记录及平台状态 | 处理中/未知时不能换号重发;请管理员核对 |
通用查询失败示例:
{"code":-1,"msg":"请求未成功,请按具体错误说明处理"}页面中的错误文本仅作格式示例。程序应按接口的 code、HTTP 状态和交易状态处理,不要把示例 msg 当作固定错误码。需要管理员部署、通道配置或插件开发说明时,请阅读使用手册。