From 1d4d9836da8c26fec6f450a6761196b6baf89c1f Mon Sep 17 00:00:00 2001 From: chengqian Date: Thu, 11 Jun 2026 12:28:44 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E9=92=B1=E5=8C=85?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E7=9B=B8=E5=85=B3=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/public/payment.go | 12 ++++++------ internal/logic/billing/currency.go | 8 ++++++-- internal/logic/payment/callback.go | 14 ++++++++++++++ internal/logic/payment/epay.go | 3 ++- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/internal/handler/public/payment.go b/internal/handler/public/payment.go index 65a5bca..260e98c 100644 --- a/internal/handler/public/payment.go +++ b/internal/handler/public/payment.go @@ -28,13 +28,13 @@ func HandlePaymentCallback(r *ghttp.Request) { } // HandlePaymentEpayReturn 处理 Epay 支付完成后的浏览器同步跳转。 +// 仅查询订单状态决定跳转目标,履约由异步回调完成。 func HandlePaymentEpayReturn(r *ghttp.Request) { - err := payment.ProcessCallback(r.Context(), r.Request, "epay") - if err != nil { - r.Response.WriteStatus(302) - r.Response.Header().Set("Location", "/console/wallet?pay=fail") + orderNo := r.GetQuery("out_trade_no").String() + paid := payment.QueryOrderPaid(r.Context(), orderNo) + if paid { + r.Response.RedirectTo("/console/wallet?pay=success") return } - r.Response.WriteStatus(302) - r.Response.Header().Set("Location", "/console/wallet?pay=success") + r.Response.RedirectTo("/console/wallet?pay=fail") } diff --git a/internal/logic/billing/currency.go b/internal/logic/billing/currency.go index bea08c9..dc09ca1 100644 --- a/internal/logic/billing/currency.go +++ b/internal/logic/billing/currency.go @@ -2,6 +2,7 @@ package billing import ( "context" + "math" lcommon "github.com/qianfree/team-api/internal/logic/common" ) @@ -9,10 +10,13 @@ import ( const defaultCNYToUSD = 0.14 const defaultUSDToCNY = 7.25 -// ConvertCNYToUSD 将人民币金额转换为美元 +// usdPrecision 钱包余额精度:小数点后 6 位。 +const usdPrecision = 1e6 + +// ConvertCNYToUSD 将人民币金额转换为美元,向上取整到小数点后 6 位。 func ConvertCNYToUSD(ctx context.Context, cnyAmount float64) float64 { rate := GetExchangeRateCNYToUSD(ctx) - return cnyAmount * rate + return math.Ceil(cnyAmount*rate*usdPrecision) / usdPrecision } // ConvertUSDToCNY 将美元金额转换为人民币 diff --git a/internal/logic/payment/callback.go b/internal/logic/payment/callback.go index 9762fa9..6f46d0e 100644 --- a/internal/logic/payment/callback.go +++ b/internal/logic/payment/callback.go @@ -138,3 +138,17 @@ func ProcessCallback(ctx context.Context, r *http.Request, channelType string) e return nil } + +// QueryOrderPaid 查询订单是否已完成支付(paid 或 fulfilled)。 +// 用于浏览器同步回跳时展示结果,不参与履约处理。 +func QueryOrderPaid(ctx context.Context, orderNo string) bool { + if orderNo == "" { + return false + } + var status string + _ = dao.OrdOrders.Ctx(ctx). + Where("order_no", orderNo). + Fields("status"). + Scan(&status) + return status == "paid" || status == "fulfilled" +} diff --git a/internal/logic/payment/epay.go b/internal/logic/payment/epay.go index 5c87bdb..e42ed3f 100644 --- a/internal/logic/payment/epay.go +++ b/internal/logic/payment/epay.go @@ -3,6 +3,7 @@ package payment import ( "context" "crypto/md5" + "crypto/subtle" "encoding/hex" "fmt" "io" @@ -72,7 +73,7 @@ func (p *EpayProvider) HandleCallback(ctx context.Context, r *http.Request, conf receivedSign := r.FormValue("sign") expectedSign := epaySign(params, cfg.MerchantKey) - if receivedSign != expectedSign { + if subtle.ConstantTimeCompare([]byte(receivedSign), []byte(expectedSign)) != 1 { return nil, lcommon.NewBusinessError(422, "易支付签名验证失败") }