Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
3251dc0b
authored
Sep 27, 2025
by
Little Write
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
调整 优化代码细节
parent
447166ac
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
103 additions
and
103 deletions
+103
-103
controller/topup_creem.go
+13
-12
model/topup.go
+1
-2
web/src/components/settings/PaymentSetting.js
+1
-6
web/src/pages/TopUp/index.js
+88
-83
No files found.
controller/topup_creem.go
View file @
3251dc0b
...
...
@@ -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, 客户邮箱:
<redacted>
, 产品: %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
)
...
...
model/topup.go
View file @
3251dc0b
...
...
@@ -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
}
}
...
...
web/src/components/settings/PaymentSetting.js
View file @
3251dc0b
...
...
@@ -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'
:
...
...
web/src/pages/TopUp/index.js
View file @
3251dc0b
...
...
@@ -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 = () => {
<
/Modal
>
<
Modal
title
=
{
t
(
'确定要充值吗'
)}
visible
=
{
stripeOpen
}
onOk
=
{
onlineStripeTopUp
}
onCancel
=
{
handleStripeCancel
}
maskClosable
=
{
false
}
size
=
'small'
centered
confirmLoading
=
{
confirmLoading
}
title
=
{
t
(
'确定要充值吗'
)}
visible
=
{
stripeOpen
}
onOk
=
{
onlineStripeTopUp
}
onCancel
=
{
handleStripeCancel
}
maskClosable
=
{
false
}
size
=
'small'
centered
confirmLoading
=
{
confirmLoading
}
>
<
p
>
{
t
(
'充值数量'
)}
:
{
stripeTopUpCount
}
...
...
@@ -1026,85 +1031,85 @@ const TopUp = () => {
)}
{
enableStripeTopUp
&&
(
<>
{
/* 桌面端显示的自定义金额和支付按钮 */
}
<
div
className
=
'hidden md:block space-y-4'
>
<
Divider
style
=
{{
margin
:
'24px 0'
}}
>
<
Text
className
=
'text-sm font-medium'
>
{
t
(
!
enableOnlineTopUp
?
'或输入自定义金额'
:
'Stripe'
)}
<
/Text
>
<
/Divider
>
<
div
>
<
div
className
=
'flex justify-between mb-2'
>
<
Text
strong
>
{
t
(
'充值数量'
)}
<
/Text
>
{
amountLoading
?
(
<
Skeleton
.
Title
style
=
{{
width
:
'80px'
,
height
:
'16px'
}}
/
>
)
:
(
<
Text
type
=
'tertiary'
>
{
t
(
'实付金额:'
)
+
renderStripeAmount
()}
<
/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
className
=
'hidden md:block space-y-4'
>
<
Divider
style
=
{{
margin
:
'24px 0'
}}
>
<
Text
className
=
'text-sm font-medium'
>
{
t
(
!
enableOnlineTopUp
?
'或输入自定义金额'
:
'Stripe'
)}
<
/Text
>
<
/Divider
>
<
div
>
<
div
className
=
'flex justify-between mb-2'
>
<
Text
strong
>
{
t
(
'充值数量'
)}
<
/Text
>
{
amountLoading
?
(
<
Skeleton
.
Title
style
=
{{
width
:
'80px'
,
height
:
'16px'
}}
/
>
)
:
(
<
Text
type
=
'tertiary'
>
{
t
(
'实付金额:'
)
+
renderStripeAmount
()}
<
/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
>
<
Text
strong
className
=
'block mb-3'
>
{
t
(
'选择支付方式'
)}
<
/Text
>
<
div
className
=
'grid grid-cols-1 gap-3'
>
<
Button
key
=
'stripe'
type
=
'primary'
onClick
=
{()
=>
stripePreTopUp
()}
size
=
'large'
disabled
=
{
!
enableStripeTopUp
}
loading
=
{
paymentLoading
&&
payWay
===
'stripe'
}
icon
=
{
<
CreditCard
size
=
{
16
}
/>
}
style
=
{{
height
:
'40px'
,
color
:
'#b161fe'
,
}}
className
=
'transition-all hover:shadow-md w-full'
>
<
span
className
=
'ml-1'
>
Stripe
<
/span
>
<
/Button
>
<
/div
>
<
div
>
<
Text
strong
className
=
'block mb-3'
>
{
t
(
'选择支付方式'
)}
<
/Text
>
<
div
className
=
'grid grid-cols-1 gap-3'
>
<
Button
key
=
'stripe'
type
=
'primary'
onClick
=
{()
=>
stripePreTopUp
()}
size
=
'large'
disabled
=
{
!
enableStripeTopUp
}
loading
=
{
paymentLoading
&&
payWay
===
'stripe'
}
icon
=
{
<
CreditCard
size
=
{
16
}
/>
}
style
=
{{
height
:
'40px'
,
color
:
'#b161fe'
,
}}
className
=
'transition-all hover:shadow-md w-full'
>
<
span
className
=
'ml-1'
>
Stripe
<
/span
>
<
/Button
>
<
/div
>
<
/div
>
<
/div
>
{
/* 移动端 Stripe 充值区域 */
}
<
div
className
=
'md:hidden space-y-4'
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment