feat(v2): 配置驱动注册三渠道 adapter + crypto 真 adapter 全链 e2e(下单→查单→入账→webhook)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package gateway_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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/crypto"
|
||||
"github.com/wangjia/pay/internal/store"
|
||||
)
|
||||
|
||||
// TestE2ECryptoQuerySettles 验证真 crypto adapter(非 fake)跑通 P2 全链:
|
||||
// 下单(单地址+唯一金额)→ 查单兜底(假 TronGrid only_confirmed 精确匹配)→ 入账 → webhook。
|
||||
// 不经 provider.BuildRegistry(那是 main 装配期用的),直接用 crypto.New 组 gateway——
|
||||
// 本用例验证的是「真 adapter 跑通管线」,BuildRegistry 由 registry_build_test.go 覆盖装配决策。
|
||||
func TestE2ECryptoQuerySettles(t *testing.T) {
|
||||
const addr = "TWe2eADDRESS00000000000000000000000"
|
||||
t.Setenv("E2E_ADDRESS", addr)
|
||||
t.Setenv("E2E_TRONGRID_KEY", "k")
|
||||
|
||||
// 假 TronGrid:已确认、金额精确匹配、块时晚于建单的转账(期望金额在下单后从 payload 取)。
|
||||
var expected int64
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := map[string]any{"success": true, "data": []map[string]any{{
|
||||
"transaction_id": "tx-e2e",
|
||||
"to": addr,
|
||||
"type": "Transfer",
|
||||
"value": strconv.FormatInt(expected, 10),
|
||||
"block_timestamp": time.Now().Add(5 * time.Second).UnixMilli(), // 晚于建单(查单在下单后发生)
|
||||
}}}
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
db := model.OpenTestDB(t)
|
||||
orders := store.NewOrderStore(db)
|
||||
acctReg := accounts.New([]config.AccountConfig{
|
||||
{AccountID: "e2e-1", Channel: "crypto", Enabled: true, Region: "global", CredentialEnvPrefix: "e2e"},
|
||||
})
|
||||
preg := provider.NewRegistry()
|
||||
preg.Register(crypto.New(acctReg, crypto.WithBaseURL(ts.URL), crypto.WithHTTPClient(ts.Client())))
|
||||
|
||||
// gateway.New 要求 accounts.Picker(非裸 *accounts.Registry);单账户场景用默认
|
||||
// round_robin 的 Router 包一层即可(同 gateway_test.go newGateway 的装法)。
|
||||
picker := accounts.NewRouter(acctReg, nil, nil)
|
||||
|
||||
spy := &spyEnqueuer{}
|
||||
g := gateway.New(orders, preg, picker, cryptoResolver{}, spy, "global")
|
||||
|
||||
// 下单 → 从 session payload 拿到期望链上金额(base+唯一尾数),喂给假 TronGrid。
|
||||
res, err := g.CreateOrder(context.Background(), gateway.CreateOrderInput{
|
||||
SKU: "pro_year", Method: "crypto", BizSystem: "pangolin", BizRef: "u-e2e",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create: %v", err)
|
||||
}
|
||||
atts, _ := orders.ListAttemptsByStatus(model.AttemptPending, 10)
|
||||
if len(atts) != 1 {
|
||||
t.Fatalf("want 1 pending attempt, got %d", len(atts))
|
||||
}
|
||||
expected = res.Session.Payload["amount_minor"].(int64) // 唯一金额(随机尾数)只有 payload/provider_ref 知道
|
||||
|
||||
// 查单兜底 → 命中 → 入账 → webhook
|
||||
n, err := g.SyncPendingAttempts(context.Background(), 10)
|
||||
if err != nil || n != 1 {
|
||||
t.Fatalf("sync settled=%d err=%v want 1", n, err)
|
||||
}
|
||||
o, _ := orders.GetOrder(res.OrderNo)
|
||||
if o.Status != model.OrderPaidV2 {
|
||||
t.Fatalf("order status = %s want paid", o.Status)
|
||||
}
|
||||
if len(spy.calls) != 1 || spy.calls[0]["product_biz_code"] != "pro_year" {
|
||||
t.Fatalf("webhook = %+v", spy.calls)
|
||||
}
|
||||
}
|
||||
|
||||
type cryptoResolver struct{}
|
||||
|
||||
func (cryptoResolver) Resolve(sku, currency string) (int64, string, string, error) {
|
||||
if sku == "pro_year" && currency == "USDT" {
|
||||
return 29990000, "Pro 年付", "pro_year", nil
|
||||
}
|
||||
return 0, "", "", gateway.ErrProductNotFound
|
||||
}
|
||||
Reference in New Issue
Block a user