98 lines
3.7 KiB
Go
98 lines
3.7 KiB
Go
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。
|
|
// 不经 providerbuild.BuildRegistry(那是 main 装配期用的),直接用 crypto.New 组 gateway——
|
|
// 本用例验证的是「真 adapter 跑通管线」,BuildRegistry 本身的装配/跳过决策由
|
|
// internal/providerbuild/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)
|
|
refunds := store.NewRefundStore(db)
|
|
subs := store.NewSubscriptionStore(db)
|
|
chargebacks := store.NewChargebackStore(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, refunds, preg, picker, cryptoResolver{}, spy, "global", subs, chargebacks)
|
|
|
|
// 下单 → 从 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
|
|
}
|