
做收款接入时,一个容易漏掉的问题是:用户回到了“支付完成”页面,后台却还没有可靠的付款结果。这时就发货,会把浏览器跳转当成收款凭据。
我是 8码分享团队成员。这篇结合我们当前公开的易支付兼容接口说明,整理接入时的几个判断点。8码分享支持个人微信、支付宝收款,付款确认后自动发货;外部应用接入仍以账号接口权限、实际可用通道为准。下文使用演示参数,只验证签名计算,没有执行真实下单或付款。
创建订单成功,只说明平台接收了订单,并给出了收银页地址。
买家回到 return_url,只说明浏览器完成了一次跳转。不能因为访问了这个地址,就把订单改成已付款。
真正用于交付的结果来自服务端:收到通知后完成验签和订单核对,或通过服务端查单确认付款。无论走哪条路,都要和创建订单时保存的数据比对。
8码分享的 /mapi.php 接受 POST 表单,Content-Type 是 application/x-www-form-urlencoded,不是 JSON。创建订单后,保存自己的 out_trade_no、平台 trade_no、原始金额、付款方式和通知地址,再打开返回的 payurl。
金额建议以整数“分”存储,发送时转换成两位小数字符串,避免浮点计算把 10.01 变成另一笔金额。个人收款要求买家填写收银页显示的精确金额,一分钱也不能有差异;接入方也不能为了避开订单冲突而自行增减金额。
请求超时不等于下单失败。重试时保留原订单号和原始参数,不要每重试一次就生成一笔新订单。
签名前移除 sign、sign_type 和空值,按参数名 ASCII 升序排列,用 & 连接,再直接追加密钥。先签名,再做表单编码。字符串 "0" 不能当作空值删除。
下面只演示签名函数,输入是已经解析、且拒绝了同名重复参数的单值字符串字典。secret 是公开测试密钥,不是任何账号的商户密钥。
import hashlib
def sign(params, key):
pairs = [
f"{k}={v}" for k, v in sorted(params.items())
if k not in ("sign", "sign_type") and v not in (None, "")
]
raw = "&".join(pairs) + key
return hashlib.md5(raw.encode("utf-8")).hexdigest()
params = {
"money": "10.01", "pid": "10000", "name": "测试商品",
"empty": "", "sign_type": "MD5"
}
assert sign(params, "secret") == "2b2da921f78d3e3e8fbe2631eb0df3ae"
这组参数的待签名原文是 money=10.01&name=测试商品&pid=10000secret。若先把中文做 URL 编码再签名,结果会不同。实际接入请从服务端配置读取密钥,不要放在网页、截图或公开日志中。
平台通过 GET 请求 notify_url。接收时拒绝同名重复参数,按文档验签,再用原始订单核对商户号、商户订单号、平台订单号、付款方式、金额及 TRADE_SUCCESS 状态。
不能把通知里的金额同时当成“收到的金额”和“预期金额”。预期值必须来自你自己的订单记录。
如果 notify_url 自带查询参数,先按保存的原始通知地址核对这些字段,再按文档从平台签名字段中移除;也可以在初次接入时使用没有额外查询参数的通知地址,减少歧义。
验签失败、金额不符或找不到订单,都不能发货。通知只提供了收款线索、平台尚未确认付款时,同样不能自行标记成功。
通知可能重试。交付不能写成“每来一个请求就发一张卡”。先以订单号建立唯一处理约束,事务内更新订单状态、分配库存并记录交付任务。若交付依赖邮件或其他外部服务,再用唯一任务号重试外部动作,避免重复发送。
处理已可靠提交,或确认同一笔合法通知已经处理过后,返回 HTTP 200,正文只写 success。不要返回 JSON、HTML 或登录页。处理失败则不要提前回复 success,否则平台可能认为通知已成功接收。
/api.php 的订单查询建议由后端 POST 表单调用。这里的 key 是凭据,不要放到浏览器地址栏或公开日志。
返回 code=1 表示查询请求成功,不表示买家已经付款。还要检查 status=1,并核对原始订单信息;status=0 时不能交付。
本文只完成了公开测试向量的离线核验。正式启用前,仍需要在自己的接口权限和通道条件下完成真实付款、通知重试及交付验收。
完整字段、通知校验示例和接入规则见 8码分享易支付接入文档。文档公开可读,不包含商户密钥。参数含义和后续变化以当前文档为准。
暂无关联产品