版本 v1.0 | 更新日期:2026-08-10 | 接口地址:https://test1.pay.sckeji.top
MaPay 是一套基于个人微信/支付宝收款码的免签约支付系统。商户无需申请官方支付接口,通过"个人收款码 + PC监控端"即可实现在线收款的自动回调。
success| 凭证 | 说明 |
|---|---|
pid | 商户ID,唯一数字标识 |
secret_key | 商户密钥,用于签名验证,请妥善保管 |
| 项目 | 说明 |
|---|---|
| 接口地址 | https://test1.pay.sckeji.top |
| 请求方式 | POST(部分接口支持 GET) |
| 数据格式 | application/x-www-form-urlencoded |
| 字符编码 | UTF-8 |
| 签名算法 | MD5 |
适用于表单提交跳转收银台的场景。
接口地址:https://test1.pay.sckeji.top/submit
适用于前后端分离或移动端场景,返回 JSON 数据。
接口地址:https://test1.pay.sckeji.top/mapi
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
pid | int | 必填 | 商户ID |
type | string | 必填 | 支付方式:wxpay=微信,alipay=支付宝 |
out_trade_no | string | 必填 | 商户订单号(需唯一) |
notify_url | string | 必填 | 异步通知地址(支付结果回调URL) |
return_url | string | 可选 | 同步跳转地址(支付成功后浏览器跳转) |
name | string | 必填 | 商品名称 |
money | float | 必填 | 订单金额(元,如 10.00) |
sign | string | 必填 | MD5签名 |
sign_type | string | 必填 | 固定值 MD5 |
{
"code": 1,
"msg": "订单创建成功",
"trade_no": "H2026081012345678",
"qrcode": "https://test1.pay.sckeji.top/Pay/console/H2026081012345678"
}
{"code": 0, "msg": "签名错误"}| 字段 | 说明 |
|---|---|
code | 1=成功,其他=失败 |
trade_no | 平台订单号 |
qrcode | 收银台地址(可生成二维码供用户扫描) |
sign 和 sign_type)按参数名字典序排列& 连接,格式 key=value,空值参数不参与secret_key假设参数:pid=1001, type=wxpay, out_trade_no=ORDER_001, name=测试商品, money=10.00, notify_url=https://your-site.com/notify
排序拼接:
money=10.00&name=测试商品¬ify_url=https://your-site.com/notify&out_trade_no=ORDER_001&pid=1001&type=wxpay
追加密钥后 MD5:
sign = md5("money=10.00&name=测试商品&...&type=wxpay" + "你的secret_key")
function createSign(array $params, string $secret_key): string
{
unset($params['sign'], $params['sign_type']);
$params = array_filter($params, fn($v) => $v !== '');
ksort($params);
$signStr = '';
foreach ($params as $k => $v) {
$signStr .= $k . '=' . $v . '&';
}
$signStr = substr($signStr, 0, -1) . $secret_key;
return md5($signStr);
}
import hashlib
def create_sign(params: dict, secret_key: str) -> str:
filtered = {k: v for k, v in params.items()
if k not in ('sign', 'sign_type') and v != ''}
sign_str = '&'.join(f'{k}={filtered[k]}' for k in sorted(filtered))
sign_str += secret_key
return hashlib.md5(sign_str.encode('utf-8')).hexdigest()
public static String createSign(Map<String, String> params, String secretKey) {
TreeMap<String, String> sorted = new TreeMap<>();
for (var e : params.entrySet()) {
if (!"sign".equals(e.getKey()) && !"sign_type".equals(e.getKey())
&& !e.getValue().isEmpty()) {
sorted.put(e.getKey(), e.getValue());
}
}
StringBuilder sb = new StringBuilder();
for (var e : sorted.entrySet()) {
sb.append(e.getKey()).append("=").append(e.getValue()).append("&");
}
sb.deleteCharAt(sb.length() - 1).append(secretKey);
return DigestUtils.md5Hex(sb.toString());
}
支付成功后,系统会向商户的 notify_url 发送异步通知。
系统通过 GET 方式将参数附加到 notify_url:
| 参数 | 类型 | 说明 |
|---|---|---|
pid | int | 商户ID |
trade_no | string | 平台订单号 |
out_trade_no | string | 商户订单号 |
type | string | 支付方式:wxpay / alipay |
name | string | 商品名称 |
money | string | 订单金额(元) |
trade_status | string | TRADE_SUCCESS = 交易成功 |
sign_type | string | MD5 |
sign | string | MD5签名 |
GET https://your-site.com/notify?pid=1001&trade_no=H2026081012345678
&out_trade_no=ORDER_001&type=wxpay&name=测试商品
&money=10.00&trade_status=TRADE_SUCCESS
&sign_type=MD5&sign=abc123...
success:否则系统认为通知失败| 次数 | 间隔 |
|---|---|
| 第1次 | 支付成功后立即通知 |
| 第2次 | 5秒后重试 |
| 第3次 | 30秒后重试 |
| 第4次 | 5分钟后重试 |
// 接收通知
$data = $_GET;
$sign = $data['sign'];
unset($data['sign']);
// 验签
$mySign = createSign($data, $my_secret_key);
if ($mySign !== $sign) {
exit('签名验证失败');
}
// 处理业务
if ($data['trade_status'] === 'TRADE_SUCCESS') {
$order = findOrder($data['out_trade_no']);
if ($order && $order['money'] == $data['money'] && $order['status'] == 0) {
updateOrderPaid($order['id']);
echo 'success'; // 必须返回 success
}
}
@app.route('/notify')
def payment_notify():
data = request.args.to_dict()
sign = data.pop('sign', '')
# 验签
filtered = {k: v for k, v in data.items() if v != ''}
sign_str = '&'.join(f'{k}={filtered[k]}' for k in sorted(filtered))
if hashlib.md5((sign_str + SECRET_KEY).encode()).hexdigest() != sign:
return '签名失败', 403
# 处理业务
if data.get('trade_status') == 'TRADE_SUCCESS':
# TODO: 校验订单、更新状态
return 'success'
支付成功后,若创建订单时传入了 return_url,用户浏览器会跳转到该地址。
跳转参数与异步通知相同(GET方式附加在URL后)。
商户可主动查询订单支付状态。
接口地址:GET https://test1.pay.sckeji.top/getOrderState/{order_id}
{
"order_id": "H2026081012345678",
"passtime": 120,
"state": 0
}
{
"order_id": "H2026081012345678",
"passtime": 0,
"state": 1,
"return_url": "https://your-site.com/return?pid=1001&trade_no=H20260810..."
}
| 字段 | 说明 |
|---|---|
state | 0=待支付,1=已支付 |
passtime | 剩余有效时间(秒) |
本节面向 PC 监控端开发者,说明监控端如何与系统对接。
接口地址:https://test1.pay.sckeji.top/monitor/api/heartbeat
监控端应每 60秒 发送一次心跳。超过5分钟无心跳将标记为离线。
| 参数 | 必填 | 说明 |
|---|---|---|
client_id | 必填 | 客户端ID |
client_secret | 必填 | 客户端密钥 |
// 响应示例
{
"code": 1,
"msg": "ok",
"data": {
"client_id": 1,
"client_name": "我的电脑",
"pid": 1001,
"pay_api_url": "https://test1.pay.sckeji.top",
"pay_secret_key": "abc123..."
}
}
接口地址:https://test1.pay.sckeji.top/monitor/api/notify
| 参数 | 必填 | 说明 |
|---|---|---|
client_id | 必填 | 客户端ID |
client_secret | 必填 | 客户端密钥 |
amount | 必填 | 收款金额(元),如 10.00 |
pay_type | 可选 | wxpay / alipay |
source | 可选 | 来源标识,默认 notification |
// 响应示例
{
"code": 1,
"msg": "回调成功",
"data": { "matched": true }
}
系统采用"精确金额 + 递增偏移"机制:
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
签名错误 | sign 验证不通过 | 检查签名算法和密钥 |
用户禁用或不存在 | 商户被禁用 | 联系管理员 |
订单提交重复 | out_trade_no 已存在 | 确保订单号唯一 |
创建订单失败 | 无可用收款通道 | 联系管理员配置 |
参数错误 | 缺少必填参数 | 检查参数完整性 |
客户端验证失败 | client_id/secret 错误 | 检查监控端凭证 |
参数缺失或金额无效 | 金额为0或缺失 | 检查 amount 参数 |
A:订单创建后 180 秒(3分钟)内有效,超时自动关闭。
A:目前支持微信支付(wxpay)和支付宝(alipay)。
A:系统会自动重试多次。商户也可通过 /getOrderState/{order_id} 主动查询。
A:先用小额(如 0.01 元)测试,确认签名和回调正常后再投入使用。
A:系统自动递增 0.01 元区分,监控端上报实际到账金额即可。
A:trade_no 是平台订单号,out_trade_no 是商户传入的商户订单号。
success