243 lines
6.8 KiB
Go
243 lines
6.8 KiB
Go
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)
|
|
switch method {
|
|
case payment.TypeAlipay, payment.TypeWxpay:
|
|
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)
|
|
}
|
|
}
|
|
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 != payment.TypeAlipay && method != payment.TypeWxpay {
|
|
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
|
|
}
|