From 95cb7fc862e91c4dcfad14ff9eab77f1958460b0 Mon Sep 17 00:00:00 2001 From: Little Write Date: Sat, 27 Sep 2025 18:04:48 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/topup_creem.go | 25 +-- model/topup.go | 3 +- web/src/components/settings/PaymentSetting.js | 7 +- web/src/pages/TopUp/index.js | 169 +++++++++--------- 4 files changed, 102 insertions(+), 102 deletions(-) diff --git a/controller/topup_creem.go b/controller/topup_creem.go index 76968a3d1..a22868b80 100644 --- a/controller/topup_creem.go +++ b/controller/topup_creem.go @@ -36,8 +36,9 @@ func generateCreemSignature(payload string, secret string) string { // 验证Creem webhook签名 func verifyCreemSignature(payload string, signature string, secret string) bool { if secret == "" { + log.Printf("Creem webhook secret not set") if setting.CreemTestMode { - log.Printf("Creem webhook secret未配置,测试模式下跳过签名验证") + log.Printf("Skip Creem webhook sign verify in test mode") return true } return false @@ -146,8 +147,8 @@ func RequestCreemPay(c *gin.Context) { // 读取body内容用于打印,同时保留原始数据供后续使用 bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { - log.Printf("读取请求body失败: %v", err) - c.JSON(200, gin.H{"message": "error", "data": "读取请求失败"}) + log.Printf("read creem pay req body err: %v", err) + c.JSON(200, gin.H{"message": "error", "data": "read query error"}) return } @@ -158,7 +159,6 @@ func RequestCreemPay(c *gin.Context) { c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes)) err = c.ShouldBindJSON(&req) - log.Printf(" json body is %+v", req) if err != nil { c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) return @@ -251,7 +251,9 @@ func CreemWebhook(c *gin.Context) { // 打印关键信息(避免输出完整敏感payload) log.Printf("Creem Webhook - URI: %s", c.Request.RequestURI) - if signature == "" && !setting.CreemTestMode { + if setting.CreemTestMode { + log.Printf("Creem Webhook - Signature: %s , Body: %s", signature, bodyBytes) + } else if signature == "" { log.Printf("Creem Webhook缺少签名头") c.AbortWithStatus(http.StatusUnauthorized) return @@ -314,12 +316,11 @@ func handleCheckoutCompleted(c *gin.Context, event *CreemWebhookEvent) { } // 记录详细的支付信息 - log.Printf("处理Creem支付完成 - 订单号: %s, Creem订单ID: %s, 支付金额: %d %s, 客户邮箱: %s, 产品: %s", + log.Printf("处理Creem支付完成 - 订单号: %s, Creem订单ID: %s, 支付金额: %d %s, 客户邮箱: , 产品: %s", referenceId, event.Object.Order.Id, event.Object.Order.AmountPaid, event.Object.Order.Currency, - event.Object.Customer.Email, event.Object.Product.Name) // 查询本地订单确认存在 @@ -355,8 +356,8 @@ func handleCheckoutCompleted(c *gin.Context, event *CreemWebhookEvent) { return } - log.Printf("Creem充值成功 - 订单号: %s, 充值额度: %d, 支付金额: %.2f, 客户邮箱: %s, 客户姓名: %s", - referenceId, topUp.Amount, topUp.Money, customerEmail, customerName) + log.Printf("Creem充值成功 - 订单号: %s, 充值额度: %d, 支付金额: %.2f", + referenceId, topUp.Amount, topUp.Money) c.Status(http.StatusOK) } @@ -438,11 +439,11 @@ func genCreemLink(referenceId string, product *CreemProduct, email string, usern return "", fmt.Errorf("读取响应失败: %v", err) } - log.Printf("Creem API响应 - 状态码: %d, 响应体: %s", resp.StatusCode, string(body)) + log.Printf("Creem API resp - status code: %d, resp: %s", resp.StatusCode, string(body)) // 检查响应状态 if resp.StatusCode/100 != 2 { - return "", fmt.Errorf("Creem API 返回错误状态 %d: %s", resp.StatusCode, string(body)) + return "", fmt.Errorf("Creem API http status %d ", resp.StatusCode) } // 解析响应 var checkoutResp CreemCheckoutResponse @@ -452,7 +453,7 @@ func genCreemLink(referenceId string, product *CreemProduct, email string, usern } if checkoutResp.CheckoutUrl == "" { - return "", fmt.Errorf("Creem API 未返回支付链接") + return "", fmt.Errorf("Creem API resp no checkout url ") } log.Printf("Creem 支付链接创建成功 - 订单号: %s, 支付链接: %s", referenceId, checkoutResp.CheckoutUrl) diff --git a/model/topup.go b/model/topup.go index d621918b8..8b4ab0c25 100644 --- a/model/topup.go +++ b/model/topup.go @@ -98,7 +98,7 @@ func Recharge(referenceId string, customerId string) (err error) { return nil } -func RechargeCreem(referenceId string, customerEmail string) (err error) { +func RechargeCreem(referenceId string, customerEmail string, customerName string) (err error) { if referenceId == "" { return errors.New("未提供支付单号") } @@ -148,7 +148,6 @@ func RechargeCreem(referenceId string, customerEmail string) (err error) { // 如果用户邮箱为空,则更新为支付时使用的邮箱 if user.Email == "" { updateFields["email"] = customerEmail - // 避免输出敏感信息到stdout } } diff --git a/web/src/components/settings/PaymentSetting.js b/web/src/components/settings/PaymentSetting.js index 01884c964..044d36de5 100644 --- a/web/src/components/settings/PaymentSetting.js +++ b/web/src/components/settings/PaymentSetting.js @@ -50,12 +50,7 @@ const PaymentSetting = () => { } break; case 'CreemProducts': - try { - newInputs[item.key] = item.value; - } catch (error) { - console.error('解析CreemProducts出错:', error); - newInputs[item.key] = '[]'; - } + newInputs[item.key] = item.value || '[]'; break; case 'Price': case 'MinTopUp': diff --git a/web/src/pages/TopUp/index.js b/web/src/pages/TopUp/index.js index d7625685c..e4b26925d 100644 --- a/web/src/pages/TopUp/index.js +++ b/web/src/pages/TopUp/index.js @@ -315,6 +315,11 @@ const TopUp = () => { showError(t('请选择产品')); return; } + // Validate product has required fields + if (!selectedCreemProduct.productId) { + showError(t('产品配置错误,请联系管理员')); + return; + } setConfirmLoading(true); try { const res = await API.post('/api/user/creem/pay', { @@ -668,14 +673,14 @@ const TopUp = () => {

{t('充值数量')}:{stripeTopUpCount} @@ -1026,85 +1031,85 @@ const TopUp = () => { )} {enableStripeTopUp && ( - <> - {/* 桌面端显示的自定义金额和支付按钮 */} -

- - - {t(!enableOnlineTopUp ? '或输入自定义金额' : 'Stripe')} - - + <> + {/* 桌面端显示的自定义金额和支付按钮 */} +
+ + + {t(!enableOnlineTopUp ? '或输入自定义金额' : 'Stripe')} + + -
-
- {t('充值数量')} - {amountLoading ? ( - - ) : ( - - {t('实付金额:') + renderStripeAmount()} - - )} -
- { - if (value && value >= 1) { - setStripeTopUpCount(value); - setSelectedPreset(null); - await getStripeAmount(value); - } - }} - onBlur={(e) => { - const value = parseInt(e.target.value); - if (!value || value < 1) { - setStripeTopUpCount(1); - getStripeAmount(1); - } - }} - size='large' - className='w-full' - formatter={(value) => (value ? `${value}` : '')} - parser={(value) => - value ? parseInt(value.replace(/[^\d]/g, '')) : 0 - } - /> +
+
+ {t('充值数量')} + {amountLoading ? ( + + ) : ( + + {t('实付金额:') + renderStripeAmount()} + + )}
+ { + if (value && value >= 1) { + setStripeTopUpCount(value); + setSelectedPreset(null); + await getStripeAmount(value); + } + }} + onBlur={(e) => { + const value = parseInt(e.target.value); + if (!value || value < 1) { + setStripeTopUpCount(1); + getStripeAmount(1); + } + }} + size='large' + className='w-full' + formatter={(value) => (value ? `${value}` : '')} + parser={(value) => + value ? parseInt(value.replace(/[^\d]/g, '')) : 0 + } + /> +
-
- - {t('选择支付方式')} - -
- -
+
+ + {t('选择支付方式')} + +
+
+
{/* 移动端 Stripe 充值区域 */}