调整 优化代码细节

This commit is contained in:
Little Write
2025-09-27 18:04:48 +08:00
parent 01925858ec
commit 95cb7fc862
4 changed files with 102 additions and 102 deletions

View File

@@ -36,8 +36,9 @@ func generateCreemSignature(payload string, secret string) string {
// 验证Creem webhook签名 // 验证Creem webhook签名
func verifyCreemSignature(payload string, signature string, secret string) bool { func verifyCreemSignature(payload string, signature string, secret string) bool {
if secret == "" { if secret == "" {
log.Printf("Creem webhook secret not set")
if setting.CreemTestMode { if setting.CreemTestMode {
log.Printf("Creem webhook secret未配置测试模式下跳过签名验证") log.Printf("Skip Creem webhook sign verify in test mode")
return true return true
} }
return false return false
@@ -146,8 +147,8 @@ func RequestCreemPay(c *gin.Context) {
// 读取body内容用于打印同时保留原始数据供后续使用 // 读取body内容用于打印同时保留原始数据供后续使用
bodyBytes, err := io.ReadAll(c.Request.Body) bodyBytes, err := io.ReadAll(c.Request.Body)
if err != nil { if err != nil {
log.Printf("读取请求body失败: %v", err) log.Printf("read creem pay req body err: %v", err)
c.JSON(200, gin.H{"message": "error", "data": "读取请求失败"}) c.JSON(200, gin.H{"message": "error", "data": "read query error"})
return return
} }
@@ -158,7 +159,6 @@ func RequestCreemPay(c *gin.Context) {
c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes)) c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes))
err = c.ShouldBindJSON(&req) err = c.ShouldBindJSON(&req)
log.Printf(" json body is %+v", req)
if err != nil { if err != nil {
c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
return return
@@ -251,7 +251,9 @@ func CreemWebhook(c *gin.Context) {
// 打印关键信息避免输出完整敏感payload // 打印关键信息避免输出完整敏感payload
log.Printf("Creem Webhook - URI: %s", c.Request.RequestURI) 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缺少签名头") log.Printf("Creem Webhook缺少签名头")
c.AbortWithStatus(http.StatusUnauthorized) c.AbortWithStatus(http.StatusUnauthorized)
return 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, 客户邮箱: <redacted>, 产品: %s",
referenceId, referenceId,
event.Object.Order.Id, event.Object.Order.Id,
event.Object.Order.AmountPaid, event.Object.Order.AmountPaid,
event.Object.Order.Currency, event.Object.Order.Currency,
event.Object.Customer.Email,
event.Object.Product.Name) event.Object.Product.Name)
// 查询本地订单确认存在 // 查询本地订单确认存在
@@ -355,8 +356,8 @@ func handleCheckoutCompleted(c *gin.Context, event *CreemWebhookEvent) {
return return
} }
log.Printf("Creem充值成功 - 订单号: %s, 充值额度: %d, 支付金额: %.2f, 客户邮箱: %s, 客户姓名: %s", log.Printf("Creem充值成功 - 订单号: %s, 充值额度: %d, 支付金额: %.2f",
referenceId, topUp.Amount, topUp.Money, customerEmail, customerName) referenceId, topUp.Amount, topUp.Money)
c.Status(http.StatusOK) c.Status(http.StatusOK)
} }
@@ -438,11 +439,11 @@ func genCreemLink(referenceId string, product *CreemProduct, email string, usern
return "", fmt.Errorf("读取响应失败: %v", err) 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 { 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 var checkoutResp CreemCheckoutResponse
@@ -452,7 +453,7 @@ func genCreemLink(referenceId string, product *CreemProduct, email string, usern
} }
if checkoutResp.CheckoutUrl == "" { 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) log.Printf("Creem 支付链接创建成功 - 订单号: %s, 支付链接: %s", referenceId, checkoutResp.CheckoutUrl)

View File

@@ -98,7 +98,7 @@ func Recharge(referenceId string, customerId string) (err error) {
return nil return nil
} }
func RechargeCreem(referenceId string, customerEmail string) (err error) { func RechargeCreem(referenceId string, customerEmail string, customerName string) (err error) {
if referenceId == "" { if referenceId == "" {
return errors.New("未提供支付单号") return errors.New("未提供支付单号")
} }
@@ -148,7 +148,6 @@ func RechargeCreem(referenceId string, customerEmail string) (err error) {
// 如果用户邮箱为空,则更新为支付时使用的邮箱 // 如果用户邮箱为空,则更新为支付时使用的邮箱
if user.Email == "" { if user.Email == "" {
updateFields["email"] = customerEmail updateFields["email"] = customerEmail
// 避免输出敏感信息到stdout
} }
} }

View File

@@ -50,12 +50,7 @@ const PaymentSetting = () => {
} }
break; break;
case 'CreemProducts': case 'CreemProducts':
try { newInputs[item.key] = item.value || '[]';
newInputs[item.key] = item.value;
} catch (error) {
console.error('解析CreemProducts出错:', error);
newInputs[item.key] = '[]';
}
break; break;
case 'Price': case 'Price':
case 'MinTopUp': case 'MinTopUp':

View File

@@ -315,6 +315,11 @@ const TopUp = () => {
showError(t('请选择产品')); showError(t('请选择产品'));
return; return;
} }
// Validate product has required fields
if (!selectedCreemProduct.productId) {
showError(t('产品配置错误,请联系管理员'));
return;
}
setConfirmLoading(true); setConfirmLoading(true);
try { try {
const res = await API.post('/api/user/creem/pay', { const res = await API.post('/api/user/creem/pay', {
@@ -668,14 +673,14 @@ const TopUp = () => {
</Modal> </Modal>
<Modal <Modal
title={t('确定要充值吗')} title={t('确定要充值吗')}
visible={stripeOpen} visible={stripeOpen}
onOk={onlineStripeTopUp} onOk={onlineStripeTopUp}
onCancel={handleStripeCancel} onCancel={handleStripeCancel}
maskClosable={false} maskClosable={false}
size='small' size='small'
centered centered
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
> >
<p> <p>
{t('充值数量')}{stripeTopUpCount} {t('充值数量')}{stripeTopUpCount}
@@ -1026,85 +1031,85 @@ const TopUp = () => {
)} )}
{enableStripeTopUp && ( {enableStripeTopUp && (
<> <>
{/* 桌面端显示的自定义金额和支付按钮 */} {/* 桌面端显示的自定义金额和支付按钮 */}
<div className='hidden md:block space-y-4'> <div className='hidden md:block space-y-4'>
<Divider style={{ margin: '24px 0' }}> <Divider style={{ margin: '24px 0' }}>
<Text className='text-sm font-medium'> <Text className='text-sm font-medium'>
{t(!enableOnlineTopUp ? '或输入自定义金额' : 'Stripe')} {t(!enableOnlineTopUp ? '或输入自定义金额' : 'Stripe')}
</Text> </Text>
</Divider> </Divider>
<div> <div>
<div className='flex justify-between mb-2'> <div className='flex justify-between mb-2'>
<Text strong>{t('充值数量')}</Text> <Text strong>{t('充值数量')}</Text>
{amountLoading ? ( {amountLoading ? (
<Skeleton.Title <Skeleton.Title
style={{ width: '80px', height: '16px' }} style={{ width: '80px', height: '16px' }}
/> />
) : ( ) : (
<Text type='tertiary'> <Text type='tertiary'>
{t('实付金额:') + renderStripeAmount()} {t('实付金额:') + renderStripeAmount()}
</Text> </Text>
)} )}
</div>
<InputNumber
disabled={!enableStripeTopUp}
placeholder={
t('充值数量,最低 ') + renderQuotaWithAmount(stripeMinTopUp)
}
value={stripeTopUpCount}
min={stripeMinTopUp}
max={999999999}
step={1}
precision={0}
onChange={async (value) => {
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
}
/>
</div> </div>
<InputNumber
disabled={!enableStripeTopUp}
placeholder={
t('充值数量,最低 ') + renderQuotaWithAmount(stripeMinTopUp)
}
value={stripeTopUpCount}
min={stripeMinTopUp}
max={999999999}
step={1}
precision={0}
onChange={async (value) => {
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
}
/>
</div>
<div> <div>
<Text strong className='block mb-3'> <Text strong className='block mb-3'>
{t('选择支付方式')} {t('选择支付方式')}
</Text> </Text>
<div className='grid grid-cols-1 gap-3'> <div className='grid grid-cols-1 gap-3'>
<Button <Button
key='stripe' key='stripe'
type='primary' type='primary'
onClick={() => stripePreTopUp()} onClick={() => stripePreTopUp()}
size='large' size='large'
disabled={!enableStripeTopUp} disabled={!enableStripeTopUp}
loading={paymentLoading && payWay === 'stripe'} loading={paymentLoading && payWay === 'stripe'}
icon={<CreditCard size={16} />} icon={<CreditCard size={16} />}
style={{ style={{
height: '40px', height: '40px',
color: '#b161fe', color: '#b161fe',
}} }}
className='transition-all hover:shadow-md w-full' className='transition-all hover:shadow-md w-full'
> >
<span className='ml-1'>Stripe</span> <span className='ml-1'>Stripe</span>
</Button> </Button>
</div>
</div> </div>
</div> </div>
</div>
{/* 移动端 Stripe 充值区域 */} {/* 移动端 Stripe 充值区域 */}
<div className='md:hidden space-y-4'> <div className='md:hidden space-y-4'>