同步历史版本代码

This commit is contained in:
2026-03-03 09:32:22 -08:00
parent 7d46b31866
commit 4d8516b2e1
140 changed files with 8399 additions and 489 deletions
@@ -3,7 +3,10 @@ package portal
import (
"context"
"encoding/json"
"fmt"
"math"
"strconv"
"strings"
"time"
"github.com/perfect-panel/server/internal/model/log"
@@ -26,6 +29,7 @@ import (
"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"
)
@@ -73,6 +77,13 @@ func (l *PurchaseCheckoutLogic) PurchaseCheckout(req *types.CheckoutOrderRequest
}
// Route to appropriate payment handler based on payment platform
switch paymentPlatform.ParsePlatform(orderInfo.Method) {
case paymentPlatform.AppleIAP:
productId := fmt.Sprintf("merchant.hifastvpn.day%d", orderInfo.Quantity)
resp = &types.CheckoutOrderResponse{
Type: "apple_iap",
ProductIds: []string{productId},
}
return resp, nil
case paymentPlatform.EPay:
// Process EPay payment - generates payment URL for redirect
url, err := l.epayPayment(paymentConfig, orderInfo, req.ReturnUrl)
@@ -186,13 +197,20 @@ func (l *PurchaseCheckoutLogic) alipayF2fPayment(pay *payment.Payment, info *ord
l.Errorw("[PurchaseCheckout] queryExchangeRate error", logger.Field("error", err.Error()))
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "queryExchangeRate error: %s", err.Error())
}
convertAmount := int64(amount * 100) // Convert to cents for API
convertAmount := int64(math.Round(amount * 100))
l.Infow("alipay amount",
logger.Field("src_cents", info.Amount),
logger.Field("decimal", amount),
logger.Field("cents", convertAmount),
)
// Create pre-payment trade and generate QR code
QRCode, err := client.PreCreateTrade(l.ctx, alipay.Order{
o := alipay.Order{
OrderNo: info.OrderNo,
Amount: convertAmount,
})
}
l.Infow("alipay request", logger.Field("order", o))
QRCode, err := client.PreCreateTrade(l.ctx, o)
if err != nil {
l.Errorw("[PurchaseCheckout] PreCreateTrade error", logger.Field("error", err.Error()))
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "PreCreateTrade error: %s", err.Error())
@@ -218,25 +236,50 @@ func (l *PurchaseCheckoutLogic) stripePayment(config string, info *order.Order,
WebhookSecret: stripeConfig.WebhookSecret,
})
// Convert order amount to CNY using current exchange rate
amount, err := l.queryExchangeRate("CNY", info.Amount)
currency := "USD"
sysCurrency, _ := l.svcCtx.SystemModel.GetCurrencyConfig(l.ctx)
if sysCurrency != nil {
configs := struct {
CurrencyUnit string
CurrencySymbol string
AccessKey string
}{}
tool.SystemConfigSliceReflectToStruct(sysCurrency, &configs)
if configs.CurrencyUnit != "" {
currency = configs.CurrencyUnit
}
}
// Convert order amount to configured currency using current exchange rate
amount, err := l.queryExchangeRate(strings.ToUpper(currency), info.Amount)
if err != nil {
l.Errorw("[PurchaseCheckout] queryExchangeRate error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "queryExchangeRate error: %s", err.Error())
}
convertAmount := int64(amount * 100) // Convert to cents for Stripe API
convertAmount := int64(math.Round(amount * 100))
l.Infow("stripe amount",
logger.Field("src_cents", info.Amount),
logger.Field("decimal", amount),
logger.Field("cents", convertAmount),
logger.Field("currency", currency),
)
// Create Stripe payment sheet for client-side processing
result, err := client.CreatePaymentSheet(&stripe.Order{
// Map apple_pay to card for Stripe API, but keep apple_pay in config/response
paymentMethod := stripeConfig.Payment
if paymentMethod == "apple_pay" {
paymentMethod = "card"
}
ord := &stripe.Order{
OrderNo: info.OrderNo,
Subscribe: strconv.FormatInt(info.SubscribeId, 10),
Amount: convertAmount,
Currency: "cny",
Payment: stripeConfig.Payment,
},
&stripe.User{
Email: identifier,
})
Currency: strings.ToLower(currency),
Payment: paymentMethod,
}
usr := &stripe.User{Email: identifier}
l.Infow("stripe request", logger.Field("order", ord), logger.Field("user", usr))
result, err := client.CreatePaymentSheet(ord, usr)
if err != nil {
l.Errorw("[PurchaseCheckout] CreatePaymentSheet error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "CreatePaymentSheet error: %s", err.Error())
@@ -282,6 +325,11 @@ func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order
} else {
amount = float64(info.Amount) / float64(100)
}
amount = math.Round(amount*100) / 100
l.Infow("epay amount",
logger.Field("src_cents", info.Amount),
logger.Field("decimal", amount),
)
// gateway mod
isGatewayMod := report.IsGatewayMode()
@@ -342,6 +390,11 @@ func (l *PurchaseCheckoutLogic) CryptoSaaSPayment(config *payment.Payment, info
} else {
amount = float64(info.Amount) / float64(100)
}
amount = math.Round(amount*100) / 100
l.Infow("crypto amount",
logger.Field("src_cents", info.Amount),
logger.Field("decimal", amount),
)
// gateway mod
isGatewayMod := report.IsGatewayMode()
@@ -395,18 +448,53 @@ func (l *PurchaseCheckoutLogic) queryExchangeRate(to string, src int64) (amount
return amount, nil
}
// Retrieve system currency configuration from DB for FixedRate fallback
currency, dbErr := l.svcCtx.SystemModel.GetCurrencyConfig(l.ctx)
var fixedRate string
if dbErr == nil && currency != nil {
configs := struct {
CurrencyUnit string
CurrencySymbol string
AccessKey string
FixedRate string
}{}
tool.SystemConfigSliceReflectToStruct(currency, &configs)
fixedRate = strings.TrimSpace(configs.FixedRate)
}
// Skip conversion if no exchange rate API key configured
if l.svcCtx.Config.Currency.AccessKey == "" {
if to == "CNY" && strings.TrimSpace(l.svcCtx.Config.Currency.Unit) == "USD" && fixedRate != "" {
r := tool.FormatStringToFloat(fixedRate)
if r > 0 {
l.Infow("exchangeRate.fixed", logger.Field("rate", r))
return amount * r, nil
}
}
l.Infof("[PurchaseCheckout] AccessKey is empty, skip conversion")
return amount, nil
}
// Convert currency if system currency differs from target currency
result, err := exchangeRate.GetExchangeRete(l.svcCtx.Config.Currency.Unit, to, l.svcCtx.Config.Currency.AccessKey, 1)
if err != nil {
l.Logger.Error("[PurchaseCheckout] QueryExchangeRate error", logger.Field("error", err.Error()))
return 0, err
if to == "CNY" && strings.TrimSpace(l.svcCtx.Config.Currency.Unit) == "USD" && fixedRate != "" {
r := tool.FormatStringToFloat(fixedRate)
if r > 0 {
l.Infow("exchangeRate.fixed.fallback", logger.Field("rate", r), logger.Field("error", err.Error()))
return amount * r, nil
}
}
// fallback: try without access key
result2, err2 := exchangeRate.GetExchangeRete(l.svcCtx.Config.Currency.Unit, to, "", 1)
if err2 != nil {
l.Logger.Error("[PurchaseCheckout] QueryExchangeRate error", logger.Field("error", err.Error()))
return 0, err
}
result = result2
}
l.svcCtx.ExchangeRate = result
l.Infow("exchangeRate", logger.Field("from", l.svcCtx.Config.Currency.Unit), logger.Field("to", to), logger.Field("rate", result))
return result * amount, nil
}