491 lines
19 KiB
Go
491 lines
19 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"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"
|
|
)
|
|
|
|
const (
|
|
SettingPaymentEnabled = "payment_enabled"
|
|
SettingMinRechargeAmount = "MIN_RECHARGE_AMOUNT"
|
|
SettingMaxRechargeAmount = "MAX_RECHARGE_AMOUNT"
|
|
SettingDailyRechargeLimit = "DAILY_RECHARGE_LIMIT"
|
|
SettingOrderTimeoutMinutes = "ORDER_TIMEOUT_MINUTES"
|
|
SettingMaxPendingOrders = "MAX_PENDING_ORDERS"
|
|
SettingEnabledPaymentTypes = "ENABLED_PAYMENT_TYPES"
|
|
SettingLoadBalanceStrategy = "LOAD_BALANCE_STRATEGY"
|
|
SettingBalancePayDisabled = "BALANCE_PAYMENT_DISABLED"
|
|
SettingBalanceRechargeMult = "BALANCE_RECHARGE_MULTIPLIER"
|
|
SettingRechargeFeeRate = "RECHARGE_FEE_RATE"
|
|
SettingProductNamePrefix = "PRODUCT_NAME_PREFIX"
|
|
SettingProductNameSuffix = "PRODUCT_NAME_SUFFIX"
|
|
SettingHelpImageURL = "PAYMENT_HELP_IMAGE_URL"
|
|
SettingHelpText = "PAYMENT_HELP_TEXT"
|
|
SettingCancelRateLimitOn = "CANCEL_RATE_LIMIT_ENABLED"
|
|
SettingCancelRateLimitMax = "CANCEL_RATE_LIMIT_MAX"
|
|
SettingCancelWindowSize = "CANCEL_RATE_LIMIT_WINDOW"
|
|
SettingCancelWindowUnit = "CANCEL_RATE_LIMIT_UNIT"
|
|
SettingCancelWindowMode = "CANCEL_RATE_LIMIT_WINDOW_MODE"
|
|
)
|
|
|
|
// Default values for payment configuration settings.
|
|
const (
|
|
defaultOrderTimeoutMin = 30
|
|
defaultMaxPendingOrders = 3
|
|
)
|
|
|
|
// PaymentConfig holds the payment system configuration.
|
|
type PaymentConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
MinAmount float64 `json:"min_amount"`
|
|
MaxAmount float64 `json:"max_amount"`
|
|
DailyLimit float64 `json:"daily_limit"`
|
|
OrderTimeoutMin int `json:"order_timeout_minutes"`
|
|
MaxPendingOrders int `json:"max_pending_orders"`
|
|
EnabledTypes []string `json:"enabled_payment_types"`
|
|
BalanceDisabled bool `json:"balance_disabled"`
|
|
BalanceRechargeMultiplier float64 `json:"balance_recharge_multiplier"`
|
|
RechargeFeeRate float64 `json:"recharge_fee_rate"`
|
|
LoadBalanceStrategy string `json:"load_balance_strategy"`
|
|
ProductNamePrefix string `json:"product_name_prefix"`
|
|
ProductNameSuffix string `json:"product_name_suffix"`
|
|
HelpImageURL string `json:"help_image_url"`
|
|
HelpText string `json:"help_text"`
|
|
StripePublishableKey string `json:"stripe_publishable_key,omitempty"`
|
|
|
|
// Cancel rate limit settings
|
|
CancelRateLimitEnabled bool `json:"cancel_rate_limit_enabled"`
|
|
CancelRateLimitMax int `json:"cancel_rate_limit_max"`
|
|
CancelRateLimitWindow int `json:"cancel_rate_limit_window"`
|
|
CancelRateLimitUnit string `json:"cancel_rate_limit_unit"`
|
|
CancelRateLimitMode string `json:"cancel_rate_limit_window_mode"`
|
|
}
|
|
|
|
// UpdatePaymentConfigRequest contains fields to update payment configuration.
|
|
type UpdatePaymentConfigRequest struct {
|
|
Enabled *bool `json:"enabled"`
|
|
MinAmount *float64 `json:"min_amount"`
|
|
MaxAmount *float64 `json:"max_amount"`
|
|
DailyLimit *float64 `json:"daily_limit"`
|
|
OrderTimeoutMin *int `json:"order_timeout_minutes"`
|
|
MaxPendingOrders *int `json:"max_pending_orders"`
|
|
EnabledTypes []string `json:"enabled_payment_types"`
|
|
BalanceDisabled *bool `json:"balance_disabled"`
|
|
BalanceRechargeMultiplier *float64 `json:"balance_recharge_multiplier"`
|
|
RechargeFeeRate *float64 `json:"recharge_fee_rate"`
|
|
LoadBalanceStrategy *string `json:"load_balance_strategy"`
|
|
ProductNamePrefix *string `json:"product_name_prefix"`
|
|
ProductNameSuffix *string `json:"product_name_suffix"`
|
|
HelpImageURL *string `json:"help_image_url"`
|
|
HelpText *string `json:"help_text"`
|
|
|
|
// Cancel rate limit settings
|
|
CancelRateLimitEnabled *bool `json:"cancel_rate_limit_enabled"`
|
|
CancelRateLimitMax *int `json:"cancel_rate_limit_max"`
|
|
CancelRateLimitWindow *int `json:"cancel_rate_limit_window"`
|
|
CancelRateLimitUnit *string `json:"cancel_rate_limit_unit"`
|
|
CancelRateLimitMode *string `json:"cancel_rate_limit_window_mode"`
|
|
|
|
VisibleMethodAlipaySource *string `json:"payment_visible_method_alipay_source"`
|
|
VisibleMethodWxpaySource *string `json:"payment_visible_method_wxpay_source"`
|
|
VisibleMethodAlipayEnabled *bool `json:"payment_visible_method_alipay_enabled"`
|
|
VisibleMethodWxpayEnabled *bool `json:"payment_visible_method_wxpay_enabled"`
|
|
}
|
|
|
|
// MethodLimits holds per-payment-type limits.
|
|
type MethodLimits struct {
|
|
PaymentType string `json:"payment_type"`
|
|
FeeRate float64 `json:"fee_rate"`
|
|
DailyLimit float64 `json:"daily_limit"`
|
|
SingleMin float64 `json:"single_min"`
|
|
SingleMax float64 `json:"single_max"`
|
|
}
|
|
|
|
// MethodLimitsResponse is the full response for the user-facing /limits API.
|
|
// It includes per-method limits and the global widest range (union of all methods).
|
|
type MethodLimitsResponse struct {
|
|
Methods map[string]MethodLimits `json:"methods"`
|
|
GlobalMin float64 `json:"global_min"` // 0 = no minimum
|
|
GlobalMax float64 `json:"global_max"` // 0 = no maximum
|
|
}
|
|
|
|
type CreateProviderInstanceRequest struct {
|
|
ProviderKey string `json:"provider_key"`
|
|
Name string `json:"name"`
|
|
Config map[string]string `json:"config"`
|
|
SupportedTypes []string `json:"supported_types"`
|
|
Enabled bool `json:"enabled"`
|
|
PaymentMode string `json:"payment_mode"`
|
|
SortOrder int `json:"sort_order"`
|
|
Limits string `json:"limits"`
|
|
RefundEnabled bool `json:"refund_enabled"`
|
|
AllowUserRefund bool `json:"allow_user_refund"`
|
|
}
|
|
|
|
type UpdateProviderInstanceRequest struct {
|
|
Name *string `json:"name"`
|
|
Config map[string]string `json:"config"`
|
|
SupportedTypes []string `json:"supported_types"`
|
|
Enabled *bool `json:"enabled"`
|
|
PaymentMode *string `json:"payment_mode"`
|
|
SortOrder *int `json:"sort_order"`
|
|
Limits *string `json:"limits"`
|
|
RefundEnabled *bool `json:"refund_enabled"`
|
|
AllowUserRefund *bool `json:"allow_user_refund"`
|
|
}
|
|
type CreatePlanRequest struct {
|
|
GroupID int64 `json:"group_id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Price float64 `json:"price"`
|
|
OriginalPrice *float64 `json:"original_price"`
|
|
ValidityDays int `json:"validity_days"`
|
|
ValidityUnit string `json:"validity_unit"`
|
|
Features string `json:"features"`
|
|
ProductName string `json:"product_name"`
|
|
ForSale bool `json:"for_sale"`
|
|
SortOrder int `json:"sort_order"`
|
|
}
|
|
|
|
type UpdatePlanRequest struct {
|
|
GroupID *int64 `json:"group_id"`
|
|
Name *string `json:"name"`
|
|
Description *string `json:"description"`
|
|
Price *float64 `json:"price"`
|
|
OriginalPrice *float64 `json:"original_price"`
|
|
ValidityDays *int `json:"validity_days"`
|
|
ValidityUnit *string `json:"validity_unit"`
|
|
Features *string `json:"features"`
|
|
ProductName *string `json:"product_name"`
|
|
ForSale *bool `json:"for_sale"`
|
|
SortOrder *int `json:"sort_order"`
|
|
}
|
|
|
|
// PaymentConfigService manages payment configuration and CRUD for
|
|
// provider instances, channels, and subscription plans.
|
|
type PaymentConfigService struct {
|
|
entClient *dbent.Client
|
|
settingRepo SettingRepository
|
|
encryptionKey []byte
|
|
}
|
|
|
|
// NewPaymentConfigService creates a new PaymentConfigService.
|
|
func NewPaymentConfigService(entClient *dbent.Client, settingRepo SettingRepository, encryptionKey []byte) *PaymentConfigService {
|
|
return &PaymentConfigService{entClient: entClient, settingRepo: settingRepo, encryptionKey: encryptionKey}
|
|
}
|
|
|
|
// IsPaymentEnabled returns whether the payment system is enabled.
|
|
func (s *PaymentConfigService) IsPaymentEnabled(ctx context.Context) bool {
|
|
val, err := s.settingRepo.GetValue(ctx, SettingPaymentEnabled)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return val == "true"
|
|
}
|
|
|
|
// GetPaymentConfig returns the full payment configuration.
|
|
func (s *PaymentConfigService) GetPaymentConfig(ctx context.Context) (*PaymentConfig, error) {
|
|
keys := []string{
|
|
SettingPaymentEnabled, SettingMinRechargeAmount, SettingMaxRechargeAmount,
|
|
SettingDailyRechargeLimit, SettingOrderTimeoutMinutes, SettingMaxPendingOrders,
|
|
SettingEnabledPaymentTypes, SettingBalancePayDisabled, SettingBalanceRechargeMult, SettingRechargeFeeRate, SettingLoadBalanceStrategy,
|
|
SettingProductNamePrefix, SettingProductNameSuffix,
|
|
SettingHelpImageURL, SettingHelpText,
|
|
SettingCancelRateLimitOn, SettingCancelRateLimitMax,
|
|
SettingCancelWindowSize, SettingCancelWindowUnit, SettingCancelWindowMode,
|
|
SettingPaymentVisibleMethodAlipayEnabled, SettingPaymentVisibleMethodAlipaySource,
|
|
SettingPaymentVisibleMethodWxpayEnabled, SettingPaymentVisibleMethodWxpaySource,
|
|
}
|
|
vals, err := s.settingRepo.GetMultiple(ctx, keys)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get payment config settings: %w", err)
|
|
}
|
|
cfg := s.parsePaymentConfig(vals)
|
|
if s.entClient != nil {
|
|
instances, err := s.entClient.PaymentProviderInstance.Query().
|
|
Where(paymentproviderinstance.EnabledEQ(true)).
|
|
All(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list enabled provider instances: %w", err)
|
|
}
|
|
cfg.EnabledTypes = applyVisibleMethodRoutingToEnabledTypes(cfg.EnabledTypes, vals, buildVisibleMethodSourceAvailability(instances))
|
|
} else {
|
|
cfg.EnabledTypes = applyVisibleMethodRoutingToEnabledTypes(cfg.EnabledTypes, vals, nil)
|
|
}
|
|
// Load Stripe publishable key from the first enabled Stripe provider instance
|
|
cfg.StripePublishableKey = s.getStripePublishableKey(ctx)
|
|
return cfg, nil
|
|
}
|
|
|
|
func (s *PaymentConfigService) parsePaymentConfig(vals map[string]string) *PaymentConfig {
|
|
cfg := &PaymentConfig{
|
|
Enabled: vals[SettingPaymentEnabled] == "true",
|
|
MinAmount: pcParseFloat(vals[SettingMinRechargeAmount], 1),
|
|
MaxAmount: pcParseFloat(vals[SettingMaxRechargeAmount], 0),
|
|
DailyLimit: pcParseFloat(vals[SettingDailyRechargeLimit], 0),
|
|
OrderTimeoutMin: pcParseInt(vals[SettingOrderTimeoutMinutes], defaultOrderTimeoutMin),
|
|
MaxPendingOrders: pcParseInt(vals[SettingMaxPendingOrders], defaultMaxPendingOrders),
|
|
BalanceDisabled: vals[SettingBalancePayDisabled] == "true",
|
|
BalanceRechargeMultiplier: normalizeBalanceRechargeMultiplier(pcParseFloat(vals[SettingBalanceRechargeMult], defaultBalanceRechargeMultiplier)),
|
|
RechargeFeeRate: pcParseFloat(vals[SettingRechargeFeeRate], 0),
|
|
LoadBalanceStrategy: vals[SettingLoadBalanceStrategy],
|
|
ProductNamePrefix: vals[SettingProductNamePrefix],
|
|
ProductNameSuffix: vals[SettingProductNameSuffix],
|
|
HelpImageURL: vals[SettingHelpImageURL],
|
|
HelpText: vals[SettingHelpText],
|
|
|
|
CancelRateLimitEnabled: vals[SettingCancelRateLimitOn] == "true",
|
|
CancelRateLimitMax: pcParseInt(vals[SettingCancelRateLimitMax], 10),
|
|
CancelRateLimitWindow: pcParseInt(vals[SettingCancelWindowSize], 1),
|
|
CancelRateLimitUnit: vals[SettingCancelWindowUnit],
|
|
CancelRateLimitMode: vals[SettingCancelWindowMode],
|
|
}
|
|
if cfg.LoadBalanceStrategy == "" {
|
|
cfg.LoadBalanceStrategy = payment.DefaultLoadBalanceStrategy
|
|
}
|
|
if raw := vals[SettingEnabledPaymentTypes]; raw != "" {
|
|
types := make([]string, 0, len(strings.Split(raw, ",")))
|
|
for _, t := range strings.Split(raw, ",") {
|
|
t = strings.TrimSpace(t)
|
|
if t != "" {
|
|
types = append(types, t)
|
|
}
|
|
}
|
|
cfg.EnabledTypes = NormalizeVisibleMethods(types)
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
// getStripePublishableKey finds the publishable key from the first enabled Stripe provider instance.
|
|
func (s *PaymentConfigService) getStripePublishableKey(ctx context.Context) string {
|
|
if s.entClient == nil {
|
|
return ""
|
|
}
|
|
instances, err := s.entClient.PaymentProviderInstance.Query().
|
|
Where(
|
|
paymentproviderinstance.EnabledEQ(true),
|
|
paymentproviderinstance.ProviderKeyEQ(payment.TypeStripe),
|
|
).Limit(1).All(ctx)
|
|
if err != nil || len(instances) == 0 {
|
|
return ""
|
|
}
|
|
cfg, err := s.decryptConfig(instances[0].Config)
|
|
if err != nil || cfg == nil {
|
|
return ""
|
|
}
|
|
return cfg[payment.ConfigKeyPublishableKey]
|
|
}
|
|
|
|
// UpdatePaymentConfig updates the payment configuration settings.
|
|
// NOTE: This function exceeds 30 lines because each field requires an independent
|
|
// nil-check before serialisation — this is inherent to patch-style update patterns
|
|
// and cannot be meaningfully decomposed without introducing unnecessary abstraction.
|
|
func (s *PaymentConfigService) UpdatePaymentConfig(ctx context.Context, req UpdatePaymentConfigRequest) error {
|
|
if req.BalanceRechargeMultiplier != nil {
|
|
if math.IsNaN(*req.BalanceRechargeMultiplier) || math.IsInf(*req.BalanceRechargeMultiplier, 0) || *req.BalanceRechargeMultiplier <= 0 {
|
|
return infraerrors.BadRequest("INVALID_BALANCE_RECHARGE_MULTIPLIER", "balance recharge multiplier must be greater than 0")
|
|
}
|
|
}
|
|
if req.RechargeFeeRate != nil {
|
|
v := *req.RechargeFeeRate
|
|
if math.IsNaN(v) || math.IsInf(v, 0) || v < 0 || v > 100 {
|
|
return infraerrors.BadRequest("INVALID_RECHARGE_FEE_RATE", "recharge fee rate must be between 0 and 100")
|
|
}
|
|
// Enforce max 2 decimal places
|
|
if math.Round(v*100) != v*100 {
|
|
return infraerrors.BadRequest("INVALID_RECHARGE_FEE_RATE", "recharge fee rate allows at most 2 decimal places")
|
|
}
|
|
}
|
|
m := map[string]string{
|
|
SettingPaymentEnabled: formatBoolOrEmpty(req.Enabled),
|
|
SettingMinRechargeAmount: formatPositiveFloat(req.MinAmount),
|
|
SettingMaxRechargeAmount: formatPositiveFloat(req.MaxAmount),
|
|
SettingDailyRechargeLimit: formatPositiveFloat(req.DailyLimit),
|
|
SettingOrderTimeoutMinutes: formatPositiveInt(req.OrderTimeoutMin),
|
|
SettingMaxPendingOrders: formatPositiveInt(req.MaxPendingOrders),
|
|
SettingBalancePayDisabled: formatBoolOrEmpty(req.BalanceDisabled),
|
|
SettingBalanceRechargeMult: formatPositiveFloat(req.BalanceRechargeMultiplier),
|
|
SettingRechargeFeeRate: formatNonNegativeFloat(req.RechargeFeeRate),
|
|
SettingLoadBalanceStrategy: derefStr(req.LoadBalanceStrategy),
|
|
SettingProductNamePrefix: derefStr(req.ProductNamePrefix),
|
|
SettingProductNameSuffix: derefStr(req.ProductNameSuffix),
|
|
SettingHelpImageURL: derefStr(req.HelpImageURL),
|
|
SettingHelpText: derefStr(req.HelpText),
|
|
SettingCancelRateLimitOn: formatBoolOrEmpty(req.CancelRateLimitEnabled),
|
|
SettingCancelRateLimitMax: formatPositiveInt(req.CancelRateLimitMax),
|
|
SettingCancelWindowSize: formatPositiveInt(req.CancelRateLimitWindow),
|
|
SettingCancelWindowUnit: derefStr(req.CancelRateLimitUnit),
|
|
SettingCancelWindowMode: derefStr(req.CancelRateLimitMode),
|
|
SettingPaymentVisibleMethodAlipaySource: derefStr(req.VisibleMethodAlipaySource),
|
|
SettingPaymentVisibleMethodWxpaySource: derefStr(req.VisibleMethodWxpaySource),
|
|
SettingPaymentVisibleMethodAlipayEnabled: formatBoolOrEmpty(req.VisibleMethodAlipayEnabled),
|
|
SettingPaymentVisibleMethodWxpayEnabled: formatBoolOrEmpty(req.VisibleMethodWxpayEnabled),
|
|
}
|
|
if req.EnabledTypes != nil {
|
|
m[SettingEnabledPaymentTypes] = strings.Join(req.EnabledTypes, ",")
|
|
} else {
|
|
m[SettingEnabledPaymentTypes] = ""
|
|
}
|
|
return s.settingRepo.SetMultiple(ctx, m)
|
|
}
|
|
|
|
func formatBoolOrEmpty(v *bool) string {
|
|
if v == nil {
|
|
return ""
|
|
}
|
|
return strconv.FormatBool(*v)
|
|
}
|
|
|
|
func formatPositiveFloat(v *float64) string {
|
|
if v == nil || *v <= 0 {
|
|
return "" // empty → parsePaymentConfig uses default
|
|
}
|
|
return strconv.FormatFloat(*v, 'f', 2, 64)
|
|
}
|
|
|
|
func formatNonNegativeFloat(v *float64) string {
|
|
if v == nil || *v < 0 {
|
|
return ""
|
|
}
|
|
return strconv.FormatFloat(*v, 'f', 2, 64)
|
|
}
|
|
|
|
func formatPositiveInt(v *int) string {
|
|
if v == nil || *v <= 0 {
|
|
return ""
|
|
}
|
|
return strconv.Itoa(*v)
|
|
}
|
|
|
|
func derefStr(v *string) string {
|
|
if v == nil {
|
|
return ""
|
|
}
|
|
return *v
|
|
}
|
|
|
|
func splitTypes(s string) []string {
|
|
if s == "" {
|
|
return nil
|
|
}
|
|
parts := strings.Split(s, ",")
|
|
result := make([]string, 0, len(parts))
|
|
for _, p := range parts {
|
|
p = strings.TrimSpace(p)
|
|
if p != "" {
|
|
result = append(result, p)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func joinTypes(types []string) string {
|
|
return strings.Join(types, ",")
|
|
}
|
|
|
|
func pcParseFloat(s string, defaultVal float64) float64 {
|
|
if s == "" {
|
|
return defaultVal
|
|
}
|
|
v, err := strconv.ParseFloat(s, 64)
|
|
if err != nil {
|
|
return defaultVal
|
|
}
|
|
return v
|
|
}
|
|
|
|
func pcParseInt(s string, defaultVal int) int {
|
|
if s == "" {
|
|
return defaultVal
|
|
}
|
|
v, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return defaultVal
|
|
}
|
|
return v
|
|
}
|
|
|
|
func buildVisibleMethodSourceAvailability(instances []*dbent.PaymentProviderInstance) map[string]bool {
|
|
available := make(map[string]bool, 4)
|
|
for _, inst := range instances {
|
|
switch inst.ProviderKey {
|
|
case payment.TypeAlipay:
|
|
if inst.SupportedTypes == "" || payment.InstanceSupportsType(inst.SupportedTypes, payment.TypeAlipay) || payment.InstanceSupportsType(inst.SupportedTypes, payment.TypeAlipayDirect) {
|
|
available[VisibleMethodSourceOfficialAlipay] = true
|
|
}
|
|
case payment.TypeWxpay:
|
|
if inst.SupportedTypes == "" || payment.InstanceSupportsType(inst.SupportedTypes, payment.TypeWxpay) || payment.InstanceSupportsType(inst.SupportedTypes, payment.TypeWxpayDirect) {
|
|
available[VisibleMethodSourceOfficialWechat] = true
|
|
}
|
|
case payment.TypeEasyPay:
|
|
for _, supportedType := range splitTypes(inst.SupportedTypes) {
|
|
switch NormalizeVisibleMethod(supportedType) {
|
|
case payment.TypeAlipay:
|
|
available[VisibleMethodSourceEasyPayAlipay] = true
|
|
case payment.TypeWxpay:
|
|
available[VisibleMethodSourceEasyPayWechat] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return available
|
|
}
|
|
|
|
func applyVisibleMethodRoutingToEnabledTypes(base []string, vals map[string]string, available map[string]bool) []string {
|
|
shouldExpose := map[string]bool{
|
|
payment.TypeAlipay: visibleMethodShouldBeExposed(payment.TypeAlipay, vals, available),
|
|
payment.TypeWxpay: visibleMethodShouldBeExposed(payment.TypeWxpay, vals, available),
|
|
}
|
|
|
|
seen := make(map[string]struct{}, len(base)+2)
|
|
out := make([]string, 0, len(base)+2)
|
|
appendType := func(paymentType string) {
|
|
paymentType = NormalizeVisibleMethod(paymentType)
|
|
if paymentType == "" {
|
|
return
|
|
}
|
|
if _, ok := seen[paymentType]; ok {
|
|
return
|
|
}
|
|
seen[paymentType] = struct{}{}
|
|
out = append(out, paymentType)
|
|
}
|
|
|
|
for _, paymentType := range base {
|
|
visibleMethod := NormalizeVisibleMethod(paymentType)
|
|
switch visibleMethod {
|
|
case payment.TypeAlipay, payment.TypeWxpay:
|
|
if shouldExpose[visibleMethod] {
|
|
appendType(visibleMethod)
|
|
}
|
|
default:
|
|
appendType(visibleMethod)
|
|
}
|
|
}
|
|
|
|
for _, visibleMethod := range []string{payment.TypeAlipay, payment.TypeWxpay} {
|
|
if shouldExpose[visibleMethod] {
|
|
appendType(visibleMethod)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func visibleMethodShouldBeExposed(method string, vals map[string]string, available map[string]bool) bool {
|
|
enabledKey := visibleMethodEnabledSettingKey(method)
|
|
sourceKey := visibleMethodSourceSettingKey(method)
|
|
if enabledKey == "" || sourceKey == "" || vals[enabledKey] != "true" {
|
|
return false
|
|
}
|
|
source := NormalizeVisibleMethodSource(method, vals[sourceKey])
|
|
return source != "" && available[source]
|
|
}
|