支付宝当面付
安全边界
- 禁止输出、记录或提交真实 App ID、应用私钥、应用公钥、支付宝公钥、商户 ID、证书、
.env、回调原文或完整订单数据。 - 要求用户通过服务端环境变量或部署平台 Secret 提供凭据,不要让用户在对话中粘贴密钥。
- 只在诊断中输出参数名、错误码、支付状态和脱敏标识;标识最多保留前 4 位与后 4 位。
- 禁止通过关闭请求签名、通知验签、金额校验或幂等检查来绕过错误。
- 使用占位符编写示例,例如
your-app-id、ALIPAY_PRIVATE_KEY和https://example.com/api/payment/notify/alipay。
先检查再修改
搜索现有支付实现、订单模型和权益发放路径,不要猜文件位置:
rg -n "alipay|trade\.precreate|trade\.query|notify|callback|order.*status|credit|entitlement" src识别运行时、框架、金额单位、订单状态机、数据库事务能力和已有支付抽象。
只检查环境变量是否存在和长度是否合理,禁止打印值;确认密钥文件、证书和
.env未被 Git 跟踪。优先使用支付宝官方 SDK;仅在 Cloudflare Workers 等运行时不兼容时,用 Web Crypto 实现最小协议层。
修改前先读 协议与排障参考,尤其是请求签名与通知验签的不同过滤规则。
实现流程
1. 建立配置边界
- 仅从服务端读取
ALIPAY_APP_ID、ALIPAY_PRIVATE_KEY、ALIPAY_PUBLIC_KEY、网关和通知地址。 - 明确区分三种材料:应用私钥用于请求加签;应用公钥上传支付宝开放平台;支付宝公钥用于响应与通知验签。
- 固定生产网关为官方配置值,显式区分沙箱与生产环境,禁止自动回退到另一个环境。
- 在启动或首次调用时验证配置形状,错误信息只指出缺少哪个变量或密钥格式,不回显内容。
2. 预创建二维码订单
- 先创建本地待支付订单,生成全局唯一且可追踪的
out_trade_no。 - 从服务端商品或套餐配置计算金额,不信任前端传入的金额、权益数量或标题。
- 调用
alipay.trade.precreate,至少提供out_trade_no、两位小数字符串total_amount、subject,并设置公开 HTTPSnotify_url。 - 对请求原文做 RSA2 加签。请求加签只排除
sign,必须保留sign_type=RSA2。 - 检查业务响应
code === "10000"且存在qr_code;向前端返回二维码内容和本地订单号,不返回密钥、签名原文或完整网关响应。 - 在前端用成熟 QR 库渲染
qr_code内容。不要用data:text/html跳转顶层页面,也不要把支付宝返回的表单 HTML 当二维码。
3. 处理异步通知
- 只接受支付宝规定的表单请求,按框架方式解析一次
application/x-www-form-urlencoded。 - 用支付宝公钥验证 RSA2 签名;不要使用应用公钥,也不要复用请求签名的参数过滤函数。
- 验签通过后再校验
app_id、out_trade_no、total_amount、订单归属和可用的卖家标识。 - 仅处理
TRADE_SUCCESS或TRADE_FINISHED,并调用统一的幂等“确认支付”服务。 - 成功完成后返回纯文本
success;处理失败返回非success,让支付宝按策略重试。
4. 主动查询与前端轮询
- 让二维码页面以本地订单号轮询自己的状态接口,限制频率、超时和订单所有权。
- 在通知延迟或丢失时调用
alipay.trade.query对账;只信任验签成功且订单、金额匹配的支付宝结果。 - 让通知与主动查询复用同一个幂等确认函数,禁止各自实现权益发放。
- 在订单已支付但权益缺失时执行可审计修复,不能因为状态已是 paid 就直接返回。
5. 保证幂等与一致性
- 在数据库事务中执行待支付到已支付的状态转换和权益流水写入。
- 为订单号与权益类型建立唯一约束,重复通知、重复轮询和并发请求只能发放一次。
- 保留必要的非敏感审计字段,例如支付宝交易号、金额、状态、确认来源和时间;不要保存完整回调。
- 拒绝金额、App ID、订单归属或商品快照不匹配的通知,并记录脱敏告警。
Cloudflare Workers 要点
- 使用
wrangler secret put或控制台 Secret 保存凭据,不要放入wrangler.toml的公开vars。 - 使用 Web Crypto 的
RSASSA-PKCS1-v1_5与SHA-256实现 RSA2;私钥导入为 PKCS#8,公钥导入为 SPKI。 - 兼容 Secret 中的真实换行和字面量
\n,但禁止在日志中输出标准化后的 PEM。 - 先对未编码的规范字符串签名,再做 URL/form 编码;禁止对已编码字符串加签或二次解码通知字段。
- 用
Asia/Shanghai生成网关要求的时间格式,并确认部署时钟与环境正确。
验证门槛
- 添加确定性单元测试,断言请求签名原文包含
sign_type=RSA2,只排除sign。 - 使用测试时动态生成的密钥对验证签名,不向仓库提交固定私钥或真实证书。
- 覆盖合法/非法通知、金额不匹配、App ID 不匹配、重复通知、重复轮询和“已支付但权益缺失”。
- 运行项目最快的类型检查、支付专项测试和构建;Cloudflare 项目再运行 Worker 构建检查。
- 部署前执行敏感信息扫描,并逐项检查暂存区;禁止在脏工作树中使用
git add .。 - 生产烟测只创建最低风险测试订单,确认能获得
qr_code、页面能显示二维码、支付后订单最终一致;不要在公开日志中粘贴响应。
排障顺序
- 先保留支付宝
code、sub_code、sub_msg和脱敏 trace ID,区分网关验签失败、业务失败与前端渲染失败。 - 遇到“验签出错”时,先比较规范字符串字段集合,再检查密钥角色和格式;不要先换密钥。
- 遇到“没有二维码”时,确认调用的是
alipay.trade.precreate且成功响应中读取的是qr_code。 - 遇到“支付成功未到账”时,依次检查通知可达性、通知验签、订单/金额校验、主动查询和幂等权益流水。
- 需要字段级诊断、测试矩阵或 Cloudflare 检查时,读取 协议与排障参考。