公司支付台 v0.1.6

对接文档

公开接口说明,无需后台登录。

易支付对接指南

服务根地址:https://pay.strcpy.cn。当前只支持支付宝(alipay)。

本页与 AI 对接 Skill 均无需登录。商户 PID、商户 KEY 从管理员提供的网站接入信息中获取,每个网站使用独立凭据。商户 KEY 用于易支付签名,与支付宝应用私钥不同。

1. 配置接入网站

管理员在「接入网站」创建网站,选择收款应用,登记通知与返回地址。接入方填写:

配置 填写内容
支付地址 / API 地址 https://pay.strcpy.cn,填写根地址,不追加 submit.php
商户 PID 该网站的 PID
商户 KEY / PKey 该网站的 KEY,保存在接入网站服务端
支付方式 alipay
通知地址 接入网站接收付款结果的完整 HTTP / HTTPS 地址
返回地址 用户付款后返回接入网站的完整 HTTP / HTTPS 地址

通知与返回地址登记时不带查询参数。实际请求可带查询参数,但协议、域名、端口、路径必须与登记值一致;路径末尾斜杠也要一致。通知必须公网可达,网关不跟随通知重定向,不向本机、内网及保留地址发送通知。

NewAPI

在 NewAPI 的易支付设置中填写以上根地址、PID、KEY,并启用支付宝。后台网站类型选择 NewAPI。

常用通知路径:/api/user/epay/notify、/api/subscription/epay/notify;订阅返回路径:/api/subscription/epay/return。充值返回路径因版本而异,例如 /console/log 或 /usage-logs。以实际版本下单请求里的完整地址为准,在支付后台登记;不要照搬其他站点的域名。

Sub2API

支付服务商选择 EasyPay,API 地址填写根地址,PID 和 PKey 填写网站凭据,支付方式选择 alipay,通道 ID 留空。本后台网站类型选择「通用易支付」。常用通知路径为 /api/v1/payment/webhook/easypay,返回路径为 /payment/result。

Sub2API 返回地址中的 order_id、out_trade_no、resume_token、status 查询参数在签名时原样保留。返回链接带有 status=success 不代表网关已确认付款,接入方仍需验签并查单或处理异步通知。

2. 接口与请求格式

接口 方法 行为
/submit.php GET / POST 成功后以 HTTP 303 跳转至收银台
/mapi.php GET / POST 成功返回 JSON,包含收银台链接
/api.php GET / POST 使用 PID / KEY 查询订单

三个接口同时支持 /xpay/epay/ 前缀,例如 /xpay/epay/submit.php。POST 下单使用 Content-Type: application/x-www-form-urlencoded;不接受 JSON 下单。POST 参数放在请求体中,不混入 URL 查询参数。相同一级参数不得重复。

下单参数

字段 必填 含义与限制
pid 是 网站商户 PID
type 是 固定为 alipay
out_trade_no 是 网站订单号,1–64 个英文字母、数字、下划线或短横线;同一网站唯一
name 是 商品名称,UTF-8,最长 127 字节;原样传给支付宝
money 是 元,十进制字符串,例如 10.06;最低 0.01,最多两位小数,且不超过网站单笔限额
notify_url 是 已登记的异步通知地址
return_url 是 已登记的付款返回地址
sign 是 小写 MD5 签名,32 位十六进制
sign_type 建议 MD5;省略时也按 MD5 校验
device 否 pc 或 mobile,选择支付页面;也会参考浏览器 User-Agent 与应用可用产品
param 否 透传字符串,最长 1024 字节;通知中原样返回
expiry_minutes 否 本次订单有效期,1–21600 分钟的整数;不传时使用网站配置,默认 30 分钟

金额不要使用浮点数计算后再直接拼接。有效期截止时间按分钟向上取整,可能额外增加不足 60 秒。同一网站订单号重复提交返回 HTTP 409;发生超时先查原订单,不要直接生成另一笔订单。

3. MD5 签名与字段扩展

  1. 取表单解码一次后的所有一级字段,排除一级 sign、sign_type 及值为空字符串的字段。
  2. 按字段名升序排列,拼接为 key=value&key=value,字段名大小写保留。
  3. 在拼接字符串末尾直接追加商户 KEY,不加 &key=。
  4. 对 UTF-8 字节计算 MD5,输出小写十六进制。
  5. 将签名加入参数后,再用标准表单编码生成请求体或查询字符串。

一级新增字段自动参与签名。URL、JSON 或 N 层嵌套数据作为一个完整字符串参与签名,不递归展开,不重新排序,也不再次解码。不要只签固定的几个字段;验签也必须包含收到的所有非空一级字段。

