Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user