From 55b526582bd6ede30fb8e4e6a46a0b58eb9d9f6a Mon Sep 17 00:00:00 2001 From: erio Date: Tue, 7 Apr 2026 00:23:54 +0800 Subject: [PATCH] feat(payment): Stripe multi-method support + card type + hide single-type selector - Add TypeCard payment type for bank card payments - Stripe provider now supports card/alipay/wxpay payment methods - Map payment types to Stripe payment_method_types (card, alipay, wechat_pay) - Hide supported types selector for single-type providers (alipay/wxpay official) - Add card i18n labels and PaymentMethodSelector styles --- backend/cmd/server/VERSION | 2 +- backend/internal/payment/provider/stripe.go | 43 +++++++++++++++---- backend/internal/payment/types.go | 3 ++ .../payment/PaymentMethodSelector.vue | 6 +-- .../payment/PaymentProviderDialog.vue | 4 +- .../src/components/payment/providerConfig.ts | 2 +- frontend/src/i18n/locales/en.ts | 1 + frontend/src/i18n/locales/zh.ts | 1 + frontend/src/types/payment.ts | 2 +- frontend/src/views/admin/SettingsView.vue | 1 + 10 files changed, 48 insertions(+), 17 deletions(-) diff --git a/backend/cmd/server/VERSION b/backend/cmd/server/VERSION index 199b791761..453618b234 100644 --- a/backend/cmd/server/VERSION +++ b/backend/cmd/server/VERSION @@ -1 +1 @@ -0.1.108.36 +0.1.108.37 diff --git a/backend/internal/payment/provider/stripe.go b/backend/internal/payment/provider/stripe.go index cc591d5c0f..5285ee9930 100644 --- a/backend/internal/payment/provider/stripe.go +++ b/backend/internal/payment/provider/stripe.go @@ -59,7 +59,7 @@ func (s *Stripe) GetPublishableKey() string { func (s *Stripe) Name() string { return "Stripe" } func (s *Stripe) ProviderKey() string { return "stripe" } func (s *Stripe) SupportedTypes() []payment.PaymentType { - return []payment.PaymentType{payment.TypeStripe} + return []payment.PaymentType{payment.TypeCard, payment.TypeAlipay, payment.TypeWxpay} } // centsToYuan converts an amount in cents (int64) to yuan (float64). @@ -76,6 +76,13 @@ func yuanToCents(amountStr string) (int64, error) { return int64(math.Round(amount * 100)), nil } +// stripePaymentMethodTypes maps our PaymentType to Stripe payment_method_types. +var stripePaymentMethodTypes = map[string][]string{ + payment.TypeCard: {"card"}, + payment.TypeAlipay: {"alipay"}, + payment.TypeWxpay: {"wechat_pay"}, +} + // CreatePayment creates a Stripe PaymentIntent. func (s *Stripe) CreatePayment(ctx context.Context, req payment.CreatePaymentRequest) (*payment.CreatePaymentResponse, error) { s.ensureInit() @@ -85,15 +92,33 @@ func (s *Stripe) CreatePayment(ctx context.Context, req payment.CreatePaymentReq return nil, fmt.Errorf("stripe create payment: %w", err) } - params := &stripe.PaymentIntentCreateParams{ - Amount: stripe.Int64(amountInCents), - Currency: stripe.String(stripeCurrency), - AutomaticPaymentMethods: &stripe.PaymentIntentCreateAutomaticPaymentMethodsParams{ - Enabled: stripe.Bool(true), - }, - Description: stripe.String(req.Subject), - Metadata: map[string]string{"orderId": req.OrderID}, + methods, ok := stripePaymentMethodTypes[req.PaymentType] + if !ok { + methods = []string{"card"} } + + pmTypes := make([]*string, len(methods)) + for i, m := range methods { + pmTypes[i] = stripe.String(m) + } + + params := &stripe.PaymentIntentCreateParams{ + Amount: stripe.Int64(amountInCents), + Currency: stripe.String(stripeCurrency), + PaymentMethodTypes: pmTypes, + Description: stripe.String(req.Subject), + Metadata: map[string]string{"orderId": req.OrderID}, + } + + // WeChat Pay requires payment_method_options with client type + if req.PaymentType == payment.TypeWxpay { + params.PaymentMethodOptions = &stripe.PaymentIntentCreatePaymentMethodOptionsParams{ + WeChatPay: &stripe.PaymentIntentCreatePaymentMethodOptionsWeChatPayParams{ + Client: stripe.String("web"), + }, + } + } + params.SetIdempotencyKey(fmt.Sprintf("pi-%s", req.OrderID)) params.Context = ctx diff --git a/backend/internal/payment/types.go b/backend/internal/payment/types.go index 5e8dc41610..15c488cb30 100644 --- a/backend/internal/payment/types.go +++ b/backend/internal/payment/types.go @@ -14,6 +14,7 @@ const ( TypeAlipayDirect PaymentType = "alipay_direct" TypeWxpayDirect PaymentType = "wxpay_direct" TypeStripe PaymentType = "stripe" + TypeCard PaymentType = "card" TypeEasyPay PaymentType = "easypay" ) @@ -39,6 +40,8 @@ func GetBasePaymentType(t string) string { switch { case t == "easypay": return "easypay" + case t == "card": + return "card" case len(t) >= 6 && t[:6] == "alipay": return "alipay" case len(t) >= 5 && t[:5] == "wxpay": diff --git a/frontend/src/components/payment/PaymentMethodSelector.vue b/frontend/src/components/payment/PaymentMethodSelector.vue index c315c5a068..6f1952b4be 100644 --- a/frontend/src/components/payment/PaymentMethodSelector.vue +++ b/frontend/src/components/payment/PaymentMethodSelector.vue @@ -63,14 +63,14 @@ const emit = defineEmits<{ const { t } = useI18n() function isStripe(type: string) { - return type === 'stripe' + return type === 'stripe' || type === 'card' } function iconBgClass(type: string): string { if (type === 'easypay') return 'bg-[#FF6B35]' if (type.includes('alipay')) return 'bg-[#00AEEF]' if (type.includes('wxpay')) return 'bg-[#07C160]' - if (type === 'stripe') return 'bg-[#635bff]' + if (type === 'stripe' || type === 'card') return 'bg-[#635bff]' return 'bg-gray-500' } @@ -85,7 +85,7 @@ function methodSelectedClass(type: string): string { if (type === 'easypay') return 'border-[#FF6B35] bg-orange-50 text-gray-900 shadow-sm dark:bg-orange-950 dark:text-gray-100' if (type.includes('alipay')) return 'border-[#00AEEF] bg-blue-50 text-gray-900 shadow-sm dark:bg-blue-950 dark:text-gray-100' if (type.includes('wxpay')) return 'border-[#07C160] bg-green-50 text-gray-900 shadow-sm dark:bg-green-950 dark:text-gray-100' - if (type === 'stripe') return 'border-[#635bff] bg-indigo-50 text-gray-900 shadow-sm dark:bg-indigo-950 dark:text-gray-100' + if (type === 'stripe' || type === 'card') return 'border-[#635bff] bg-indigo-50 text-gray-900 shadow-sm dark:bg-indigo-950 dark:text-gray-100' return 'border-primary-500 bg-primary-50 text-gray-900 shadow-sm dark:bg-primary-950 dark:text-gray-100' } diff --git a/frontend/src/components/payment/PaymentProviderDialog.vue b/frontend/src/components/payment/PaymentProviderDialog.vue index 9c833192e6..e56df3bffa 100644 --- a/frontend/src/components/payment/PaymentProviderDialog.vue +++ b/frontend/src/components/payment/PaymentProviderDialog.vue @@ -29,8 +29,8 @@ - -
+ +