接口版本对比
| 维度 | V1 | V2 |
| 签名算法 | MD5(KEY) | SHA256WithRSA |
| 提交地址 | submit.php / mapi.php | /api/pay/create |
| 数据格式 | application/x-www-form-urlencoded | application/json |
| 时间戳 | 否 | 是(ISO8601) |
| 退款 / 代付 | 不支持 | 支持 |
V1 接口说明
POST /submit.php:页面跳转支付
POST /mapi.php:API 下单,返回二维码或支付链接
| 参数 | 必填 | 说明 |
| pid | 是 | 商户ID |
| type | 是 | alipay / wxpay |
| out_trade_no | 是 | 商户订单号 |
| notify_url | 是 | 异步回调地址 |
| name | 是 | 商品名称 |
| money | 是 | 金额(元,两位小数) |
| sign_type | 是 | MD5 |
V1 签名规则(MD5)
排除空值与 sign,按参数名 ASCII 升序拼接,末尾追加 key,再进行 MD5 运算。
<?php
$key = 'your_md5_key';
$data = [
'pid' => 10001,
'type' => 'alipay',
'out_trade_no' => 'NO20260723001',
'notify_url' => 'https://example.com/notify.php',
'name' => 'VIP会员',
'money' => '10.00',
'sign_type' => 'MD5'
];
ksort($data);
$str = '';
foreach ($data as $k => $v) {
if ($v !== '' && $k != 'sign') {
$str .= $k . '=' . $v . '&';
}
}
$str .= 'key=' . $key;
$data['sign'] = md5($str);
?>
V1 submit.php 页面跳转
<form action="https://pay.example.com/submit.php" method="post">
<input type="hidden" name="pid" value="10001">
<input type="hidden" name="type" value="alipay">
<input type="hidden" name="out_trade_no" value="NO20260723001">
<input type="hidden" name="notify_url" value="https://example.com/notify.php">
<input type="hidden" name="name" value="VIP会员">
<input type="hidden" name="money" value="10.00">
<input type="hidden" name="sign_type" value="MD5">
<input type="hidden" name="sign" value="<?php echo $data['sign']; ?>">
<button type="submit">立即支付</button>
</form>
V1 mapi.php 服务端下单
<?php
$ch = curl_init('https://pay.example.com/mapi.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => http_build_query($data)
]);
$response = curl_exec($ch);
$result = json_decode($response, true);
// $result['qrcode'] 或 $result['payurl']
?>
V1 异步回调验签
<?php
$key = 'your_md5_key';
$params = $_POST;
$sign = $params['sign'];
unset($params['sign']);
ksort($params);
$str = '';
foreach ($params as $k => $v) {
if ($v !== '') {
$str .= $k . '=' . $v . '&';
}
}
$str .= 'key=' . $key;
if (md5($str) === $sign && $params['trade_status'] === 'TRADE_SUCCESS') {
echo 'success';
} else {
echo 'fail';
}
?>
回调必须输出 success,否则支付平台会持续重试通知。
V2 接口说明(RSA)
POST /api/pay/create:统一下单接口
- 签名算法:SHA256WithRSA
- 商户私钥签名,平台公钥验签
| 参数 | 必填 | 说明 |
| merchant_no | 是 | 商户号 |
| out_trade_no | 是 | 商户订单号 |
| name | 是 | 商品名称 |
| amount | 是 | 金额(单位按平台配置) |
| notify_url | 是 | 异步回调地址 |
| timestamp | 是 | ISO8601 时间戳 |
| sign | 是 | RSA 签名(Base64) |
V2 签名规则(RSA)
<?php
$privateKey = file_get_contents('private.pem');
$data = [
'merchant_no' => 'M10001',
'out_trade_no' => 'NO20260723001',
'name' => 'VIP会员',
'amount' => '1000',
'notify_url' => 'https://example.com/notify_v2.php',
'timestamp' => '2026-07-23T10:00:00+08:00'
];
ksort($data);
$str = '';
foreach ($data as $k => $v) {
if ($v !== '') {
$str .= $k . '=' . $v . '&';
}
}
$str = rtrim($str, '&');
openssl_sign($str, $signature, $privateKey, OPENSSL_ALGO_SHA256);
$data['sign'] = base64_encode($signature);
?>
V2 统一下单请求
<?php
$payload = json_encode($data, JSON_UNESCAPED_SLASHES);
$ch = curl_init('https://pay.example.com/api/pay/create');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => $payload
]);
$response = curl_exec($ch);
$result = json_decode($response, true);
// $result['data']['payurl'] 或 qrcode
?>
V2 异步回调验签
<?php
$publicKey = file_get_contents('platform_public.pem');
$params = json_decode(file_get_contents('php://input'), true);
$sign = base64_decode($params['sign']);
unset($params['sign']);
ksort($params);
$str = '';
foreach ($params as $k => $v) {
if ($v !== '') {
$str .= $k . '=' . $v . '&';
}
}
$str = rtrim($str, '&');
if (openssl_verify($str, $sign, $publicKey, OPENSSL_ALGO_SHA256) === 1) {
echo json_encode(['code' => 'success']);
} else {
echo json_encode(['code' => 'sign_error']);
}
?>
安全建议
- 金额、订单号以服务端数据为准,不信任前端传入参数
- 订单处理逻辑需具备幂等性,防止重复入账
- V2 接口必须校验 timestamp,建议允许误差 ±5 分钟
- 私钥与商户 KEY 严禁出现在前端代码中
- 异步回调(notify)用于发货,同步跳转(return)仅用于展示