mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
fix(payment): critical fixes from agent audit
- Fix CreateOrderResult field names (snake_case → camelCase to match backend) - Fix MethodLimits JSON tags to consistent snake_case - Fix Stripe webhook header case sensitivity (lowercase keys for map lookup) - Fix MaxAmount=0 backend validation (0 = no limit, not reject all) - Fix structured error for INVALID_AMOUNT per CLAUDE.md spec
This commit is contained in:
@@ -1 +1 @@
|
||||
0.1.108.63
|
||||
0.1.108.64
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/payment"
|
||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||
@@ -70,7 +71,7 @@ func (h *PaymentWebhookHandler) handleNotify(c *gin.Context, providerKey string)
|
||||
|
||||
headers := make(map[string]string)
|
||||
for k := range c.Request.Header {
|
||||
headers[k] = c.GetHeader(k)
|
||||
headers[strings.ToLower(k)] = c.GetHeader(k)
|
||||
}
|
||||
|
||||
notification, err := provider.VerifyNotification(c.Request.Context(), string(body), headers)
|
||||
|
||||
@@ -78,11 +78,11 @@ type UpdatePaymentConfigRequest struct {
|
||||
|
||||
// MethodLimits holds per-payment-type limits.
|
||||
type MethodLimits struct {
|
||||
PaymentType string `json:"paymentType"`
|
||||
FeeRate float64 `json:"feeRate"`
|
||||
PaymentType string `json:"payment_type"`
|
||||
FeeRate float64 `json:"fee_rate"`
|
||||
DailyLimit float64 `json:"daily_limit"`
|
||||
SingleMin float64 `json:"singleMin"`
|
||||
SingleMax float64 `json:"singleMax"`
|
||||
SingleMin float64 `json:"single_min"`
|
||||
SingleMax float64 `json:"single_max"`
|
||||
}
|
||||
|
||||
type CreateProviderInstanceRequest struct {
|
||||
|
||||
@@ -210,8 +210,9 @@ func (s *PaymentService) validateOrderInput(ctx context.Context, req CreateOrder
|
||||
if req.OrderType == "subscription" {
|
||||
return s.validateSubOrder(ctx, req)
|
||||
}
|
||||
if req.Amount < cfg.MinAmount || req.Amount > cfg.MaxAmount {
|
||||
return nil, infraerrors.BadRequest("INVALID_AMOUNT", fmt.Sprintf("amount must be between %.2f and %.2f", cfg.MinAmount, cfg.MaxAmount))
|
||||
if (cfg.MinAmount > 0 && req.Amount < cfg.MinAmount) || (cfg.MaxAmount > 0 && req.Amount > cfg.MaxAmount) {
|
||||
return nil, infraerrors.BadRequest("INVALID_AMOUNT", "amount out of range").
|
||||
WithMetadata(map[string]string{"min": fmt.Sprintf("%.2f", cfg.MinAmount), "max": fmt.Sprintf("%.2f", cfg.MaxAmount)})
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -120,12 +120,12 @@ export interface CreateOrderRequest {
|
||||
}
|
||||
|
||||
export interface CreateOrderResult {
|
||||
order_id: number
|
||||
pay_url?: string
|
||||
qr_code?: string
|
||||
client_secret?: string
|
||||
pay_amount: number
|
||||
expires_at: string
|
||||
orderId: number
|
||||
payUrl?: string
|
||||
qrCode?: string
|
||||
clientSecret?: string
|
||||
payAmount: number
|
||||
expiresAt: string
|
||||
}
|
||||
|
||||
export interface DashboardStats {
|
||||
|
||||
@@ -230,12 +230,12 @@ async function createOrder(orderAmount: number, orderType: string, planId?: numb
|
||||
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 } })
|
||||
} else if (result.qr_code) {
|
||||
router.push({ path: '/payment/qrcode', query: { order_id: String(result.order_id), qr: result.qr_code || '', pay_url: result.pay_url || '' } })
|
||||
} else if (result.pay_url) {
|
||||
window.location.href = result.pay_url
|
||||
if (result.clientSecret) {
|
||||
router.push({ path: '/payment/stripe', query: { order_id: String(result.orderId), client_secret: result.clientSecret } })
|
||||
} else if (result.qrCode) {
|
||||
router.push({ path: '/payment/qrcode', query: { order_id: String(result.orderId), qr: result.qrCode || '', pay_url: result.payUrl || '' } })
|
||||
} else if (result.payUrl) {
|
||||
window.location.href = result.payUrl
|
||||
} else {
|
||||
errorMessage.value = t('payment.result.failed')
|
||||
appStore.showError(errorMessage.value)
|
||||
|
||||
Reference in New Issue
Block a user