V1 对原始值使用 & 拼接,存在边界歧义。网关会拒绝值中 &字段名= 与同名保留字段取值冲突的请求。保留字段为 pid、type、money、out_trade_no、trade_no、trade_status、name、param、notify_url、return_url、expiry_minutes、sign、sign_type(边界检查不区分大小写)。普通 R&D、uid=5&plan=pro、非保留字段或重复值相同的合法参数可以使用。Sub2API 返回地址重复相同的 out_trade_no 可以通过。

回跳参数也不能与网关支付字段冲突。对于有歧义的业务数据,优先改用结构化 JSON 放入 param,或调整字段名;若必须对内层值编码,接入方需约定同一编码方式,不能对整个请求反复编码。

Python 下单示例

以下示例只生成请求体,不创建订单。将环境变量设为该网站实际配置后,将输出通过服务端 POST 发给 /mapi.php。PAY_RETURN_URL 可带接入网站需要的查询参数。

import hashlib
import os
from urllib.parse import urlencode

def epay_sign(fields, key):
    pairs = [f"{k}={fields[k]}" for k in sorted(fields)
             if k not in ("sign", "sign_type") and fields[k] != ""]
    return hashlib.md5(("&".join(pairs) + key).encode("utf-8")).hexdigest()

fields = {
    "pid": os.environ["PAY_PID"],
    "type": "alipay",
    "out_trade_no": os.environ["PAY_ORDER_NO"],
    "name": "API 额度充值",
    "money": "10.06",
    "notify_url": os.environ["PAY_NOTIFY_URL"],
    "return_url": os.environ["PAY_RETURN_URL"],
    "param": '{"user_id":"42","plan":"pro"}',
    "sign_type": "MD5",
}
fields["sign"] = epay_sign(fields, os.environ["PAY_KEY"])
print(urlencode(fields))

下单响应

/mapi.php 成功:

{"code":1,"msg":"success","trade_no":"CPexample","payurl":"https://pay.strcpy.cn/checkout/CPexample","money":"10.06"}

跳转用户到 payurl 完成付款。trade_no 是网关支付订单号,并非支付宝交易号。获得链接仅代表网关创建了待支付订单。

下单失败返回非 2xx HTTP 状态,响应体为文本。常见情况:400 参数、金额或回调地址错误;401 PID 或签名无效;409 订单重复或收款应用不可用;415 下单 POST 使用了错误 Content-Type。接入方先判断 HTTP 状态,再解析成功 JSON。

4. 异步通知与付款后返回

网关确认付款后,POST 表单到订单原始 notify_url,包含 pid、trade_no、out_trade_no、type、name、money、trade_status=TRADE_SUCCESS、sign_type=MD5、sign;非空 param 也包含在内。

接入网站按以下顺序处理:

  1. 拒绝重复一级参数,按本站 KEY 验证全部一级字段签名。
  2. 验证 PID、网站订单号、金额、支付方式和 TRADE_SUCCESS;金额按整数分比较。
  3. 在数据库事务内将订单标记为已支付并完成余额或订阅更新。以订单号做唯一约束,重复通知不重复入账。
  4. 事务成功提交后返回 HTTP 200,响应体为纯文本 success。已处理的合法重复通知也返回 success。

通知失败会自动重试,间隔逐步增加;管理员也可手动重试。不要在写入失败时返回 success。

用户回跳为 GET,网关会将付款参数与原 return_url 查询参数合并并重新签名。验签必须包含所有合并后的非空一级字段。浏览器跳转本身不能证明付款,也不能仅靠 status=success、前端状态或收银台链接给用户入账。

付款状态与通知状态独立:已付款但通知失败仍是已付款。异步通知只发送付款结果,不提供退款扣回事件;退款需人工核对,业务系统自行处理退款后的余额和权益。

5. 订单查询

服务端 POST 到 https://pay.strcpy.cn/api.php,使用表单字段:

字段 含义
act 固定 order
pid 网站 PID
key 网站 KEY;该接口直接校验凭据,不使用下单 MD5 签名
out_trade_no 网站订单号;与 trade_no 至少传一个
trade_no 网关支付订单号,通常以 CP 开头;两者都传时必须对应同一订单

成功返回 code=1、msg、pid、trade_no、out_trade_no、name、money、type、status、trade_status。只查询当前 PID 的订单。此接口返回网关已记录的付款状态,不实时调用支付宝。

status trade_status 含义
0 WAIT_BUYER_PAY 尚未确认付款
1 TRADE_SUCCESS 已确认付款,与网站通知是否成功无关
0 TRADE_CLOSED 已关闭或已过期

查单业务错误使用 HTTP 200、code=-1、msg。因此 HTTP 200 不等于业务成功。退款待核对订单也返回 code=-1(订单存在退款,请人工核对),不能当作可再次发货的已支付订单。没有退款、提现或修改订单接口。

6. 联调与常见问题

本页描述当前服务实现。接入资料中的网站凭据、账号权限及支付宝应用资料仍由管理员管理,公开文档不包含它们。