diff --git a/backend/internal/service/payment_refund.go b/backend/internal/service/payment_refund.go index 01eecff8..57469fa3 100644 --- a/backend/internal/service/payment_refund.go +++ b/backend/internal/service/payment_refund.go @@ -21,7 +21,7 @@ import ( // getOrderProviderInstance looks up the provider instance that processed this order. // For legacy orders without provider_instance_id, it resolves only when the -// enabled instance is uniquely identifiable from the stored order fields. +// historical instance is uniquely identifiable from the stored order fields. func (s *PaymentService) getOrderProviderInstance(ctx context.Context, o *dbent.PaymentOrder) (*dbent.PaymentProviderInstance, error) { if s == nil || s.entClient == nil || o == nil { return nil, nil @@ -40,47 +40,55 @@ func (s *PaymentService) getOrderProviderInstance(ctx context.Context, o *dbent. } func (s *PaymentService) resolveUniqueLegacyOrderProviderInstance(ctx context.Context, o *dbent.PaymentOrder) (*dbent.PaymentProviderInstance, error) { + paymentType := payment.GetBasePaymentType(strings.TrimSpace(o.PaymentType)) providerKey := strings.TrimSpace(psStringValue(o.ProviderKey)) if providerKey != "" { instances, err := s.entClient.PaymentProviderInstance.Query(). - Where( - paymentproviderinstance.EnabledEQ(true), - paymentproviderinstance.ProviderKeyEQ(providerKey), - ). + Where(paymentproviderinstance.ProviderKeyEQ(providerKey)). All(ctx) if err != nil { return nil, err } - if len(instances) == 1 { - return instances[0], nil + matched := psFilterLegacyOrderProviderInstances(paymentType, instances) + if len(matched) == 1 { + return matched[0], nil } return nil, nil } - paymentType := payment.GetBasePaymentType(strings.TrimSpace(o.PaymentType)) if paymentType == "" { return nil, nil } instances, err := s.entClient.PaymentProviderInstance.Query(). - Where(paymentproviderinstance.EnabledEQ(true)). All(ctx) if err != nil { return nil, err } - var matched []*dbent.PaymentProviderInstance - for _, inst := range instances { - if psLegacyOrderMatchesInstance(paymentType, inst) { - matched = append(matched, inst) - } - } + matched := psFilterLegacyOrderProviderInstances(paymentType, instances) if len(matched) == 1 { return matched[0], nil } return nil, nil } +func psFilterLegacyOrderProviderInstances(orderPaymentType string, instances []*dbent.PaymentProviderInstance) []*dbent.PaymentProviderInstance { + if len(instances) == 0 { + return nil + } + if strings.TrimSpace(orderPaymentType) == "" { + return instances + } + var matched []*dbent.PaymentProviderInstance + for _, inst := range instances { + if psLegacyOrderMatchesInstance(orderPaymentType, inst) { + matched = append(matched, inst) + } + } + return matched +} + func psLegacyOrderMatchesInstance(orderPaymentType string, inst *dbent.PaymentProviderInstance) bool { if inst == nil { return false diff --git a/backend/internal/service/payment_webhook_provider_test.go b/backend/internal/service/payment_webhook_provider_test.go index 33e4186d..4f0b6848 100644 --- a/backend/internal/service/payment_webhook_provider_test.go +++ b/backend/internal/service/payment_webhook_provider_test.go @@ -141,6 +141,70 @@ func TestGetOrderProviderInstanceLeavesAmbiguousLegacyOrderUnresolved(t *testing require.Nil(t, got) } +func TestGetOrderProviderInstanceLeavesLegacyProviderKeyUnresolvedWhenHistoricalInstancesConflict(t *testing.T) { + ctx := context.Background() + client := newPaymentConfigServiceTestClient(t) + _, err := client.PaymentProviderInstance.Create(). + SetProviderKey(payment.TypeStripe). + SetName("stripe-disabled-legacy"). + SetConfig("{}"). + SetSupportedTypes("stripe"). + SetEnabled(false). + Save(ctx) + require.NoError(t, err) + _, err = client.PaymentProviderInstance.Create(). + SetProviderKey(payment.TypeStripe). + SetName("stripe-enabled-current"). + SetConfig("{}"). + SetSupportedTypes("stripe"). + SetEnabled(true). + Save(ctx) + require.NoError(t, err) + + providerKey := payment.TypeStripe + order := &dbent.PaymentOrder{ + PaymentType: payment.TypeStripe, + ProviderKey: &providerKey, + } + + svc := &PaymentService{ + entClient: client, + loadBalancer: newWebhookProviderTestLoadBalancer(client), + } + + got, err := svc.getOrderProviderInstance(ctx, order) + require.NoError(t, err) + require.Nil(t, got) +} + +func TestGetOrderProviderInstanceLeavesProviderKeyMatchUnresolvedWhenTypeNotSupported(t *testing.T) { + ctx := context.Background() + client := newPaymentConfigServiceTestClient(t) + _, err := client.PaymentProviderInstance.Create(). + SetProviderKey(payment.TypeWxpay). + SetName("wxpay-only"). + SetConfig("{}"). + SetSupportedTypes("wxpay"). + SetEnabled(true). + Save(ctx) + require.NoError(t, err) + + providerKey := payment.TypeWxpay + order := &dbent.PaymentOrder{ + PaymentType: payment.TypeAlipayDirect, + ProviderKey: &providerKey, + } + + svc := &PaymentService{ + entClient: client, + loadBalancer: newWebhookProviderTestLoadBalancer(client), + } + + got, err := svc.getOrderProviderInstance(ctx, order) + require.NoError(t, err) + require.Nil(t, got) +} + func TestGetWebhookProviderRejectsAmbiguousRegistryFallback(t *testing.T) { ctx := context.Background() client := newPaymentConfigServiceTestClient(t)