Files
pangolin/server/internal/pay/testutil_test.go
T
wangjia bc9aa38392 fix(server): pay webhook 兜底补行不再漏停用用户(修静默丢钱)+ 未知事件日志 + PAY_BIZ_SECRET 启动守卫
- webhook 兜底路径(台账缺行按 biz_ref 定位用户)去掉 status='active' 过滤,
  与正常路径(row.UserID 直接开通不看状态)对齐:钱已实收,不因用户被停用
  (banned)而拒绝补行开通,避免 500→pay 12 次重投后死信→静默丢钱。
- 未知 event_type 分支加 slog.Warn,便于将来 pay 侧误注册无 handler 的事件
  类型时能被观测到。
- PAY_BASE_URL 已设但 PAY_BIZ_SECRET 为空时 log.Fatal 拒绝启动,避免出站
  签名失败+入站验签全 401 的静默瘫痪。
- InsertFromWebhookTx 不再把 channel 冒充 method 写入台账(payload 无 method
  字段可复原,留空并加注释,消除台账观感误导)。

新增 TestWebhook_MissingLedgerFallsBackEvenWhenUserSuspended 覆盖 #1:
台账缺行 + 目标用户 banned 时,兜底补行仍成功开通并回 SUCCESS。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-11 00:48:57 +08:00

48 lines
1.5 KiB
Go

package pay
import (
"context"
"database/sql"
"testing"
"time"
"github.com/wangjia/pangolin/server/internal/config"
"github.com/wangjia/pangolin/server/internal/store"
)
func openMigratedSQLite(t *testing.T) *sql.DB {
t.Helper()
db, err := store.Open(&config.Config{Driver: "sqlite", DSN: ":memory:"})
if err != nil {
t.Fatalf("open: %v", err)
}
t.Cleanup(func() { _ = db.Close() })
if err := store.MigrateUp(db, "sqlite"); err != nil {
t.Fatalf("migrate: %v", err)
}
if err := store.ApplyCodesLibMigrations(context.Background(), db, "sqlite"); err != nil {
t.Fatalf("lib migrate: %v", err)
}
return db
}
// seedUser 造最小 users 行(列以 codes/sqlite_helper_test.go::seedUser 为准,
// 实现时照抄那份 INSERT——含 uuid/email/pw_hash/dp_uuid 等 NOT NULL 列)。
func seedUser(t *testing.T, db *sql.DB, id int64, uuid string) {
t.Helper()
seedUserWithStatus(t, db, id, uuid, "active")
}
// seedUserWithStatus 同 seedUser,可指定 status(如 'suspended')——用于测试兜底路径
// 不得因用户被停用而拒绝补行开通(钱已实收,见 webhook.go settle 兜底分支的注释)。
func seedUserWithStatus(t *testing.T, db *sql.DB, id int64, uuid, status string) {
t.Helper()
_, err := db.Exec(
`INSERT INTO users (id, uuid, email, pw_hash, dp_uuid, status, created_at)
VALUES (?, ?, ?, 'x', ?, ?, ?)`,
id, uuid, uuid+"@t.local", uuid, status, time.Now().UTC())
if err != nil {
t.Fatalf("seedUserWithStatus: %v", err)
}
}