fix(payment): fix order creation + show actual provider types on payment page

- Fix CreateOrderRequest field name mismatch (payment_type → paymentType)
- Payment page now discovers available types from providers (not global config)
- Backend GetLimits returns map keyed by payment type
- EasyPay shows 3 buttons (跳转/支付宝/微信), Stripe shows card/alipay/wxpay/link
- Auto-select first method after limits loaded
This commit is contained in:
erio
2026-04-07 03:20:26 +08:00
parent 11701e9b5e
commit 82cc410cdf
5 changed files with 47 additions and 16 deletions
+1 -1
View File
@@ -1 +1 @@
0.1.108.61
0.1.108.62
+7 -7
View File
@@ -61,20 +61,20 @@ func (h *PaymentHandler) GetChannels(c *gin.Context) {
response.Success(c, channels)
}
// GetLimits returns per-payment-type limits.
// GetLimits returns per-payment-type limits derived from enabled provider instances.
// GET /api/v1/payment/limits
func (h *PaymentHandler) GetLimits(c *gin.Context) {
cfg, err := h.configService.GetPaymentConfig(c.Request.Context())
limits, err := h.configService.GetAvailableMethodLimits(c.Request.Context())
if err != nil {
response.ErrorFrom(c, err)
return
}
limits, err := h.configService.GetMethodLimits(c.Request.Context(), cfg.EnabledTypes)
if err != nil {
response.ErrorFrom(c, err)
return
// Return as map keyed by payment type (frontend expects Record<string, MethodLimit>)
result := make(map[string]service.MethodLimits, len(limits))
for _, ml := range limits {
result[ml.PaymentType] = ml
}
response.Success(c, limits)
response.Success(c, result)
}
// CreateOrderRequest is the request body for creating a payment order.
@@ -606,6 +606,36 @@ func (s *PaymentConfigService) GetPlan(ctx context.Context, id int64) (*dbent.Su
return plan, nil
}
// GetAvailableMethodLimits collects all payment types from enabled provider
// instances and returns limits for each. This is used by the user-facing payment
// page to discover which payment methods are actually available.
func (s *PaymentConfigService) GetAvailableMethodLimits(ctx context.Context) ([]MethodLimits, error) {
instances, err := s.entClient.PaymentProviderInstance.Query().
Where(paymentproviderinstance.EnabledEQ(true)).All(ctx)
if err != nil {
return nil, fmt.Errorf("query provider instances: %w", err)
}
// Collect unique payment types from all enabled providers
typeSet := make(map[string]bool)
for _, inst := range instances {
for _, t := range splitTypes(inst.SupportedTypes) {
typeSet[t] = true
}
}
result := make([]MethodLimits, 0, len(typeSet))
for pt := range typeSet {
ml := MethodLimits{PaymentType: pt}
for _, inst := range instances {
if !payment.InstanceSupportsType(inst.SupportedTypes, pt) {
continue
}
pcApplyInstanceLimits(inst, pt, &ml)
}
result = append(result, ml)
}
return result, nil
}
// GetMethodLimits returns per-payment-type limits from enabled provider instances.
func (s *PaymentConfigService) GetMethodLimits(ctx context.Context, types []string) ([]MethodLimits, error) {
instances, err := s.entClient.PaymentProviderInstance.Query().
+3 -3
View File
@@ -114,9 +114,9 @@ export interface ProviderInstance {
export interface CreateOrderRequest {
amount: number
payment_type: string
order_type: string
plan_id?: number
paymentType: string
orderType: string
planId?: number
}
export interface CreateOrderResult {
+6 -5
View File
@@ -156,7 +156,8 @@ const tabs = computed(() => {
return result
})
const enabledMethods = computed(() => config.value?.enabled_payment_types ?? [])
// Available methods derived from limits API (actual provider types)
const enabledMethods = computed(() => Object.keys(methodLimits.value))
// 0 = no limit; provider-level overrides global
const minAmount = computed(() => {
const limit = methodLimits.value[selectedMethod.value]
@@ -225,9 +226,9 @@ async function createOrder(orderAmount: number, orderType: string, planId?: numb
try {
const result = await paymentStore.createOrder({
amount: orderAmount,
payment_type: selectedMethod.value,
order_type: orderType,
plan_id: planId,
paymentType: selectedMethod.value,
orderType: orderType,
planId: planId,
})
if (result.client_secret) {
router.push({ path: '/payment/stripe', query: { order_id: String(result.order_id), client_secret: result.client_secret } })
@@ -260,11 +261,11 @@ watch(() => activeTab.value, (tab) => {
onMounted(async () => {
try {
await paymentStore.fetchConfig(true)
if (enabledMethods.value.length) selectedMethod.value = enabledMethods.value[0]
try {
const limitsRes = await paymentAPI.getLimits()
methodLimits.value = limitsRes.data
} catch (e) { /* limits endpoint may not exist */ }
if (enabledMethods.value.length) selectedMethod.value = enabledMethods.value[0]
if (config.value?.balance_disabled) {
activeTab.value = 'subscription'
await loadPlans()