初始提交:岩美 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:
@@ -0,0 +1,69 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/wangjia/pay/internal/model"
|
||||
)
|
||||
|
||||
// Registry 按商户缓存已构造的渠道实例,避免每次下单都重新加载密钥。
|
||||
type Registry struct {
|
||||
db *gorm.DB
|
||||
mu sync.RWMutex
|
||||
cache map[uint64]Channel
|
||||
}
|
||||
|
||||
func NewRegistry(db *gorm.DB) *Registry {
|
||||
return &Registry{db: db, cache: make(map[uint64]Channel)}
|
||||
}
|
||||
|
||||
// ByMerchantID 取(或构造)指定商户的渠道。
|
||||
func (r *Registry) ByMerchantID(id uint64) (Channel, *model.Merchant, error) {
|
||||
var m model.Merchant
|
||||
if err := r.db.First(&m, "id = ? AND enabled = ?", id, true).Error; err != nil {
|
||||
return nil, nil, fmt.Errorf("商户不存在或已停用: %w", err)
|
||||
}
|
||||
ch, err := r.get(&m)
|
||||
return ch, &m, err
|
||||
}
|
||||
|
||||
// AlipayByAppID 异步回调时用 app_id 反查商户并取其渠道(用于验签)。
|
||||
func (r *Registry) AlipayByAppID(appID string) (Channel, *model.Merchant, error) {
|
||||
if appID == "" {
|
||||
return nil, nil, fmt.Errorf("回调缺少 app_id")
|
||||
}
|
||||
var m model.Merchant
|
||||
if err := r.db.First(&m, "channel = ? AND app_id = ? AND enabled = ?", "alipay", appID, true).Error; err != nil {
|
||||
return nil, nil, fmt.Errorf("找不到 app_id=%s 对应的支付宝商户: %w", appID, err)
|
||||
}
|
||||
ch, err := r.get(&m)
|
||||
return ch, &m, err
|
||||
}
|
||||
|
||||
func (r *Registry) get(m *model.Merchant) (Channel, error) {
|
||||
r.mu.RLock()
|
||||
if ch, ok := r.cache[m.ID]; ok {
|
||||
r.mu.RUnlock()
|
||||
return ch, nil
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
|
||||
ch, err := Build(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.mu.Lock()
|
||||
r.cache[m.ID] = ch
|
||||
r.mu.Unlock()
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
// Invalidate 商户凭证变更后清缓存(预留给管理接口)。
|
||||
func (r *Registry) Invalidate(id uint64) {
|
||||
r.mu.Lock()
|
||||
delete(r.cache, id)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
Reference in New Issue
Block a user