Files
wangjia da8bbefe2e merge: P8 订阅/recurring + 拒付 chargeback 并入(订阅生命周期/续费/取消/past_due/chargeback/事件集收口)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u

# Conflicts:
#	internal/model/testdb.go
#	internal/provider/provider.go
#	internal/router/router.go
#	internal/store/order_query_test.go
#	main.go
2026-07-10 19:43:32 +08:00

55 lines
1.8 KiB
Go

package handler_test
import (
"net/http"
"testing"
"github.com/gin-gonic/gin"
"github.com/wangjia/pay/config"
"github.com/wangjia/pay/internal/accounts"
"github.com/wangjia/pay/internal/gateway"
"github.com/wangjia/pay/internal/model"
"github.com/wangjia/pay/internal/provider"
"github.com/wangjia/pay/internal/provider/fake"
"github.com/wangjia/pay/internal/router"
"github.com/wangjia/pay/internal/store"
)
// jiu 反馈波 item 1:POST /api/v2/orders 的 metadata 字段只应放行白名单键
// (is_mobile/render),任意其它键(潜在注入 provider 内部逻辑)必须被丢弃。
func TestV2CreateOrderMetadataWhitelist(t *testing.T) {
gin.SetMode(gin.TestMode)
db := model.OpenTestDB(t)
orders := store.NewOrderStore(db)
refunds := store.NewRefundStore(db)
preg := provider.NewRegistry()
fp := fake.New()
preg.Register(fp)
areg := accounts.New([]config.AccountConfig{
{AccountID: "fake-a1", Channel: "fake", Region: "global", Enabled: true, Weight: 1},
})
picker := accounts.NewRouter(areg, nil, nil)
g := gateway.New(orders, refunds, preg, picker, oneResolver{}, nopEnqueuer{}, "global", store.NewSubscriptionStore(db), store.NewChargebackStore(db))
r := gin.New()
router.SetupV2(r, g)
w, out := do(t, r, http.MethodPost, "/api/v2/orders", map[string]any{
"sku": "pro_year", "method": "fake",
"metadata": map[string]any{
"is_mobile": "1",
"evil_key": "inject-me", // 不在白名单,必须被丢弃
},
})
if w.Code != http.StatusOK {
t.Fatalf("create code=%d body=%v", w.Code, out)
}
got := fp.LastMetadata()
if got["is_mobile"] != "1" {
t.Fatalf("白名单键 is_mobile 应透传给 provider, got %+v", got)
}
if _, ok := got["evil_key"]; ok {
t.Fatalf("非白名单键 evil_key 不应透传给 provider, got %+v", got)
}
}