DEVELOPER API码支付 · 兼容易支付协议

从签名到回调,
按这份文档一次接通。

适用于独立商城、发卡系统、订单系统及其他服务端应用。本文档按真实接口调用顺序编排,包含完整参数、MD5 签名、回调验签、查单与排错方法。

先准备好商户 PID、支付对接密钥和至少一个已启用通道

凭证请从“商户中心 → API 配置”复制。签名使用的是支付对接密钥(pay_key),不要使用登录密码或挂机软件密钥。

00

CHOOSE A ROUTE

先选择你的接入方式

DuoCai 同时提供原生码支付 API和易支付兼容协议。两者都能使用,但配置方式不同,请只按选中的路线操作。

已有易支付插件直接填三项配置

接口地址填 https://pay.1kexiu.xyz/(保留末尾 /),再填商户中心显示的 PID 和支付对接密钥 KEY。不要把地址填成 /mapi.php。

自主开发接码支付原生 API

服务端按本文签名后 POST /mapi.php下单,用 /api.php 查单,并在 notify_url 处验签、幂等入账。

网页收银台服务端签名后跳转

需要让用户浏览器直接进收银台时,由服务端生成已签名的表单并提交到 /submit.php。

当前没有独立测试环境

上述域名是生产环境。先运行离线签名测试,再用独立测试订单号创建 0.01 元订单;未经确认不要在生产订单上发货。

01

QUICK START

接入流程

推荐由你的服务端完成签名和下单。浏览器、APP、小程序只接收服务端返回的收银台地址,绝不能持有支付密钥。

  1. 1
    获取凭证

    复制商户 PID 与支付对接密钥,启用套餐和支付通道。

  2. 2
    生成签名

    服务端按 ASCII 键名排序,拼接参数与密钥并计算 MD5。

  3. 3
    创建订单

    原生 API 请求 mapi.php;易支付插件只需配置网关根地址、PID 和 KEY。

  4. 4
    处理回调

    验签、核对金额和订单状态,幂等更新后返回纯文本 success。

接口约定
  • 协议:HTTPS
  • 编码:UTF-8
  • 请求:application/x-www-form-urlencoded
  • 金额单位:人民币元
  • 签名:MD5(32 位小写)
02

AUTHENTICATION

商户凭证

pid商户 PID

平台分配的对接标识,以后台显示值为准,当前通常为 M 开头。

pay_key支付对接密钥

只用于支付签名和回调验签。密钥重置后,旧签名立即失效。

密钥安全红线

不要写进 HTML、JavaScript、APP 安装包、公开仓库或访问日志;不要将 key 返回给前端。生产环境请通过环境变量或密钥管理服务读取。

03

SIGNATURE

MD5 签名算法

  1. 排序

    把本次实际发送的全部参数按参数名 ASCII 升序排列。

  2. 过滤

    排除 sign、sign_type、路由参数 a/c/m/s 以及值为空字符串的参数。

  3. 拼接

    按 key=value 用 & 连接;值保持原样,签名前不要 URL 编码。

  4. 加密

    在参数串末尾直接追加支付对接密钥,计算 32 位小写 MD5。

验签使用表单解析后的原始字段值

服务端以 application/x-www-form-urlencoded 解析后的值计算签名。验签前不会执行 HTML 转义、去除首尾空格、大小写转换、金额格式重写、JSON 转义或二次 URL 编解码。notify_url、return_url 可以携带查询参数,其中的原始 & 必须参与签名;不要改成 &。

完整签名规则
  • 排除 sign、sign_type、a、c、m、s。
  • 排除值为''或 null 的字段,其他已发送字段全部参与。
  • 按参数名升序排列,按 参数名=原始参数值 拼接并以 & 连接。
  • 参数串末尾直接追加 pay_key,计算 32 位小写 MD5。
  • 客户端必须先计算签名,再用 http_build_query 或等价方法编码并发送。
待签名字符串money=0.01&name=测试商品&...&type=alipayYOUR_PAY_KEY末尾密钥前不增加 &key=,而是直接拼接密钥。
签名一致性

