fix(dev): redirect 形态落地自包含 mock-cashier 页(返回可用),替代 /result 的 404 死路

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-11 07:45:26 +08:00
parent 1d9a8cb26a
commit 1f87b0fcbf
2 changed files with 40 additions and 2 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ func (p *Provider) Create(_ context.Context, req provider.CreateRequest) (*provi
ProviderRef: ref,
RenderType: provider.RenderRedirect,
Payload: map[string]any{
"url": "http://localhost:8090/result?out_trade_no=" + req.OutTradeNo,
"url": "http://localhost:8090/mock-cashier?out_trade_no=" + req.OutTradeNo,
},
ExpiresAt: &exp,
}, nil
+39 -1
View File
@@ -94,7 +94,13 @@ func main() {
// 入队,免去手工查库拼回调体。刻意不挂 stateChangeRateLimit(本机联调用,不设限)。
devH := handler.NewDevMockHandler(db, gw)
r.POST("/api/v2/dev/orders/:order_no/mark-paid", devH.MarkPaid)
log.Println("[dev] MOCK_CHANNEL_ENABLED=true:已挂载 POST /api/v2/dev/orders/:order_no/mark-paid")
// dev-only:fake 渠道 redirect 形态的落地页(自包含,返回可用)。真实流程这里是
// 支付宝/微信收银台;本地 mock 无需真付款,提示用户回购买页点「模拟付款成功」。
r.GET("/mock-cashier", func(c *gin.Context) {
c.Header("Content-Type", "text/html; charset=utf-8")
c.String(200, mockCashierHTML)
})
log.Println("[dev] MOCK_CHANNEL_ENABLED=true:已挂载 POST /api/v2/dev/orders/:order_no/mark-paid + GET /mock-cashier")
}
// P6 后台守护 / 对账:订单过期清理 + 用量刷新 + 查单对账收敛 + 已付抽查 +
@@ -382,3 +388,35 @@ func seedPangolin(db *gorm.DB) {
}
log.Printf("[seed-pangolin] pangolin 业务套餐就绪:%d 个(pro_month/pro_quarter/pro_year,CNY+USDT)", len(plans))
}
// mockCashierHTML — dev-only:fake 渠道 redirect 形态的自包含落地页(返回可用,无外链)。
const mockCashierHTML = `<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>模拟收银台</title>
<style>
:root{color-scheme:dark}
body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;
background:#0b0f17;color:#e8edf5;font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
.card{max-width:460px;padding:36px 32px;background:#141a24;border:1px solid #232c3a;
border-radius:16px;text-align:center;box-shadow:0 8px 40px rgba(0,0,0,.4)}
h1{font-size:22px;margin:8px 0 6px}
.sub{color:#93a1b5;font-size:14px;margin:0 0 20px}
.tip{background:#0f1622;border:1px solid #232c3a;border-radius:10px;padding:14px 16px;
font-size:13px;color:#aab6c6;text-align:left;margin:0 0 22px}
.btn{display:inline-block;padding:11px 20px;border-radius:10px;border:1px solid #2f3b4d;
background:#1b2432;color:#cdd7e4;text-decoration:none;font-size:14px;cursor:pointer}
.btn.primary{background:#2563eb;border-color:#2563eb;color:#fff}
.row{display:flex;gap:10px;justify-content:center}
.emoji{font-size:40px}
</style></head><body>
<div class="card">
<div class="emoji">🧪</div>
<h1>模拟支付收银台</h1>
<p class="sub">这是本地联调的占位收银台,不是真实支付。</p>
<div class="tip">真实流程这里会是<b>支付宝 / 微信 / Stripe</b> 的收银台,你在此完成付款后渠道异步通知到账。
本地 mock 无需真付款——请<b>关闭本页</b>,回到购买页点击<b>「🧪 模拟付款成功」</b>即可看到订阅开通。</div>
<div class="row">
<a class="btn primary" href="javascript:window.close()">关闭本页</a>
<a class="btn" href="javascript:history.back()">← 返回上一页</a>
</div>
</div></body></html>`