修复 docs/claude代码分析.md 中等优先级的 8 项确认问题: - tenant/project.go:ProjectUpdate/Archive/Unarchive 的 UPDATE 补上 tenant_id 过滤,恢复写操作租户隔离约定 - taskchannel(ali/sora/suno/volcengine/midjourney):裸 http.Client 改为 common.NewPooledClient(走连接池 + 尊重渠道 UseProxy),不再丢弃 BuildRequestHeader 的错误 - handler/public/payment.go:支付回调失败补日志,伪造/篡改尝试可追溯 - billing/wallet.go + payment/fulfill.go:新增导出 InvalidateWallet 同时清进程内 walletCache 与 Redis,消除充值后读到旧余额 - logic/task/task.go:ExecuteTask 领取任务改为原子 CAS,多副本不再重复执行 - middleware/rbac.go:matchPermission 后缀匹配改为按路径分段边界, 修复 /test 规则误命中 .../latest - admin/channel.go + channel_oauth.go:渠道→密钥跨表写入包进事务, 杜绝孤儿渠道 - admin/promo_code.go:CreatePromoCode 增加字段白名单,对齐 UpdatePromoCode 另有 6 项经评估暂不修改(配额非原子/补扣负数/无 jti token/setup 竞态/ crypto AAD/turnstile 校验),原因见分析报告说明。
62 lines
1.9 KiB
Go
62 lines
1.9 KiB
Go
package public
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/net/ghttp"
|
|
|
|
"github.com/qianfree/team-api/internal/logic/payment"
|
|
)
|
|
|
|
// HandlePaymentCallback 处理支付渠道的异步回调通知(支持 GET/POST)。
|
|
// channel 从 URL 路径获取(如 "epay")。
|
|
func HandlePaymentCallback(r *ghttp.Request) {
|
|
channel := r.Get("channel").String()
|
|
if channel == "" {
|
|
r.Response.WriteStatus(400)
|
|
r.Response.Write("fail")
|
|
return
|
|
}
|
|
|
|
err := payment.ProcessCallback(r.Context(), r.Request, channel)
|
|
if err != nil {
|
|
// 回调处理失败(验签失败 / 金额不符 / 订单不存在等)必须落日志:
|
|
// 否则签名伪造/篡改尝试在服务端完全不可追溯。返回 200 fail 供渠道停止重推。
|
|
g.Log().Warningf(r.Context(), "[Payment] callback failed: channel=%s clientIP=%s err=%v",
|
|
channel, r.GetClientIp(), err)
|
|
r.Response.WriteStatus(200)
|
|
r.Response.Write("fail")
|
|
return
|
|
}
|
|
|
|
r.Response.WriteStatus(200)
|
|
r.Response.Write("success")
|
|
}
|
|
|
|
// HandlePaymentEpayReturn 处理 Epay 支付完成后的浏览器同步跳转。
|
|
// 仅查询订单状态决定跳转目标,履约由异步回调完成。
|
|
func HandlePaymentEpayReturn(r *ghttp.Request) {
|
|
orderNo := r.GetQuery("out_trade_no").String()
|
|
if orderNo == "" {
|
|
r.Response.RedirectTo("/console/wallet?pay=fail")
|
|
return
|
|
}
|
|
|
|
ctx := r.Context()
|
|
// 异步 notify 可能晚于浏览器回跳到达:最多轮询约 4 秒等待回调入账。
|
|
// 仍未到账不一定是失败(回调可能仍在途),引导到"处理中"页由前端继续刷新,
|
|
// 避免给用户展示误导性的"失败"。
|
|
paid := payment.QueryOrderPaid(ctx, orderNo)
|
|
for i := 0; !paid && i < 8; i++ {
|
|
time.Sleep(500 * time.Millisecond)
|
|
paid = payment.QueryOrderPaid(ctx, orderNo)
|
|
}
|
|
|
|
if paid {
|
|
r.Response.RedirectTo("/console/wallet?pay=success")
|
|
return
|
|
}
|
|
r.Response.RedirectTo("/console/wallet?pay=processing")
|
|
}
|