可选参数只要非空且被发送,就必须参与签名。请求金额用什么字符串签名就发送什么字符串;回调中的 money 固定为两位小数,例如 1.00。

固定签名测试向量

先用下面的假凭证验证你的签名函数。只有得到相同结果,再替换成真实 PID 和密钥发起请求;该测试向量不能用于真实下单。

三种语言都必须得到同一 MD5
pay_key = test_pay_key_20260813
pid = M1234567890
type = alipay
out_trade_no = ORDER202608130001
notify_url = https://shop.example.com/pay/notify?channel=alipay&version=2
return_url = https://shop.example.com/pay/return?order=ORDER202608130001&source=pay
name = 测试订单
money = 1.00

expected_sign = beddd35092c3afea54c5218de0872502
04

PAGE PAYMENT

页面跳转支付

POST / GEThttps://pay.1kexiu.xyz/submit.php

适合网页商城和发卡系统。建议用服务端生成的自动提交表单将用户浏览器跳转到此接口;成功后进入平台收银台,响应是 HTML,不是 JSON。

请求参数

参数必填类型说明
pid必填String商户 PID,以后台 API 配置页显示值为准。
type必填String支付分类:alipay、wxpay、qqpay;需有对应启用通道。
out_trade_no必填String(128)商户订单号,只允许字母、数字及 . _ - |;同一 PID 内必须唯一。为避免跨系统碰撞,建议使用“项目标识 + 时间戳 + 随机串”生成全局高概率唯一值,不要只用简单自增数字。
notify_url必填String(288)服务端异步通知地址,必须可被公网 HTTPS 访问;允许携带查询参数,原始 & 参与签名。
return_url必填String(288)付款后的浏览器跳转地址;允许携带查询参数,不能代替异步通知。
name必填String(188)商品或订单标题。
money必填String单位元,范围 0.01~99999999.99,最多两位小数;建议始终使用两位小数字符串。
param选填String(300)业务扩展参数,付款回调时原样返回;不要放敏感信息。
sign必填String按本文算法生成的 32 位小写 MD5。
sign_type必填String固定为 MD5。
HTML 自动提交示例
<form id="payForm" action="https://pay.1kexiu.xyz/submit.php" method="post">
  <input type="hidden" name="pid" value="YOUR_PID">
  <input type="hidden" name="type" value="alipay">
  <input type="hidden" name="out_trade_no" value="ORDER_202608130001">
  <input type="hidden" name="notify_url" value="https://your-site.example/pay/notify">
  <input type="hidden" name="return_url" value="https://your-site.example/pay/return">
  <input type="hidden" name="name" value="测试商品">
  <input type="hidden" name="money" value="0.01">
  <input type="hidden" name="sign" value="SERVER_GENERATED_SIGN">
  <input type="hidden" name="sign_type" value="MD5">
</form>
<script>document.getElementById('payForm').submit();</script>
SERVER_GENERATED_SIGN 只是占位符

不能把它原样提交。必须先在你的服务端按本文算法计算真实 sign,再渲染自动提交表单;可直接复用后文 PHP/Node.js/Python 的 makeSign 函数。

05

API PAYMENT

API 下单

POSThttps://pay.1kexiu.xyz/mapi.php

适合服务端请求后自行处理支付链接或二维码。请求参数与 页面跳转支付一致,推荐使用 POST 表单编码。

return_url 在新版中必须传

旧版文档曾把它标为选填,但新版核心下单服务会校验该字段;缺少时会返回“回调地址(return_url)不能为空”。

请求超时时不要盲目重复下单

先使用原 out_trade_no 调用 act=order 查单;查到订单就继续使用该平台订单。如暂时查不到,等待数秒后再查,不要立即更换订单号;当前接口不承诺下单请求幂等。

兼容参数

参数必填说明
clientip选填付款用户 IP,兼容易支付客户端;发送非空值时必须参与签名。
device选填pc、mobile、wechat、alipay 或 qq;发送时参与签名。

成功响应

“商户中心 → 通道管理 → 通道配置 → API 返回模式”决定主要字段:payurl 为支付/收银台地址,qrcode 为可生成二维码的内容。接入端应兼容两者,优先处理实际非空字段。

支付链接模式
{
  "code": 1,
  "msg": "下单成功",
  "trade_no": "5090058146920009728",
  "payurl": "https://pay.1kexiu.xyz/gateway/Submit/pay?trade_no=..."
}
二维码模式
{
  "code": 1,
  "msg": "下单成功",
  "trade_no": "5090058146920009728",
  "qrcode": "https://qr.alipay.com/..."
}
失败响应
{
  "code": -1,
  "msg": "签名校验未通过,请检查签名算法、参与签名的参数原文和支付对接密钥(请求编号:xxxxxxxxxxxxxxxx)"
}
提供请求编号可加快排查

签名失败时请保留响应中的请求编号,并向技术支持提供 PID、请求时间和脱敏后的参数名称列表。请勿发送支付对接密钥或完整回调 Token。

06

NOTIFICATION

支付回调

支付成功后,平台会向下单时的 notify_url 发送异步通知,并向 return_url 携带同步跳转参数。为兼容不同通道版本,接收端应同时读取 URL 查询参数和表单参数;只有异步通知可以作为可靠到账依据。

notify_url 不能填 localhost 或内网地址

必须使用平台服务器可直接访问的公网 HTTPS 地址。本地开发时请使用受信任的内网穿透/反向代理,并确认该路由不会 301/302 跳转。

回调请求读取规则

PHP 可使用 array_replace($_GET, $_POST) 合并回调字段,同名字段以 POST 为准。无论参数来自 GET 还是 POST,都必须使用框架解析后的原始字段值验签,不要先做 HTML 转义、trim 或金额格式化。

参数类型说明
pidString商户 PID。
trade_noString平台支付订单号。
out_trade_noString你的商户订单号。
typeString支付分类。
nameString商品名称。
moneyString商户订单金额,固定两位小数,例如 1.00。
trade_statusString只有精确等于 TRADE_SUCCESS 才表示成功。
paramString下单扩展参数;下单未传时可能不存在。
signString使用支付对接密钥按相同算法验签。
sign_typeString固定为 MD5。
1
先验签

使用收到的原始参数验签,失败立即拒绝。

2
再核订单

核对 PID、订单号、订单金额与本地待支付记录。

3
幂等入账

以 trade_no 或 out_trade_no 加唯一约束,重复通知不能重复发货。

4
最后应答

业务处理成功后返回纯文本小写 success,不要附加 HTML 或 JSON。

严禁只凭浏览器跳转发货

return_url 可能被用户关闭、重复访问或伪造。同步页只用于展示结果,必须以已验签的异步通知或主动查单结果更新订单。

PHP 8+:可独立运行的回调验签骨架
<?php
function makeSign(array $params, string $key): string {
    unset($params['sign'], $params['sign_type'],
          $params['a'], $params['c'], $params['m'], $params['s']);
    $params = array_filter($params, fn($v) => $v !== '' && $v !== null);
    ksort($params, SORT_STRING);
    $pairs = [];
    foreach ($params as $name => $value) $pairs[] = $name . '=' . $value;
    return md5(implode('&', $pairs) . $key);
}

$params = array_replace($_GET, $_POST); // POST 覆盖同名 GET
$clientSign = (string)($params['sign'] ?? '');

if ($clientSign === '' || !hash_equals(makeSign($params, getenv('DUOCAI_PAY_KEY')), $clientSign)) {
    http_response_code(400);
    exit('fail');
}
if (($params['trade_status'] ?? '') !== 'TRADE_SUCCESS') {
    http_response_code(400);
    exit('fail');
}

// TODO:接下来必须在事务中锁定本地订单,核对 pid/out_trade_no/money/trade_no,
// 仅把“未支付”更新为“已支付”;重复通知可返回 success,但禁止重复发货。
// 在 updateLocalOrderOnce($params) 尚未真正实现并成功提交事务前,必须保持 fail。
http_response_code(501);
exit('fail');

