feat(v2): 多币种 product(ProductPrice 分币价目 + v1 元回退)+ 结算币种由渠道能力驱动

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-10 14:20:12 +08:00
parent abf6a43b8c
commit 585133532c
8 changed files with 128 additions and 54 deletions
+40 -17
View File
@@ -3,31 +3,54 @@ package gateway_test
import (
"testing"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/wangjia/pay/internal/gateway"
"github.com/wangjia/pay/internal/model"
)
func TestDBProductResolver(t *testing.T) {
db, err := gorm.Open(sqlite.Open("file:prodtest?mode=memory&cache=shared"),
&gorm.Config{Logger: logger.Default.LogMode(logger.Silent), TranslateError: true})
if err != nil {
t.Fatalf("open: %v", err)
}
if err := db.AutoMigrate(&model.Product{}); err != nil {
t.Fatalf("migrate: %v", err)
}
db := model.OpenTestDB(t)
db.Create(&model.Product{Name: "标准年付", Price: "299.00", BizCode: "annual_standard", Active: true})
r := gateway.NewDBProductResolver(db, "CNY")
minor, cur, subject, bizCode, err := r.Resolve("annual_standard")
if err != nil || minor != 29900 || cur != "CNY" || subject != "标准年付" || bizCode != "annual_standard" {
t.Fatalf("resolve = %d %s %s %s %v", minor, cur, subject, bizCode, err)
r := gateway.NewDBProductResolver(db)
minor, subject, bizCode, err := r.Resolve("annual_standard", "CNY")
if err != nil || minor != 29900 || subject != "标准年付" || bizCode != "annual_standard" {
t.Fatalf("resolve = %d %s %s %v", minor, subject, bizCode, err)
}
if _, _, _, _, err := r.Resolve("ghost"); err != gateway.ErrProductNotFound {
if _, _, _, err := r.Resolve("ghost", "CNY"); err != gateway.ErrProductNotFound {
t.Fatalf("缺套餐应 ErrProductNotFound, got %v", err)
}
}
func TestDBProductResolverMultiCurrency(t *testing.T) {
db := model.OpenTestDB(t)
// 一个套餐:CNY 走 v1 Price 元 string 回退;USD/USDT 走 ProductPrice。
p := model.Product{Name: "Pro 年付", Price: "199.00", BizCode: "pro_year", Active: true, MerchantID: 1}
if err := db.Create(&p).Error; err != nil {
t.Fatal(err)
}
rows := []model.ProductPrice{
{ProductID: p.ID, Currency: "USD", AmountMinor: 2999},
{ProductID: p.ID, Currency: "USDT", AmountMinor: 29990000},
}
if err := db.Create(&rows).Error; err != nil {
t.Fatal(err)
}
r := gateway.NewDBProductResolver(db)
// CNY 回退 Product.Price("199.00" 元 → 19900 分)
if amt, _, code, err := r.Resolve("pro_year", "CNY"); err != nil || amt != 19900 || code != "pro_year" {
t.Fatalf("CNY resolve = %d,%q,%v want 19900,pro_year,nil", amt, code, err)
}
// USD 走 ProductPrice
if amt, _, _, err := r.Resolve("pro_year", "USD"); err != nil || amt != 2999 {
t.Fatalf("USD resolve = %d,%v want 2999", amt, err)
}
// USDT 走 ProductPrice
if amt, _, _, err := r.Resolve("pro_year", "USDT"); err != nil || amt != 29990000 {
t.Fatalf("USDT resolve = %d,%v want 29990000", amt, err)
}
// 不支持的币种 → ErrProductNotFound
if _, _, _, err := r.Resolve("pro_year", "JPY"); err != gateway.ErrProductNotFound {
t.Fatalf("JPY resolve err = %v want ErrProductNotFound", err)
}
}