From 798fb9e245041e1ca61509291bf3d2ff547311e7 Mon Sep 17 00:00:00 2001 From: Tension Date: Wed, 31 Dec 2025 11:47:24 +0800 Subject: [PATCH] feat(currency): add currency configuration support and integrate into payment processing --- initialize/currency.go | 34 ++++++++++ initialize/init.go | 1 + internal/config/config.go | 7 ++ .../public/portal/purchaseCheckoutLogic.go | 64 ++++++++----------- internal/svc/serviceContext.go | 2 +- 5 files changed, 71 insertions(+), 37 deletions(-) create mode 100644 initialize/currency.go diff --git a/initialize/currency.go b/initialize/currency.go new file mode 100644 index 0000000..25dc52f --- /dev/null +++ b/initialize/currency.go @@ -0,0 +1,34 @@ +package initialize + +import ( + "context" + "fmt" + + "github.com/perfect-panel/server/internal/config" + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/pkg/logger" + "github.com/perfect-panel/server/pkg/tool" +) + +func Currency(ctx *svc.ServiceContext) { + // Retrieve system currency configuration + currency, err := ctx.SystemModel.GetCurrencyConfig(context.Background()) + if err != nil { + logger.Errorf("[INIT] Failed to get currency configuration: %v", err.Error()) + panic(fmt.Sprintf("[INIT] Failed to get currency configuration: %v", err.Error())) + } + // Parse currency configuration + configs := struct { + CurrencyUnit string + CurrencySymbol string + AccessKey string + }{} + tool.SystemConfigSliceReflectToStruct(currency, &configs) + + ctx.Config.Currency = config.Currency{ + Unit: configs.CurrencyUnit, + Symbol: configs.CurrencySymbol, + AccessKey: configs.AccessKey, + } + logger.Infof("[INIT] Currency configuration: %v", ctx.Config.Currency) +} diff --git a/initialize/init.go b/initialize/init.go index 8023ce5..2333b5e 100644 --- a/initialize/init.go +++ b/initialize/init.go @@ -15,6 +15,7 @@ func StartInitSystemConfig(svc *svc.ServiceContext) { Subscribe(svc) Register(svc) Mobile(svc) + Currency(svc) if !svc.Config.Debug { Telegram(svc) } diff --git a/internal/config/config.go b/internal/config/config.go index 59ece74..fc93da5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -29,6 +29,7 @@ type Config struct { Invite InviteConfig `yaml:"Invite"` Telegram Telegram `yaml:"Telegram"` Log Log `yaml:"Log"` + Currency Currency `yaml:"Currency"` Administrator struct { Email string `yaml:"Email" default:"admin@ppanel.dev"` Password string `yaml:"Password" default:"password"` @@ -241,3 +242,9 @@ type NodeDBConfig struct { Block string Outbound string } + +type Currency struct { + Unit string `yaml:"Unit" default:"CNY"` + Symbol string `yaml:"Symbol" default:"USD"` + AccessKey string `yaml:"AccessKey" default:""` +} diff --git a/internal/logic/public/portal/purchaseCheckoutLogic.go b/internal/logic/public/portal/purchaseCheckoutLogic.go index ff7faf2..105e1dc 100644 --- a/internal/logic/public/portal/purchaseCheckoutLogic.go +++ b/internal/logic/public/portal/purchaseCheckoutLogic.go @@ -9,6 +9,7 @@ import ( "github.com/perfect-panel/server/internal/model/log" "github.com/perfect-panel/server/internal/report" "github.com/perfect-panel/server/pkg/constant" + "github.com/perfect-panel/server/pkg/exchangeRate" paymentPlatform "github.com/perfect-panel/server/pkg/payment" @@ -21,12 +22,10 @@ import ( "github.com/perfect-panel/server/internal/model/payment" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/types" - "github.com/perfect-panel/server/pkg/exchangeRate" "github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/payment/alipay" "github.com/perfect-panel/server/pkg/payment/epay" "github.com/perfect-panel/server/pkg/payment/stripe" - "github.com/perfect-panel/server/pkg/tool" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" ) @@ -261,6 +260,7 @@ func (l *PurchaseCheckoutLogic) stripePayment(config string, info *order.Order, // epayPayment processes EPay payment by generating a payment URL for redirect // It handles currency conversion and creates a payment URL for external payment processing func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order.Order, returnUrl string) (string, error) { + var err error // Parse EPay configuration from payment settings epayConfig := &payment.EPayConfig{} if err := epayConfig.Unmarshal([]byte(config.Config)); err != nil { @@ -269,15 +269,18 @@ func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order } // Initialize EPay client with merchant credentials client := epay.NewClient(epayConfig.Pid, epayConfig.Url, epayConfig.Key, epayConfig.Type) - - // Convert order amount to CNY using current exchange rate - amount, err := l.queryExchangeRate("CNY", info.Amount) - if err != nil { - return "", err + var amount float64 + if l.svcCtx.Config.Currency.Unit != "CNY" { + // Convert order amount to CNY using current exchange rate + amount, err = l.queryExchangeRate("CNY", info.Amount) + if err != nil { + return "", err + } + } else { + amount = float64(info.Amount) / float64(100) } // gateway mod - isGatewayMod := report.IsGatewayMode() // Build notification URL for payment status callbacks @@ -293,7 +296,6 @@ func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order if !ok { host = l.svcCtx.Config.Host } - notifyUrl = "https://" + host if isGatewayMod { notifyUrl += "/api" @@ -316,6 +318,7 @@ func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order // CryptoSaaSPayment processes CryptoSaaSPayment payment by generating a payment URL for redirect // It handles currency conversion and creates a payment URL for external payment processing func (l *PurchaseCheckoutLogic) CryptoSaaSPayment(config *payment.Payment, info *order.Order, returnUrl string) (string, error) { + var err error // Parse EPay configuration from payment settings epayConfig := &payment.CryptoSaaSConfig{} if err := epayConfig.Unmarshal([]byte(config.Config)); err != nil { @@ -325,10 +328,16 @@ func (l *PurchaseCheckoutLogic) CryptoSaaSPayment(config *payment.Payment, info // Initialize EPay client with merchant credentials client := epay.NewClient(epayConfig.AccountID, epayConfig.Endpoint, epayConfig.SecretKey, epayConfig.Type) - // Convert order amount to CNY using current exchange rate - amount, err := l.queryExchangeRate("CNY", info.Amount) - if err != nil { - return "", err + var amount float64 + + if l.svcCtx.Config.Currency.Unit != "CNY" { + // Convert order amount to CNY using current exchange rate + amount, err = l.queryExchangeRate("CNY", info.Amount) + if err != nil { + return "", err + } + } else { + amount = float64(info.Amount) / float64(100) } // gateway mod @@ -377,35 +386,18 @@ func (l *PurchaseCheckoutLogic) queryExchangeRate(to string, src int64) (amount return amount, nil } - // Retrieve system currency configuration - currency, err := l.svcCtx.SystemModel.GetCurrencyConfig(l.ctx) - if err != nil { - l.Errorw("[PurchaseCheckout] GetCurrencyConfig error", logger.Field("error", err.Error())) - return 0, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetCurrencyConfig error: %s", err.Error()) - } - - // Parse currency configuration - configs := struct { - CurrencyUnit string - CurrencySymbol string - AccessKey string - }{} - tool.SystemConfigSliceReflectToStruct(currency, &configs) - // Skip conversion if no exchange rate API key configured - if configs.AccessKey == "" { + if l.svcCtx.Config.Currency.AccessKey == "" { return amount, nil } // Convert currency if system currency differs from target currency - if configs.CurrencyUnit != to { - result, err := exchangeRate.GetExchangeRete(configs.CurrencyUnit, to, configs.AccessKey, 1) - if err != nil { - return 0, err - } - amount = result * amount + result, err := exchangeRate.GetExchangeRete(l.svcCtx.Config.Currency.Unit, to, l.svcCtx.Config.Currency.AccessKey, 1) + if err != nil { + return 0, err } - return amount, nil + l.svcCtx.ExchangeRate = result + return result * amount, nil } // balancePayment processes balance payment with gift amount priority logic diff --git a/internal/svc/serviceContext.go b/internal/svc/serviceContext.go index 4f6bc3a..aa79ccc 100644 --- a/internal/svc/serviceContext.go +++ b/internal/svc/serviceContext.go @@ -97,7 +97,7 @@ func NewServiceContext(c config.Config) *ServiceContext { Redis: rds, Config: c, Queue: NewAsynqClient(c), - ExchangeRate: 1.0, + ExchangeRate: 0, GeoIP: geoIP, //NodeCache: cache.NewNodeCacheClient(rds), AuthLimiter: authLimiter,