// 实现业务处理后,删除上面两行;仅在事务提交成功后返回:exit('success');
07

ORDER QUERY

订单查询

POST / GEThttps://pay.1kexiu.xyz/api.php

查询接口使用 pid 与支付对接密钥 key 鉴权,并只返回该 PID 自己的订单。推荐服务端使用 POST 表单,避免密钥出现在 URL、浏览器历史和代理日志中;GET 仅为兼容旧客户端保留。

POST查询商户状态
act=query&pid=YOUR_PID&key=YOUR_PAY_KEY

成功时返回 code=1、开通状态、余额、用户名及套餐允许的通道驱动标识列表 type;可用于服务器端连通性检查。

POST查询单个订单
act=order&pid=YOUR_PID&key=YOUR_PAY_KEY&out_trade_no=ORDER_NO

可传 out_trade_no(商户订单号)或 trade_no(平台订单号),只会查询当前 PID 的订单。

POST查询订单列表
act=orders&pid=YOUR_PID&key=YOUR_PAY_KEY&limit=20&page=1

limit 范围 1–50,page 从 1 开始。不要用高频轮询替代异步回调。

查询响应结构

act=query 返回 code,pid,key,active,money,username,type;act=order 返回 code,trade_no,out_trade_no,type,money,price,status,pay_time;act=orders 返回 code,total,data,其中 data 是订单数组。pay_time 为 Unix 秒级时间戳,未支付时为 null。

cURL:推荐的 POST 查单方式
curl --request POST 'https://pay.1kexiu.xyz/api.php' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'act=order' \
  --data-urlencode 'pid=YOUR_PID' \
  --data-urlencode 'key=YOUR_PAY_KEY' \
  --data-urlencode 'out_trade_no=ORDER_NO'

单笔订单成功响应示例

JSON
{
  "code": 1,
  "trade_no": "5090058146920009728",
  "out_trade_no": "ORDER_202608130001",
  "type": "alipay",
  "money": "0.01",
  "price": "0.01",
  "status": 1,
  "pay_time": 1787716800
}
act=query 的兼容响应包含 key

这是为旧协议兼容保留的字段。查询接口只能由你的服务端调用,禁止把完整响应转发给浏览器、APP 或第三方日志平台。

status = 0未支付 / 等待支付
status = 1支付成功
status = 2订单已超时
08

TROUBLESHOOTING

错误处理与重试

不能只看 HTTP 状态码

接口的业务失败可能仍返回 HTTP 200。mapi.php 和 api.php 必须解析 JSON 并判断 code;submit.php 失败时可能返回带错误说明的 HTML 页面。

签名校验未通过最常见

确认使用支付对接密钥;按 ASCII 排序;空值、sign、sign_type 不参与;所有实际发送的非空参数都参与签名。重点检查回调 URL 中的 & 是否在签名前被错误转成 &amp;,或是否发生 trim、金额格式化及二次 URL 编解码。

商户 PID 或支付对接密钥错误

仅用于 api.php 查单鉴权。确认传的是商户中心“API 配置”中的 PID 和“对接密钥(KEY)”,不是登录密码、软件密钥或下单签名 sign。

回调地址(return_url)不能为空

新版页面支付和 API 下单都需要传 return_url。它与 notify_url 是两个不同地址,且都应使用绝对 HTTPS URL。

当前商户无通道可用

检查套餐是否有效、所选 type 是否存在已启用通道、通道配置是否保存完整,以及通道限额/轮询配置。

商户余额不足无法发起支付

当前套餐存在费率且手续费余额不足。先在商户中心充值手续费余额,再重新发起。

订单号格式不正确 / 此订单已支付成功

out_trade_no 只使用字母、数字及 . _ - |,并按“项目标识 + 时间戳 + 随机串”生成全局高概率唯一值;支付状态不确定时先查单,不要立即重试或更换订单号。

安全处理顺序
  1. 请求超时或响应无法解析时,保留原 out_trade_no 和原始业务参数。
  2. 调用订单查询接口,数秒内重复查询确认是否已建单或已支付。
  3. 已查到的订单继续使用原平台订单;始终查不到时进入人工/延迟补偿,避免并发重复建单。
