0ae7769abea5726e8bc81e4cbb2207c178873bff
- gateway: TestSettleConcurrentSameOrder 真并发钉住 MarkAttemptPaid 行锁不变量 (文件型 sqlite,规避 in-memory cache=shared 的 SQLITE_LOCKED_SHAREDCACHE)。 - gateway: e2e_fullchain_test.go 补下单→回调→settle→webhook 实际 HTTP 投递→ delivered 整链(httptest server + 真实 store.WebhookStore/webhook.Notifier)。 - reconcile: main.go 装配抽到 reconcile.Assemble(+Runner.TaskNames 访问器), 补 assembly_test.go 钉住 7 个后台任务全部注册 + crypto 缺渠道/interval=0 分支。 - alipay: 补验签健壮性(未知多余字段/字段乱序/中文unicode/空值字段),意外定位 vendor Encoder 对空值字段的真实语义与官方文档描述不同,已记录在测试注释。 - money: 补 TestZeroDecimalCurrencyJPYNotSupported,记录 JPY/KRW 当前未注册进 exponents(接入会先踩 ErrUnknownCurrency,不是乘除法坑)。
pay — 独立支付服务
多业务 × 多渠道的收款服务。支付宝(电脑网站支付)+ 微信(V3 Native 扫码)(微信无沙箱,待真实商户号配置后可用)。 栈:Go 1.26 · Gin · GORM(SQLite 纯 Go 驱动,零 cgo)。
目录结构
pay/
├── main.go # 启动:配置→DB→迁移→seed→路由→查单兜底
├── config/ # viper 配置(config.yaml + 环境变量覆盖)
├── internal/
│ ├── model/ # Merchant 商户凭证 / Product 套餐 / Order 订单 / NotifyLog 回调日志
│ ├── channel/ # Channel 渠道统一接口 + alipay 实现 + wechat 占位 + Registry 缓存
│ ├── service/ # OrderService:下单 / 回调验签入账 / 查单兜底
│ ├── handler/ # HTTP:收款页·套餐·下单·回调·查状态
│ ├── router/ # 路由装配
│ └── util/ # 响应 / 金额(分) / 订单号生成
├── web/ # result.html 支付结果页 · qrcode 二维码渲染
└── docs/ # 设计文档(index.html 索引)
核心约定
- 金额以服务端套餐价为准,绝不信任前端传值。
- 回调必验签(支付宝公钥)+ 核对金额 + 幂等防重;成功才回
success。 - 到账以异步通知(notify)为准,同步跳转仅展示;另有
query_sync主动查单兜底。 - 多商户 / 多渠道:往
merchants表加行即可,主流程不变。
接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /result?out_trade_no= |
结果页(return_url 落地) |
| GET | /qrcode?text= |
把码串渲染成二维码 PNG(微信 code_url / 支付宝扫码) |
| GET | /health |
健康检查 |
| GET | /api/v1/products |
上架套餐列表 |
| POST | /api/v1/orders |
网页支付下单,返回 pay_url(支付宝电脑网站支付跳转) |
| POST | /api/v1/orders/qr |
扫码下单,返回 qr_code 码串(微信 Native / 支付宝当面付) |
| GET | /api/v1/orders/:out_trade_no |
查订单状态(结果页轮询) |
| POST | /api/v1/notify/alipay |
支付宝异步回调 |
| POST | /api/v1/notify/wechat |
微信异步回调(V3 解密验签) |
沙箱联调步骤
- 到 支付宝沙箱 拿 APPID / 应用私钥 / 支付宝公钥(公钥模式)。
- 编辑
config/config.yaml的alipay_sandbox:enabled: true,填好app_id,私钥/公钥建议走环境变量:export ALIPAY_APP_PRIVATE_KEY="...应用私钥..." export ALIPAY_PUBLIC_KEY="...支付宝公钥..." - 启动:
首次会自动建库、upsert 沙箱商户、补两个测试套餐(0.01 / 0.02 元)。
go run . - 浏览器开
http://localhost:8080→ 选套餐 → 跳支付宝沙箱收银台 → 用沙箱买家账号扫码/登录付款。
⚠️ 异步回调需公网可达:
notify_url由server.base_url拼成。本机只测「下单→跳收银台」用localhost即可;要验证notify回调到账,需把base_url换成内网穿透域名(frp/ngrok 映射到本机 8080),或部署到有公网的服务器。本机看不到回调时,query_sync主动查单会兜底补记到账。
构建部署
一键发布到 ali(推荐):
./deploy.sh # 交叉编译 linux/amd64 → 传 ali → 备份 → 重启 pay → 探活(失败自动回滚)
手动构建:
go build -o payd . # 纯 Go,无需 gcc,可直接拷到阿里云 Linux 运行
生产改 config.yaml:server.mode: release、base_url 为正式 HTTPS 域名、商户 production: true;密钥走环境变量(/etc/pay/pay.env,Bitwarden 灌),不入库。
Description
Languages
Go
98.7%
Shell
0.8%
HTML
0.5%