fix: 包年 CNY / 永久 USD 分币种结账
按档位分别拼 Waffo currency,避免永久商品用 CNY 报不可用。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
d4c5637168
commit
e8d98c993f
3 files changed
+60
-20
No files matched your search
+4
-2
@@ -42,14 +42,16 @@ DB_NAME=db.sqlite3
|
||||
# Waffo 支付(OpsXShell 收银台 · pancake.waffo.ai)
|
||||
# ============================================
|
||||
# 店铺: https://pancake.waffo.ai/store/opsxshell-pwsi6m4n
|
||||
# 包年一次性: PROD_1BIezCi932IbgCzRIV8hcJ (type=onetime)
|
||||
# 永久买断: PROD_0pcnzzT4nalEkkcdyIUoD5 (type=onetime)
|
||||
# 包年一次性: PROD_1BIezCi932IbgCzRIV8hcJ (type=onetime, currency=CNY)
|
||||
# 永久买断: PROD_0pcnzzT4nalEkkcdyIUoD5 (type=onetime, currency=USD)
|
||||
# 勿设置单一 WAFFO_CHECKOUT_URL,否则无法按档位分流商品
|
||||
WAFFO_STORE_SLUG=opsxshell-pwsi6m4n
|
||||
WAFFO_PRODUCT_ID=PROD_0pcnzzT4nalEkkcdyIUoD5
|
||||
WAFFO_PRODUCT_ID_YEARLY=PROD_1BIezCi932IbgCzRIV8hcJ
|
||||
WAFFO_PRODUCT_ID_PERPETUAL=PROD_0pcnzzT4nalEkkcdyIUoD5
|
||||
WAFFO_CURRENCY=CNY
|
||||
WAFFO_CURRENCY_YEARLY=CNY
|
||||
WAFFO_CURRENCY_PERPETUAL=USD
|
||||
# test | live(test 时自动附加 test=true)
|
||||
WAFFO_ENVIRONMENT=test
|
||||
# 支付成功回跳(桌面端 deep link;CreateOrder/Checkout 的 return_url 可覆盖)
|
||||
|
||||
@@ -9,14 +9,16 @@ import (
|
||||
|
||||
// WaffoConfig hosted checkout settings for pancake.waffo.ai // Logan.Li
|
||||
type WaffoConfig struct {
|
||||
CheckoutURL string
|
||||
StoreSlug string
|
||||
ProductID string
|
||||
ProductIDYearly string
|
||||
ProductIDPerpetual string
|
||||
Currency string
|
||||
Environment string
|
||||
SuccessURL string
|
||||
CheckoutURL string
|
||||
StoreSlug string
|
||||
ProductID string
|
||||
ProductIDYearly string
|
||||
ProductIDPerpetual string
|
||||
Currency string // fallback / default
|
||||
CurrencyYearly string
|
||||
CurrencyPerpetual string
|
||||
Environment string
|
||||
SuccessURL string
|
||||
}
|
||||
|
||||
// LoadWaffoConfig reads WAFFO_* env vars (same names as OpsXShell legacy license-server). // Logan.Li
|
||||
@@ -33,6 +35,19 @@ func LoadWaffoConfig() WaffoConfig {
|
||||
if currency == "" {
|
||||
currency = "CNY"
|
||||
}
|
||||
currencyYearly := strings.TrimSpace(os.Getenv("WAFFO_CURRENCY_YEARLY"))
|
||||
if currencyYearly == "" {
|
||||
currencyYearly = currency
|
||||
}
|
||||
currencyPerpetual := strings.TrimSpace(os.Getenv("WAFFO_CURRENCY_PERPETUAL"))
|
||||
if currencyPerpetual == "" {
|
||||
// 永久默认 USD(与 pancake 商品一致);可用环境变量覆盖。 // Logan.Li
|
||||
if currency == "CNY" {
|
||||
currencyPerpetual = "USD"
|
||||
} else {
|
||||
currencyPerpetual = currency
|
||||
}
|
||||
}
|
||||
defaultProduct := strings.TrimSpace(os.Getenv("WAFFO_PRODUCT_ID"))
|
||||
yearly := strings.TrimSpace(os.Getenv("WAFFO_PRODUCT_ID_YEARLY"))
|
||||
perpetual := strings.TrimSpace(os.Getenv("WAFFO_PRODUCT_ID_PERPETUAL"))
|
||||
@@ -49,6 +64,8 @@ func LoadWaffoConfig() WaffoConfig {
|
||||
ProductIDYearly: yearly,
|
||||
ProductIDPerpetual: perpetual,
|
||||
Currency: currency,
|
||||
CurrencyYearly: currencyYearly,
|
||||
CurrencyPerpetual: currencyPerpetual,
|
||||
Environment: env,
|
||||
SuccessURL: success,
|
||||
}
|
||||
@@ -103,6 +120,23 @@ func productIDForPlan(cfg WaffoConfig, planType string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func currencyForPlan(cfg WaffoConfig, planType string) string {
|
||||
switch strings.TrimSpace(planType) {
|
||||
case "yearly", "monthly":
|
||||
if cfg.CurrencyYearly != "" {
|
||||
return cfg.CurrencyYearly
|
||||
}
|
||||
case "perpetual", "lifetime":
|
||||
if cfg.CurrencyPerpetual != "" {
|
||||
return cfg.CurrencyPerpetual
|
||||
}
|
||||
}
|
||||
if cfg.Currency != "" {
|
||||
return cfg.Currency
|
||||
}
|
||||
return "CNY"
|
||||
}
|
||||
|
||||
func checkoutTypeForPlan(planType string) string {
|
||||
// OpsXShell 包年 / 永久均为 Waffo onetime 商品(包年不再走 subscription)。 // Logan.Li
|
||||
_ = planType
|
||||
@@ -110,7 +144,6 @@ func checkoutTypeForPlan(planType string) string {
|
||||
}
|
||||
|
||||
func resolveBaseCheckoutURL(cfg WaffoConfig, planType string) (*url.URL, error) {
|
||||
// Single WAFFO_CHECKOUT_URL only as absolute fallback when no store/products configured.
|
||||
// Prefer store + per-plan product IDs so yearly/perpetual map to different Waffo SKUs.
|
||||
productID := productIDForPlan(cfg, planType)
|
||||
if cfg.StoreSlug != "" && productID != "" {
|
||||
@@ -121,7 +154,7 @@ func resolveBaseCheckoutURL(cfg WaffoConfig, planType string) (*url.URL, error)
|
||||
}
|
||||
q := u.Query()
|
||||
q.Set("type", checkoutTypeForPlan(planType))
|
||||
q.Set("currency", cfg.Currency)
|
||||
q.Set("currency", currencyForPlan(cfg, planType))
|
||||
if cfg.Environment == "test" {
|
||||
q.Set("test", "true")
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildCheckoutURL_YearlyOnetime(t *testing.T) {
|
||||
func TestBuildCheckoutURL_YearlyOnetimeCNY(t *testing.T) {
|
||||
cfg := WaffoConfig{
|
||||
StoreSlug: "opsxshell-pwsi6m4n",
|
||||
ProductIDYearly: "PROD_1BIezCi932IbgCzRIV8hcJ",
|
||||
ProductIDPerpetual: "PROD_0pcnzzT4nalEkkcdyIUoD5",
|
||||
Currency: "CNY",
|
||||
CurrencyYearly: "CNY",
|
||||
CurrencyPerpetual: "USD",
|
||||
Environment: "test",
|
||||
SuccessURL: "opsxshell://pro/activated",
|
||||
}
|
||||
@@ -31,19 +33,18 @@ func TestBuildCheckoutURL_YearlyOnetime(t *testing.T) {
|
||||
t.Fatalf("type=%s want onetime", q.Get("type"))
|
||||
}
|
||||
if q.Get("currency") != "CNY" {
|
||||
t.Fatalf("currency=%s", q.Get("currency"))
|
||||
}
|
||||
if q.Get("test") != "true" {
|
||||
t.Fatalf("test=%s", q.Get("test"))
|
||||
t.Fatalf("currency=%s want CNY", q.Get("currency"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCheckoutURL_PerpetualOnetime(t *testing.T) {
|
||||
func TestBuildCheckoutURL_PerpetualOnetimeUSD(t *testing.T) {
|
||||
cfg := WaffoConfig{
|
||||
StoreSlug: "opsxshell-pwsi6m4n",
|
||||
ProductIDYearly: "PROD_1BIezCi932IbgCzRIV8hcJ",
|
||||
ProductIDPerpetual: "PROD_0pcnzzT4nalEkkcdyIUoD5",
|
||||
Currency: "CNY",
|
||||
CurrencyYearly: "CNY",
|
||||
CurrencyPerpetual: "USD",
|
||||
Environment: "test",
|
||||
SuccessURL: "opsxshell://pro/activated",
|
||||
}
|
||||
@@ -58,7 +59,11 @@ func TestBuildCheckoutURL_PerpetualOnetime(t *testing.T) {
|
||||
if !strings.Contains(u.Path, "PROD_0pcnzzT4nalEkkcdyIUoD5") {
|
||||
t.Fatalf("perpetual product missing: %s", raw)
|
||||
}
|
||||
if u.Query().Get("type") != "onetime" {
|
||||
t.Fatalf("type=%s want onetime", u.Query().Get("type"))
|
||||
q := u.Query()
|
||||
if q.Get("type") != "onetime" {
|
||||
t.Fatalf("type=%s want onetime", q.Get("type"))
|
||||
}
|
||||
if q.Get("currency") != "USD" {
|
||||
t.Fatalf("currency=%s want USD", q.Get("currency"))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user