09

AI ASSISTED INTEGRATION

复制提示词,让 AI 按真实规则接入

把下面整段提示词复制给 ChatGPT、Codex、Claude、Cursor 或其他编程助手。提示词已经写明接口、签名、回调、查单、测试和交付要求,可减少 AI 猜错参数或直接提交占位签名的问题。

OFFICIAL SERVER DEMO

先跑通固定签名,再接进你的项目

一包包含 PHP 8+、Node.js 18+、Python 3.9+ 三套无框架示例,以及下单、查单、回调验签模板和离线签名测试。

v1.1.018.3 KB无真实密钥不含第三方依赖
SHA-256f29d733bfd0cd1e55dc1e73ac16069a8a19fb8c51640f1d46cf2036afe3c35f5
下载完整 Demo ZIP 下载后先阅读 README.md
不要把真实密钥粘贴到聊天中

让 AI 使用 DUOCAI_PID 和 DUOCAI_PAY_KEY 环境变量。你只需在自己的服务器配置真实值,提示词和代码仓库中都不要出现明文密钥。

DuoCai码支付 · AI 通用接入提示词
你是一名资深支付接口集成工程师。请在我现有的项目中完成 DuoCai码支付接入,并以 https://pay.1kexiu.xyz/docs/api 为接口规则来源。先只读检查项目已有的订单、支付、回调、数据库和配置实现,优先复用现有结构,禁止重写无关模块、升级框架或修改第三方依赖。

【先判断接入方式,只选择一条主路径】
A. 如果项目已有“易支付/彩虹易支付/异步支付”兼容插件:优先只配置网关 https://pay.1kexiu.xyz/(必须保留末尾 /)、PID、KEY 和支付类型,由现有插件完成签名、下单及回调;除非插件确实缺少功能,否则不要再写一套 /mapi.php 对接。
B. 如果项目没有兼容插件、需要自行开发服务端:走 DuoCai 原生码支付流程,服务端 POST /mapi.php 下单、POST /api.php 查单,并实现 notify_url 回调验签和幂等入账。
C. 如果只需要网页跳转收银台:由服务端生成签名后 POST /submit.php;该接口返回 HTML,不是 JSON。
先说明你识别出的接入模式和判断理由;除非我明确要求,不要同时实现多条路径。

【我的接入目标】
1. 接口域名:https://pay.1kexiu.xyz
2. 商户 PID:从环境变量 DUOCAI_PID 读取。
3. 支付对接密钥:从环境变量 DUOCAI_PAY_KEY 读取。
4. 需要接入的支付分类:alipay、wxpay;如果项目还需要 qqpay,再一并支持。
5. 按上面的模式判断执行;不能识别时先列出项目中找到的支付插件、下单入口和回调路由,再选择改动最小的方案。
6. 不要要求我把真实 PID 或密钥粘贴到聊天中,不要把密钥写入前端、APP、日志、异常信息或代码仓库。

【必须严格实现的签名规则】
1. 使用 application/x-www-form-urlencoded 解析后的原始字段值。
2. 排除 sign、sign_type、a、c、m、s。
3. 排除值为 null 或空字符串的字段;数字 0 和字符串 0 必须参与签名。
4. 其余实际发送的字段按参数名升序排序。
5. 按 参数名=原始参数值 拼接,字段之间使用 & 连接。
6. 在完整参数串末尾直接追加 DUOCAI_PAY_KEY 的值,不要追加 &key=,然后计算 UTF-8 字节的 32 位小写 MD5。
7. 必须先使用原始值计算签名,再进行表单 URL 编码并发送。
8. 签名前禁止执行 htmlspecialchars、htmlentities、trim、大小写转换、金额格式重写、JSON 转义、URL 重组或二次 urlencode/urldecode。
9. notify_url 和 return_url 可以携带多个查询参数;其中的原始 & 必须参与签名,不能改成 &amp;。
10. 请求中的 money 按实际发送的原始字符串签名;回调中的 money 按平台实际返回值验签,通常为两位小数。

