| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- package service
- import (
- "context"
- "errors"
- "fmt"
- "strings"
- dbent "github.com/Wei-Shaw/sub2api/ent"
- "github.com/Wei-Shaw/sub2api/ent/paymentproviderinstance"
- "github.com/Wei-Shaw/sub2api/internal/payment"
- infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
- )
- func enabledVisibleMethodsForProvider(providerKey, supportedTypes string) []string {
- methodSet := make(map[string]struct{}, 2)
- addMethod := func(method string) {
- method = NormalizeVisibleMethod(method)
- if method != "" {
- methodSet[method] = struct{}{}
- }
- }
- switch strings.TrimSpace(providerKey) {
- case payment.TypeAlipay:
- if strings.TrimSpace(supportedTypes) == "" {
- addMethod(payment.TypeAlipay)
- break
- }
- for _, supportedType := range splitTypes(supportedTypes) {
- if NormalizeVisibleMethod(supportedType) == payment.TypeAlipay {
- addMethod(payment.TypeAlipay)
- break
- }
- }
- case payment.TypeWxpay:
- if strings.TrimSpace(supportedTypes) == "" {
- addMethod(payment.TypeWxpay)
- break
- }
- for _, supportedType := range splitTypes(supportedTypes) {
- if NormalizeVisibleMethod(supportedType) == payment.TypeWxpay {
- addMethod(payment.TypeWxpay)
- break
- }
- }
- case payment.TypeEasyPay:
- for _, supportedType := range splitTypes(supportedTypes) {
- addMethod(supportedType)
- }
- }
- methods := make([]string, 0, len(methodSet))
- for _, method := range []string{payment.TypeAlipay, payment.TypeWxpay} {
- if _, ok := methodSet[method]; ok {
- methods = append(methods, method)
- delete(methodSet, method)
- }
- }
- for _, supportedType := range splitTypes(supportedTypes) {
- method := NormalizeVisibleMethod(supportedType)
- if _, ok := methodSet[method]; ok {
- methods = append(methods, method)
- delete(methodSet, method)
- }
- }
- return methods
- }
- func providerSupportsVisibleMethod(inst *dbent.PaymentProviderInstance, method string) bool {
- if inst == nil || !inst.Enabled {
- return false
- }
- method = NormalizeVisibleMethod(method)
- for _, candidate := range enabledVisibleMethodsForProvider(inst.ProviderKey, inst.SupportedTypes) {
- if candidate == method {
- return true
- }
- }
- return false
- }
- func filterEnabledVisibleMethodInstances(instances []*dbent.PaymentProviderInstance, method string) []*dbent.PaymentProviderInstance {
- filtered := make([]*dbent.PaymentProviderInstance, 0, len(instances))
- for _, inst := range instances {
- if providerSupportsVisibleMethod(inst, method) {
- filtered = append(filtered, inst)
- }
- }
- return filtered
- }
- func filterVisibleMethodInstancesByProviderKey(instances []*dbent.PaymentProviderInstance, method string, providerKey string) []*dbent.PaymentProviderInstance {
- filtered := make([]*dbent.PaymentProviderInstance, 0, len(instances))
- for _, inst := range instances {
- if !providerSupportsVisibleMethod(inst, method) {
- continue
- }
- if !strings.EqualFold(strings.TrimSpace(inst.ProviderKey), strings.TrimSpace(providerKey)) {
- continue
- }
- filtered = append(filtered, inst)
- }
- return filtered
- }
- func distinctVisibleMethodProviderKeys(instances []*dbent.PaymentProviderInstance) []string {
- seen := make(map[string]struct{}, len(instances))
- keys := make([]string, 0, len(instances))
- for _, inst := range instances {
- if inst == nil {
- continue
- }
- key := strings.TrimSpace(inst.ProviderKey)
- if key == "" {
- continue
- }
- normalized := strings.ToLower(key)
- if _, ok := seen[normalized]; ok {
- continue
- }
- seen[normalized] = struct{}{}
- keys = append(keys, key)
- }
- return keys
- }
- func selectVisibleMethodInstanceByProviderKey(instances []*dbent.PaymentProviderInstance, providerKey string) *dbent.PaymentProviderInstance {
- providerKey = strings.TrimSpace(providerKey)
- if providerKey == "" {
- return nil
- }
- for _, inst := range instances {
- if strings.EqualFold(strings.TrimSpace(inst.ProviderKey), providerKey) {
- return inst
- }
- }
- return nil
- }
- func (s *PaymentConfigService) validateVisibleMethodEnablementConflicts(
- ctx context.Context,
- excludeID int64,
- providerKey string,
- supportedTypes string,
- enabled bool,
- ) error {
- // Visible methods are selected by configured source (official/easypay),
- // so multiple enabled providers can intentionally claim the same user-facing
- // method. Order creation and limits will route through the configured source.
- _, _, _, _, _ = ctx, excludeID, providerKey, supportedTypes, enabled
- return nil
- }
- func (s *PaymentConfigService) resolveVisibleMethodSourceProviderKey(ctx context.Context, method string) (string, error) {
- method = NormalizeVisibleMethod(method)
- sourceKey := visibleMethodSourceSettingKey(method)
- rawSource := ""
- if s != nil && s.settingRepo != nil && sourceKey != "" {
- value, err := s.settingRepo.GetValue(ctx, sourceKey)
- if err != nil {
- if !errors.Is(err, ErrSettingNotFound) {
- return "", fmt.Errorf("get %s: %w", sourceKey, err)
- }
- } else {
- rawSource = value
- }
- }
- normalizedSource, err := normalizeVisibleMethodSettingSource(method, rawSource, true)
- if err != nil {
- return "", err
- }
- if normalizedSource == "" {
- return "", nil
- }
- providerKey, ok := VisibleMethodProviderKeyForSource(method, normalizedSource)
- if !ok {
- return "", infraerrors.BadRequest(
- "INVALID_PAYMENT_VISIBLE_METHOD_SOURCE",
- fmt.Sprintf("%s source must be one of the supported payment providers", method),
- )
- }
- return providerKey, nil
- }
- func (s *PaymentConfigService) resolveVisibleMethodProviderKey(
- ctx context.Context,
- method string,
- matching []*dbent.PaymentProviderInstance,
- ) (string, error) {
- switch providerKeys := distinctVisibleMethodProviderKeys(matching); len(providerKeys) {
- case 0:
- return "", nil
- case 1:
- return strings.TrimSpace(providerKeys[0]), nil
- default:
- providerKey, err := s.resolveVisibleMethodSourceProviderKey(ctx, method)
- if err != nil {
- return "", err
- }
- if providerKey == "" {
- return "", nil
- }
- selected := selectVisibleMethodInstanceByProviderKey(matching, providerKey)
- if selected == nil {
- return "", infraerrors.BadRequest(
- "INVALID_PAYMENT_VISIBLE_METHOD_SOURCE",
- fmt.Sprintf("%s source has no enabled provider instance", method),
- )
- }
- return strings.TrimSpace(selected.ProviderKey), nil
- }
- }
- func (s *PaymentConfigService) resolveEnabledVisibleMethodInstance(
- ctx context.Context,
- method string,
- ) (*dbent.PaymentProviderInstance, error) {
- if s == nil || s.entClient == nil {
- return nil, nil
- }
- method = NormalizeVisibleMethod(method)
- if method == "" {
- return nil, nil
- }
- instances, err := s.entClient.PaymentProviderInstance.Query().
- Where(paymentproviderinstance.EnabledEQ(true)).
- Order(paymentproviderinstance.BySortOrder()).
- All(ctx)
- if err != nil {
- return nil, fmt.Errorf("query enabled payment providers: %w", err)
- }
- matching := filterEnabledVisibleMethodInstances(instances, method)
- providerKey, err := s.resolveVisibleMethodProviderKey(ctx, method, matching)
- if err != nil {
- return nil, err
- }
- if providerKey == "" {
- if len(matching) == 0 {
- return nil, nil
- }
- return &dbent.PaymentProviderInstance{ProviderKey: ""}, nil
- }
- return selectVisibleMethodInstanceByProviderKey(matching, providerKey), nil
- }
- // UsesOfficialAlipayVisibleMethod reports whether the user-facing Alipay method
- // currently resolves to an enabled official Alipay provider instance.
- func (s *PaymentConfigService) UsesOfficialAlipayVisibleMethod(ctx context.Context) (bool, error) {
- instance, err := s.resolveEnabledVisibleMethodInstance(ctx, payment.TypeAlipay)
- if err != nil {
- return false, err
- }
- return isOfficialAlipayProviderInstance(instance), nil
- }
- func isOfficialAlipayProviderInstance(instance *dbent.PaymentProviderInstance) bool {
- return instance != nil && strings.EqualFold(strings.TrimSpace(instance.ProviderKey), payment.TypeAlipay)
- }
|