fix(payment): support source routing and compatible resume signing
This commit is contained in:
@@ -164,9 +164,8 @@ func TestVerifyOrderPublicReturnsLegacyOrderState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveOrderPublicByResumeTokenReturnsFrontendContractFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
t.Setenv("PAYMENT_RESUME_SIGNING_KEY", "0123456789abcdef0123456789abcdef")
|
||||
|
||||
db, err := sql.Open("sqlite", "file:payment_handler_public_resolve?mode=memory&cache=shared")
|
||||
require.NoError(t, err)
|
||||
@@ -250,3 +249,120 @@ func TestResolveOrderPublicByResumeTokenReturnsFrontendContractFields(t *testing
|
||||
require.Contains(t, resp.Data, "expires_at")
|
||||
require.Contains(t, resp.Data, "refund_amount")
|
||||
}
|
||||
|
||||
func TestResolveOrderPublicByResumeTokenReturnsBadRequestForMismatchedToken(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
t.Setenv("PAYMENT_RESUME_SIGNING_KEY", "0123456789abcdef0123456789abcdef")
|
||||
|
||||
db, err := sql.Open("sqlite", "file:payment_handler_public_resolve_mismatch?mode=memory&cache=shared")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = db.Close() })
|
||||
|
||||
_, err = db.Exec("PRAGMA foreign_keys = ON")
|
||||
require.NoError(t, err)
|
||||
|
||||
drv := entsql.OpenDB(dialect.SQLite, db)
|
||||
client := enttest.NewClient(t, enttest.WithOptions(dbent.Driver(drv)))
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
user, err := client.User.Create().
|
||||
SetEmail("public-resolve-mismatch@example.com").
|
||||
SetPasswordHash("hash").
|
||||
SetUsername("public-resolve-mismatch-user").
|
||||
Save(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
order, err := client.PaymentOrder.Create().
|
||||
SetUserID(user.ID).
|
||||
SetUserEmail(user.Email).
|
||||
SetUserName(user.Username).
|
||||
SetAmount(100).
|
||||
SetPayAmount(103).
|
||||
SetFeeRate(0.03).
|
||||
SetRechargeCode("PUBLIC-RESOLVE-MISMATCH").
|
||||
SetOutTradeNo("resolve-order-mismatch-no").
|
||||
SetPaymentType(payment.TypeAlipay).
|
||||
SetPaymentTradeNo("trade-public-resolve-mismatch").
|
||||
SetOrderType(payment.OrderTypeBalance).
|
||||
SetStatus(service.OrderStatusPaid).
|
||||
SetExpiresAt(time.Now().Add(time.Hour)).
|
||||
SetPaidAt(time.Now()).
|
||||
SetClientIP("127.0.0.1").
|
||||
SetSrcHost("api.example.com").
|
||||
Save(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
resumeSvc := service.NewPaymentResumeService([]byte("0123456789abcdef0123456789abcdef"))
|
||||
token, err := resumeSvc.CreateToken(service.ResumeTokenClaims{
|
||||
OrderID: order.ID,
|
||||
UserID: user.ID + 999,
|
||||
PaymentType: payment.TypeAlipay,
|
||||
CanonicalReturnURL: "https://app.example.com/payment/result",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
configSvc := service.NewPaymentConfigService(client, nil, []byte("0123456789abcdef0123456789abcdef"))
|
||||
paymentSvc := service.NewPaymentService(client, payment.NewRegistry(), nil, nil, nil, configSvc, nil, nil)
|
||||
h := NewPaymentHandler(paymentSvc, nil, nil)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(
|
||||
http.MethodPost,
|
||||
"/api/v1/payment/public/orders/resolve",
|
||||
bytes.NewBufferString(`{"resume_token":"`+token+`"}`),
|
||||
)
|
||||
ctx.Request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
h.ResolveOrderPublicByResumeToken(ctx)
|
||||
|
||||
require.Equal(t, http.StatusBadRequest, recorder.Code)
|
||||
|
||||
var resp struct {
|
||||
Code int `json:"code"`
|
||||
Reason string `json:"reason"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &resp))
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
require.Equal(t, "INVALID_RESUME_TOKEN", resp.Reason)
|
||||
}
|
||||
|
||||
func TestVerifyOrderPublicRejectsBlankOutTradeNo(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
db, err := sql.Open("sqlite", "file:payment_handler_public_verify_blank?mode=memory&cache=shared")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = db.Close() })
|
||||
|
||||
_, err = db.Exec("PRAGMA foreign_keys = ON")
|
||||
require.NoError(t, err)
|
||||
|
||||
drv := entsql.OpenDB(dialect.SQLite, db)
|
||||
client := enttest.NewClient(t, enttest.WithOptions(dbent.Driver(drv)))
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
paymentSvc := service.NewPaymentService(client, payment.NewRegistry(), nil, nil, nil, nil, nil, nil)
|
||||
h := NewPaymentHandler(paymentSvc, nil, nil)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(
|
||||
http.MethodPost,
|
||||
"/api/v1/payment/public/orders/verify",
|
||||
bytes.NewBufferString(`{"out_trade_no":" "}`),
|
||||
)
|
||||
ctx.Request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
h.VerifyOrderPublic(ctx)
|
||||
|
||||
require.Equal(t, http.StatusBadRequest, recorder.Code)
|
||||
|
||||
var resp struct {
|
||||
Code int `json:"code"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &resp))
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
require.Equal(t, "INVALID_OUT_TRADE_NO", resp.Reason)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user