【创建订单】
使用 POST https://pay.1kexiu.xyz/mapi.php,Content-Type 为 application/x-www-form-urlencoded。至少发送:
pid、type、out_trade_no、notify_url、return_url、name、money、sign、sign_type=MD5。
param、clientip、device 等可选字段只要非空并实际发送,就必须参与签名。
return_url 在当前系统中为必填字段。
out_trade_no 在同一 PID 内必须唯一,最长 128 字符,只允许字母、数字和 . _ - |。为兼容当前系统并避免跨项目碰撞,必须生成全局高概率唯一值,推荐“项目标识_时间戳_随机串”,禁止只用简单自增数字。money 范围是 0.01~99999999.99 且最多两位小数;name 最长 188,notify_url/return_url 最长 288,param 最长 300 字符。
不要把 SERVER_GENERATED_SIGN 当作真实签名,它只是文档占位符。

接口业务失败可能仍返回 HTTP 200,所以必须解析 JSON 并判断 code。code=1 才表示下单成功。成功响应可能返回 payurl,也可能返回 qrcode;代码要兼容两者,优先使用实际非空字段,并保存平台 trade_no。不要只根据 HTTP 状态码判断成功。

如果使用 /submit.php,它返回 HTML 收银台页面,不是 JSON。签名必须仍在服务端生成,浏览器只能接收已经签好的自动提交表单,不能接触支付密钥。

【异步回调】
1. notify_url 接收端同时兼容 URL 查询参数和 POST 表单;PHP 可用 array_replace($_GET, $_POST),同名字段以 POST 为准,其他语言实现同等逻辑。
2. 使用收到的原始字段重新计算签名,并用安全比较函数核对 sign。
3. 验证 trade_status 必须等于 TRADE_SUCCESS。
4. 按 out_trade_no 查询本地订单,核对 pid、money、订单状态和平台 trade_no。
5. 使用数据库事务、行锁或唯一约束实现幂等:同一订单重复通知不能重复加款、发货或执行权益。
6. 只有本地订单核验、幂等业务处理和数据库事务全部成功提交后,才返回纯文本 success;失败返回 fail 或非 2xx。任何带 TODO、占位函数或尚未实现入账逻辑的回调示例都必须默认返回 fail,严禁在占位代码后直接返回 success。
7. return_url 只用于浏览器展示,禁止仅凭同步跳转更新付款状态或发货。

【主动查单】
使用服务端 POST https://pay.1kexiu.xyz/api.php,表单参数使用 pid 和 key 鉴权,key 的值来自 DUOCAI_PAY_KEY,不使用下单 sign 代替 key。
单笔查单:act=order,加 out_trade_no 或 trade_no。
订单列表:act=orders,加 page 和 limit,limit 范围 1 至 50。
商户状态:act=query。
成功响应字段必须按真实结构处理:act=query 返回 code,pid,key,active,money,username,type;act=order 返回 code,trade_no,out_trade_no,type,money,price,status,pay_time;act=orders 返回 code,total,data,其中 data 是订单数组。
status=0 表示未支付,status=1 表示支付成功,status=2 表示订单已超时;pay_time 是 Unix 秒级时间戳,未支付时可能为 null。只能在 code=1 且 status=1 时按“已支付”处理。
act=query 为兼容旧协议会在响应中包含 key,该字段等同敏感密钥;禁止把完整响应返回浏览器、APP、第三方日志或错误监控。
GET 查询仅为兼容旧客户端,不要在新代码中把密钥放进 URL。

【固定签名测试向量】
先为签名函数编写自动化测试,下面参数必须得到指定 MD5:
pay_key = test_pay_key_20260813
pid = M1234567890
type = alipay
out_trade_no = ORDER202608130001
notify_url = https://shop.example.com/pay/notify?channel=alipay&version=2
return_url = https://shop.example.com/pay/return?order=ORDER202608130001&source=pay
name = 测试订单
money = 1.00
expected_sign = beddd35092c3afea54c5218de0872502

