| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- //go:build unit
- package service
- import (
- "context"
- "testing"
- "time"
- "github.com/Wei-Shaw/sub2api/internal/payment"
- infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
- "github.com/stretchr/testify/require"
- )
- type paymentResumeLookupProvider struct {
- queryCount int
- }
- func (p *paymentResumeLookupProvider) Name() string { return "resume-lookup-provider" }
- func (p *paymentResumeLookupProvider) ProviderKey() string { return payment.TypeAlipay }
- func (p *paymentResumeLookupProvider) SupportedTypes() []payment.PaymentType {
- return []payment.PaymentType{payment.TypeAlipay}
- }
- func (p *paymentResumeLookupProvider) CreatePayment(context.Context, payment.CreatePaymentRequest) (*payment.CreatePaymentResponse, error) {
- panic("unexpected call")
- }
- func (p *paymentResumeLookupProvider) QueryOrder(context.Context, string) (*payment.QueryOrderResponse, error) {
- p.queryCount++
- return &payment.QueryOrderResponse{Status: payment.ProviderStatusPending}, nil
- }
- func (p *paymentResumeLookupProvider) VerifyNotification(context.Context, string, map[string]string) (*payment.PaymentNotification, error) {
- panic("unexpected call")
- }
- func (p *paymentResumeLookupProvider) Refund(context.Context, payment.RefundRequest) (*payment.RefundResponse, error) {
- panic("unexpected call")
- }
- func TestGetPublicOrderByResumeTokenReturnsMatchingOrder(t *testing.T) {
- ctx := context.Background()
- client := newPaymentConfigServiceTestClient(t)
- user, err := client.User.Create().
- SetEmail("resume@example.com").
- SetPasswordHash("hash").
- SetUsername("resume-user").
- Save(ctx)
- require.NoError(t, err)
- instanceID := "12"
- providerKey := payment.TypeEasyPay
- order, err := client.PaymentOrder.Create().
- SetUserID(user.ID).
- SetUserEmail(user.Email).
- SetUserName(user.Username).
- SetAmount(88).
- SetPayAmount(88).
- SetFeeRate(0).
- SetRechargeCode("RESUME-ORDER").
- SetOutTradeNo("sub2_resume_lookup").
- SetPaymentType(payment.TypeAlipay).
- SetPaymentTradeNo("trade-1").
- SetOrderType(payment.OrderTypeBalance).
- SetStatus(OrderStatusPending).
- SetExpiresAt(time.Now().Add(time.Hour)).
- SetClientIP("127.0.0.1").
- SetSrcHost("api.example.com").
- SetProviderInstanceID(instanceID).
- SetProviderKey(providerKey).
- Save(ctx)
- require.NoError(t, err)
- resumeSvc := NewPaymentResumeService([]byte("0123456789abcdef0123456789abcdef"))
- token, err := resumeSvc.CreateToken(ResumeTokenClaims{
- OrderID: order.ID,
- UserID: user.ID,
- ProviderInstanceID: instanceID,
- ProviderKey: providerKey,
- PaymentType: payment.TypeAlipay,
- CanonicalReturnURL: "https://app.example.com/payment/result",
- })
- require.NoError(t, err)
- svc := &PaymentService{
- entClient: client,
- resumeService: resumeSvc,
- }
- got, err := svc.GetPublicOrderByResumeToken(ctx, token)
- require.NoError(t, err)
- require.Equal(t, order.ID, got.ID)
- }
- func TestGetPublicOrderByResumeTokenRejectsSnapshotMismatch(t *testing.T) {
- ctx := context.Background()
- client := newPaymentConfigServiceTestClient(t)
- user, err := client.User.Create().
- SetEmail("resume-mismatch@example.com").
- SetPasswordHash("hash").
- SetUsername("resume-mismatch-user").
- Save(ctx)
- require.NoError(t, err)
- order, err := client.PaymentOrder.Create().
- SetUserID(user.ID).
- SetUserEmail(user.Email).
- SetUserName(user.Username).
- SetAmount(88).
- SetPayAmount(88).
- SetFeeRate(0).
- SetRechargeCode("RESUME-MISMATCH").
- SetOutTradeNo("sub2_resume_lookup_mismatch").
- SetPaymentType(payment.TypeAlipay).
- SetPaymentTradeNo("trade-2").
- SetOrderType(payment.OrderTypeBalance).
- SetStatus(OrderStatusPending).
- SetExpiresAt(time.Now().Add(time.Hour)).
- SetClientIP("127.0.0.1").
- SetSrcHost("api.example.com").
- SetProviderInstanceID("12").
- SetProviderKey(payment.TypeEasyPay).
- Save(ctx)
- require.NoError(t, err)
- resumeSvc := NewPaymentResumeService([]byte("0123456789abcdef0123456789abcdef"))
- token, err := resumeSvc.CreateToken(ResumeTokenClaims{
- OrderID: order.ID,
- UserID: user.ID,
- ProviderInstanceID: "99",
- ProviderKey: payment.TypeEasyPay,
- PaymentType: payment.TypeAlipay,
- CanonicalReturnURL: "https://app.example.com/payment/result",
- })
- require.NoError(t, err)
- svc := &PaymentService{
- entClient: client,
- resumeService: resumeSvc,
- }
- _, err = svc.GetPublicOrderByResumeToken(ctx, token)
- require.Error(t, err)
- require.Equal(t, "INVALID_RESUME_TOKEN", infraerrors.Reason(err))
- }
- func TestGetPublicOrderByResumeTokenUsesSnapshotAuthorityWhenColumnsDiffer(t *testing.T) {
- ctx := context.Background()
- client := newPaymentConfigServiceTestClient(t)
- user, err := client.User.Create().
- SetEmail("resume-snapshot-authority@example.com").
- SetPasswordHash("hash").
- SetUsername("resume-snapshot-authority-user").
- Save(ctx)
- require.NoError(t, err)
- order, err := client.PaymentOrder.Create().
- SetUserID(user.ID).
- SetUserEmail(user.Email).
- SetUserName(user.Username).
- SetAmount(88).
- SetPayAmount(88).
- SetFeeRate(0).
- SetRechargeCode("RESUME-SNAPSHOT-AUTHORITY").
- SetOutTradeNo("sub2_resume_snapshot_authority").
- SetPaymentType(payment.TypeAlipay).
- SetPaymentTradeNo("trade-snapshot-authority").
- SetOrderType(payment.OrderTypeBalance).
- SetStatus(OrderStatusPending).
- SetExpiresAt(time.Now().Add(time.Hour)).
- SetClientIP("127.0.0.1").
- SetSrcHost("api.example.com").
- SetProviderInstanceID("legacy-column-instance").
- SetProviderKey(payment.TypeAlipay).
- SetProviderSnapshot(map[string]any{
- "schema_version": 2,
- "provider_instance_id": "snapshot-instance",
- "provider_key": payment.TypeEasyPay,
- }).
- Save(ctx)
- require.NoError(t, err)
- resumeSvc := NewPaymentResumeService([]byte("0123456789abcdef0123456789abcdef"))
- token, err := resumeSvc.CreateToken(ResumeTokenClaims{
- OrderID: order.ID,
- UserID: user.ID,
- ProviderInstanceID: "snapshot-instance",
- ProviderKey: payment.TypeEasyPay,
- PaymentType: payment.TypeAlipay,
- CanonicalReturnURL: "https://app.example.com/payment/result",
- })
- require.NoError(t, err)
- svc := &PaymentService{
- entClient: client,
- resumeService: resumeSvc,
- }
- got, err := svc.GetPublicOrderByResumeToken(ctx, token)
- require.NoError(t, err)
- require.Equal(t, order.ID, got.ID)
- }
- func TestGetPublicOrderByResumeTokenChecksUpstreamForPendingOrder(t *testing.T) {
- ctx := context.Background()
- client := newPaymentConfigServiceTestClient(t)
- user, err := client.User.Create().
- SetEmail("resume-refresh@example.com").
- SetPasswordHash("hash").
- SetUsername("resume-refresh-user").
- Save(ctx)
- require.NoError(t, err)
- order, err := client.PaymentOrder.Create().
- SetUserID(user.ID).
- SetUserEmail(user.Email).
- SetUserName(user.Username).
- SetAmount(88).
- SetPayAmount(88).
- SetFeeRate(0).
- SetRechargeCode("RESUME-PENDING").
- SetOutTradeNo("sub2_resume_lookup_pending").
- SetPaymentType(payment.TypeAlipay).
- SetPaymentTradeNo("trade-pending").
- SetOrderType(payment.OrderTypeBalance).
- SetStatus(OrderStatusPending).
- SetExpiresAt(time.Now().Add(time.Hour)).
- SetClientIP("127.0.0.1").
- SetSrcHost("api.example.com").
- Save(ctx)
- require.NoError(t, err)
- resumeSvc := NewPaymentResumeService([]byte("0123456789abcdef0123456789abcdef"))
- token, err := resumeSvc.CreateToken(ResumeTokenClaims{
- OrderID: order.ID,
- UserID: user.ID,
- PaymentType: payment.TypeAlipay,
- CanonicalReturnURL: "https://app.example.com/payment/result",
- })
- require.NoError(t, err)
- registry := payment.NewRegistry()
- provider := &paymentResumeLookupProvider{}
- registry.Register(provider)
- svc := &PaymentService{
- entClient: client,
- registry: registry,
- resumeService: resumeSvc,
- providersLoaded: true,
- }
- got, err := svc.GetPublicOrderByResumeToken(ctx, token)
- require.NoError(t, err)
- require.Equal(t, order.ID, got.ID)
- require.Equal(t, 1, provider.queryCount)
- }
- func TestVerifyOrderPublicDoesNotCheckUpstreamForPendingOrder(t *testing.T) {
- ctx := context.Background()
- client := newPaymentConfigServiceTestClient(t)
- user, err := client.User.Create().
- SetEmail("public-verify@example.com").
- SetPasswordHash("hash").
- SetUsername("public-verify-user").
- Save(ctx)
- require.NoError(t, err)
- order, err := client.PaymentOrder.Create().
- SetUserID(user.ID).
- SetUserEmail(user.Email).
- SetUserName(user.Username).
- SetAmount(88).
- SetPayAmount(88).
- SetFeeRate(0).
- SetRechargeCode("PUBLIC-VERIFY").
- SetOutTradeNo("sub2_public_verify_pending").
- SetPaymentType(payment.TypeAlipay).
- SetPaymentTradeNo("trade-public-verify").
- SetOrderType(payment.OrderTypeBalance).
- SetStatus(OrderStatusPending).
- SetExpiresAt(time.Now().Add(time.Hour)).
- SetClientIP("127.0.0.1").
- SetSrcHost("api.example.com").
- Save(ctx)
- require.NoError(t, err)
- registry := payment.NewRegistry()
- provider := &paymentResumeLookupProvider{}
- registry.Register(provider)
- svc := &PaymentService{
- entClient: client,
- registry: registry,
- providersLoaded: true,
- }
- got, err := svc.VerifyOrderPublic(ctx, order.OutTradeNo)
- require.NoError(t, err)
- require.Equal(t, order.ID, got.ID)
- require.Equal(t, 0, provider.queryCount)
- }
- func TestVerifyOrderPublicRejectsBlankOutTradeNo(t *testing.T) {
- svc := &PaymentService{
- entClient: newPaymentConfigServiceTestClient(t),
- }
- _, err := svc.VerifyOrderPublic(context.Background(), " ")
- require.Error(t, err)
- require.Equal(t, "INVALID_OUT_TRADE_NO", infraerrors.Reason(err))
- }
|