初始提交:岩美 pay 收款服务(支付宝当面付 + 多商户多渠道架构)

- Go/Gin/GORM + 纯 Go SQLite(无 cgo)
- Channel 多渠道接口:支付宝当面付(precreate)/电脑网站支付(page.pay) 已实现,微信占位
- 多商户 merchants 表,回调验签+金额核对+幂等+查单兜底
- 收款页/结果页/二维码端点;docs/ 设计文档与部署 Runbook
- 密钥走环境变量/Bitwarden,不入库

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-25 07:21:48 +08:00
commit 6da6964451
29 changed files with 2205 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
package model
import "time"
// Base 公共字段(与 jiu 约定一致)
type Base struct {
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
+24
View File
@@ -0,0 +1,24 @@
package model
// Merchant 一个「业务 × 渠道」的收款凭证。
// 多业务、多支付宝账户、以后加微信,都是往这张表加行 —— 主流程不变。
type Merchant struct {
Base
Code string `gorm:"uniqueIndex;size:64" json:"code"` // 业务标识:yanmei / jiu ...
Name string `gorm:"size:128" json:"name"` // 展示名
Channel string `gorm:"index;size:16" json:"channel"` // alipay | wechat
Production bool `json:"production"` // false=沙箱
Enabled bool `gorm:"default:true" json:"enabled"`
// —— 支付宝 ——(app_id 用于异步回调反查商户)
AppID string `gorm:"index;size:64" json:"app_id"`
AppPrivateKey string `gorm:"type:text" json:"-"` // 应用私钥,绝不下发
AlipayPublicKey string `gorm:"type:text" json:"-"` // 支付宝公钥(验签用)
// —— 微信 ——(预留,渠道尚未实现)
MchID string `gorm:"size:64" json:"mch_id,omitempty"`
WxAppID string `gorm:"size:64" json:"wx_app_id,omitempty"`
APIv3Key string `gorm:"type:text" json:"-"`
CertSerial string `gorm:"size:128" json:"-"`
WxPrivateKey string `gorm:"type:text" json:"-"`
}
+11
View File
@@ -0,0 +1,11 @@
package model
// NotifyLog 异步回调审计日志。每条通知的原始报文 + 验签/处理结果都留一份,便于排查对账纠纷。
type NotifyLog struct {
Base
Channel string `gorm:"size:16" json:"channel"`
OutTradeNo string `gorm:"index;size:64" json:"out_trade_no"`
Verified bool `json:"verified"` // 验签是否通过
Result string `gorm:"size:32" json:"result"` // processed | duplicate | amount_mismatch | verify_failed | not_found | ignored
Raw string `gorm:"type:text" json:"raw"` // 原始表单报文
}
+28
View File
@@ -0,0 +1,28 @@
package model
import "time"
type OrderStatus string
const (
OrderPending OrderStatus = "pending" // 待支付
OrderPaid OrderStatus = "paid" // 已支付
OrderClosed OrderStatus = "closed" // 已关闭/超时
OrderRefunded OrderStatus = "refunded" // 已退款
)
// Order 一笔收款订单。OutTradeNo 是我们生成的商户订单号,贯穿下单/回调/查单。
type Order struct {
Base
OutTradeNo string `gorm:"uniqueIndex;size:64;not null" json:"out_trade_no"`
MerchantID uint64 `gorm:"index;not null" json:"merchant_id"`
Channel string `gorm:"size:16" json:"channel"`
ProductID uint64 `json:"product_id"`
Subject string `gorm:"size:128" json:"subject"`
Amount string `gorm:"size:20;not null" json:"amount"` // 权威金额,回调/查单核对用
Status OrderStatus `gorm:"index;size:16;not null" json:"status"`
TradeNo string `gorm:"index;size:64" json:"trade_no"` // 支付宝交易号
BuyerLogonID string `gorm:"size:128" json:"buyer_logon_id"`
PaidAt *time.Time `json:"paid_at"`
ClientIP string `gorm:"size:64" json:"client_ip"`
}
+13
View File
@@ -0,0 +1,13 @@
package model
// Product 固定套餐/商品。价格以本表为准(服务端权威价),绝不信任前端传值。
// 金额用 string 存(如 "0.01"),与支付宝 total_amount 口径一致,避免浮点误差。
type Product struct {
Base
MerchantID uint64 `gorm:"index;not null" json:"merchant_id"`
Name string `gorm:"size:128;not null" json:"name"`
Description string `gorm:"size:255" json:"description"`
Price string `gorm:"size:20;not null" json:"price"` // 元,两位小数
Active bool `gorm:"default:true" json:"active"`
Sort int `json:"sort"`
}