测试还必须覆盖:空字符串、null、数字 0、money 分别为 1/1.0/1.00、中文商品名、空格、+、%、引号,以及 notify_url/return_url 含多个 &。同时验证签名后再做表单编码,确保实际发送字段与参与签名字段完全一致。

【实现与安全要求】
1. 先找到项目已有的订单模型、支付服务、回调路由和配置方式,做最小修改并保持现有代码风格。
2. PID 和密钥只从服务端环境变量或安全配置读取;日志不得记录密钥、完整签名原文或完整回调 Token。
3. 不关闭 TLS 证书验证,不使用浮点数直接比较金额;金额使用 decimal 或整数分,并按业务规则精确比较。
4. 请求超时或响应不确定时保留原 out_trade_no 和全部原始业务参数,在数秒内重复查单;当前平台不承诺下单请求幂等,禁止并发重试或立即更换订单号。
5. 当前接口域名是生产环境,没有独立沙箱。未经我明确确认,不调用会创建订单或产生资金行为的 /mapi.php、/submit.php,不进行真实付款、退款、发货或生产数据写入测试;默认只做固定签名向量、Mock 回调、Mock 查单和本地自动化测试。
6. 如果现有项目规则与接口要求冲突,先指出具体文件和冲突原因,再给出保持兼容的最小修复,不要自行猜测。

【完成后请交付】
1. 说明修改了哪些文件、每个文件做了什么。
2. 给出需要配置的环境变量和部署步骤。
3. 给出签名、下单响应、回调幂等和主动查单的自动化测试结果。
4. 明确哪些内容已经实际验证、哪些因为没有真实付款而尚未验证。
5. 提供一个安全的联调检查清单;如果失败,输出脱敏后的参数名称、排序结果和请求编号,但绝不输出真实密钥。

现在请先检查我的项目结构和现有支付实现,然后直接完成最小范围接入。如果你不能直接读取或修改项目,就按我的技术栈输出可复制的完整文件,并明确每个文件的放置路径。
10

CODE EXAMPLES

可直接改造的服务端示例

示例均完成“过滤 → 排序 → 拼接 → MD5”。请把凭证放入环境变量,生产环境不要关闭 TLS 证书校验。

PHP 8+:签名并 API 下单
<?php
$apiBase = 'https://pay.1kexiu.xyz';
$payKey  = getenv('DUOCAI_PAY_KEY');

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

$params = [
    'pid'          => getenv('DUOCAI_PID'),
    'type'         => 'alipay',
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'notify_url'   => 'https://shop.example.com/pay/notify?channel=alipay&version=2',
    'return_url'   => 'https://shop.example.com/pay/return?order=ORDER_202608130001&source=pay',
    'name'         => '测试商品',
    'money'        => '0.01',
];
$params['sign'] = makeSign($params, $payKey);
$params['sign_type'] = 'MD5';

$ch = curl_init($apiBase . '/mapi.php');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    // 先用原始字段值签名,再编码发送;不要先对 URL 做 htmlspecialchars。
    CURLOPT_POSTFIELDS => http_build_query($params),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 15,
]);
$result = json_decode(curl_exec($ch), true, 512, JSON_THROW_ON_ERROR);
curl_close($ch);
if (($result['code'] ?? -1) !== 1) {
    throw new RuntimeException($result['msg'] ?? '下单失败');
}
// 优先使用实际存在的字段:payurl 或 qrcode
$paymentValue = $result['payurl'] ?? $result['qrcode'] ?? null;

回调验签最小逻辑

伪代码(适用于任意语言)
params = merge_query_and_form_params(request)
if make_sign(params, PAY_KEY) != params.sign:
    return "fail"
if params.trade_status != "TRADE_SUCCESS":
    return "fail"
order = find_local_order(params.out_trade_no)
if order is null or order.pid != params.pid or order.money != params.money:
    return "fail"
mark_paid_once(order, params.trade_no)  // 必须幂等
return "success"                       // 纯文本
11

GO LIVE

上线前检查清单

仍有对接问题?带上订单号和脱敏后的请求参数,开发群里更容易快速定位。
加入 QQ 群 100687286