Payment · Design

支付宝收款页 · 支付时序图与架构

PC 网页支付(alipay.trade.page.pay)· 固定套餐 · Go(Gin)+GORM · 沙箱联调阶段。本文说清楚一笔支付的完整时序、「独立服务」与「集成进 jiu」的区别,以及独立服务要不要建库、存哪些数据。

一、一笔支付的完整时序

客户浏览器 收款页 收款服务后端 Go · Gin · GORM 支付宝网关 收银台 / 服务端 客户手机 支付宝 App ① POST /order/create(选定套餐) 生成 out_trade_no · 订单落库(待支付) ② alipay.trade.page.pay(RSA2 加签下单) 返回收银台跳转 URL / 表单 ③ 返回跳转链接给浏览器 ④ 跳转支付宝收银台 · 展示付款二维码 ⑤ 扫码 · 确认付款 ⑥ 异步通知 notify_url(POST) 验签 + 核对金额 + 核对订单号 + 防重复通知 → 更新订单为「已支付」 应答 "success"(告知已处理,否则会重发) ⑦ 浏览器同步跳转 return_url 展示「支付成功」结果页 ⑧(兜底)alipay.trade.query 主动查单对账
请求(同步调用) 应答 / 跳转返回 兜底主动查单 后端处理 / 备注
两条“到账”路径,以异步为准:⑦ 同步跳转(return_url)只是把用户带回页面,不能作为到账依据(用户可能中途关页面)。真正确认收款的是 ⑥ 异步通知(notify_url)。两条都可能丢,所以 ⑧ 用 alipay.trade.query 主动查单兜底。订单状态以「⑥验签通过 / ⑧查单成功」为准。

二、整体架构(沙箱阶段)

三个角色、五个核心接口,全程不需要客户登录:

三、独立服务 vs 集成进 jiu

两种落地方式,差别核心就一句话:要不要复用 jiu 已有的数据库和 JWT 鉴权后台。

独立服务(ai 下新建)集成进 jiu(jiu/backend 加模块)
代码位置/Users/wangjia/code/ai 下新建 pay/,独立 Go 进程jiu/backend 里加 handler/service/model/router
数据库自建一套(SQLite 单文件起步即可,或 MySQL)直接复用 jiu 的 GORM/MySQL,订单表加进去
鉴权后台要自己做登录鉴权(或第一版不做后台)白送——复用 jiu 的 JWT 中间件
部署单独一个二进制 + systemd,独立上线跟 jiu 一起构建/部署,绑在一条流水线
耦合度与 jiu 完全解耦,可给任意业务(如 yanmei)用与 jiu 强耦合,jiu 是进销存系统,收款无关会让它变臃肿
适合收款是独立业务、或要给多个项目复用收款本就是 jiu 业务的一环(订单/客户都在 jiu 里)
开发量核心一致,多了「建库 + 鉴权(若要后台)」核心一致,省掉建库和鉴权
判断方法:收款数据要不要跟 jiu 的客户/订单/库存打通?
· 要打通 → 集成进 jiu,省事且数据一体。
· 不相干(给 yanmei 等独立收款)→ 独立服务,第一版连后台都不做,最快跑通沙箱。

四、独立服务需要建数据库吗?

需要。哪怕一张表也得有。SQLite 单文件起步就够(零运维),后面要并发/多实例再换 MySQL。

不能「只下单不落库」,原因是支付的钱和安全全靠订单记录兜底:

为什么必须存不存会怎样
回调核对金额防篡改⑥ 收到通知时要拿订单原始金额比对,没存就无法判断金额是否被改 → 可被刷单
幂等防重复入账支付宝会重发通知,没有订单状态记录就会重复发货/重复确认
查单对账兜底⑧ 主动查单需要 out_trade_no,没存就无单可查,回调一丢就丢钱
给客户/商家看结果结果页、订单列表、退款都要读订单,无库无从谈起

五、存哪些数据

① 订单表 orders(核心,必建)

字段类型说明
iduint PK自增主键
out_trade_novarchar · 唯一商户订单号(我们生成,全局唯一),贯穿下单/回调/查单
subjectvarchar套餐/商品名称(展示在收银台)
amountdecimal(10,2)订单金额(元),回调时用它核对防篡改
statusvarchar / enum待支付 / 已支付 / 已关闭 / 已退款
trade_novarchar支付宝交易号(回调/查单回填)
buyer_logon_idvarchar买家支付宝账号(可选,回调带回,脱敏存)
paid_atdatetime支付完成时间(回调回填)
product_iduint指向套餐表(固定套餐场景)
client_ip / remarkvarchar下单来源 IP、备注(可选)
created_at / updated_atdatetimeGORM 自动维护

② 套餐表 products(固定套餐场景,建议建)

固定商品/套餐就是从这张表来,避免金额写死在前端被篡改 —— 下单时金额一律以服务端这张表为准,不信任前端传来的价格。

字段类型说明
iduint PK套餐 ID(前端只传这个)
namevarchar套餐名(如「基础版 / 年付」)
pricedecimal(10,2)价格(元),服务端权威价格
descriptionvarchar套餐说明
activebool是否上架
sortint展示排序

③ 回调日志 notify_logs(可选,建议)

把每条异步通知的原始报文、验签结果、处理结果存一份,方便排查对账纠纷与审计。非必须,但出问题时极其有用。

安全红线(无论独立还是集成都一样):
1. 金额、套餐价格一律以服务端 DB 为准,绝不信任前端传值;
2. 回调 必须验签(支付宝公钥)+ 核对 out_trade_nototal_amount
3. 回调幂等:同一 trade_no 只处理一次;
4. 处理成功才返回 success,否则支付宝会按策略重发。

六、结论与下一步

定了「独立 / 集成」后,我就把它写成正式设计文档 + 实现计划,照 jiu 的 Go(Gin)+GORM 约定搭骨架,用支付宝沙箱跑通全链路。