mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
fix: structured errors, slog logging, snake_case JSON tags
- Registry uses infraerrors.NotFound instead of fmt.Errorf - Webhook handler uses slog instead of log.Printf - PaymentConfigService JSON tags unified to snake_case - PaymentOrderExpiryService uses slog - Remove duplicate declarations in registry.go
This commit is contained in:
@@ -2,7 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/payment"
|
||||
@@ -53,14 +53,14 @@ func (h *PaymentWebhookHandler) StripeWebhook(c *gin.Context) {
|
||||
func (h *PaymentWebhookHandler) handleNotify(c *gin.Context, providerKey string) {
|
||||
body, err := io.ReadAll(io.LimitReader(c.Request.Body, 1<<20)) // 1MB limit
|
||||
if err != nil {
|
||||
log.Printf("[Payment Webhook] failed to read body for %s: %v", providerKey, err)
|
||||
slog.Error("[Payment Webhook] failed to read body", "provider", providerKey, "error", err)
|
||||
c.String(http.StatusBadRequest, "failed to read body")
|
||||
return
|
||||
}
|
||||
|
||||
provider, err := h.registry.GetProviderByKey(providerKey)
|
||||
if err != nil {
|
||||
log.Printf("[Payment Webhook] provider %s not registered: %v", providerKey, err)
|
||||
slog.Warn("[Payment Webhook] provider not registered", "provider", providerKey, "error", err)
|
||||
c.String(http.StatusOK, successResponse(providerKey))
|
||||
return
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func (h *PaymentWebhookHandler) handleNotify(c *gin.Context, providerKey string)
|
||||
|
||||
notification, err := provider.VerifyNotification(c.Request.Context(), string(body), headers)
|
||||
if err != nil {
|
||||
log.Printf("[Payment Webhook] %s verify failed: %v", providerKey, err)
|
||||
slog.Error("[Payment Webhook] verify failed", "provider", providerKey, "error", err)
|
||||
c.String(http.StatusBadRequest, "verify failed")
|
||||
return
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (h *PaymentWebhookHandler) handleNotify(c *gin.Context, providerKey string)
|
||||
}
|
||||
|
||||
if err := h.paymentService.HandlePaymentNotification(c.Request.Context(), notification, providerKey); err != nil {
|
||||
log.Printf("[Payment Webhook] %s handle notification failed: %v", providerKey, err)
|
||||
slog.Error("[Payment Webhook] handle notification failed", "provider", providerKey, "error", err)
|
||||
c.String(http.StatusInternalServerError, "handle failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
|
||||
)
|
||||
|
||||
// Registry is a thread-safe registry mapping PaymentType to Provider.
|
||||
@@ -11,6 +12,10 @@ type Registry struct {
|
||||
providers map[PaymentType]Provider
|
||||
}
|
||||
|
||||
// ErrProviderNotFound is returned when a requested payment provider is not registered.
|
||||
var ErrProviderNotFound = infraerrors.NotFound("PROVIDER_NOT_FOUND", "payment provider not registered")
|
||||
|
||||
|
||||
// NewRegistry creates a new empty provider registry.
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
@@ -34,7 +39,7 @@ func (r *Registry) GetProvider(t PaymentType) (Provider, error) {
|
||||
defer r.mu.RUnlock()
|
||||
p, ok := r.providers[t]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no payment provider registered for type: %s", t)
|
||||
return nil, ErrProviderNotFound
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
@@ -51,7 +56,7 @@ func (r *Registry) GetProviderByKey(key string) (Provider, error) {
|
||||
}
|
||||
seen[k] = true
|
||||
}
|
||||
return nil, fmt.Errorf("no payment provider registered with key: %s", key)
|
||||
return nil, ErrProviderNotFound
|
||||
}
|
||||
|
||||
// GetProviderKey returns the provider key for the given payment type,
|
||||
@@ -81,7 +86,7 @@ func (r *Registry) SupportedTypes() []PaymentType {
|
||||
func (r *Registry) Clear() {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
for k := range r.providers {
|
||||
delete(r.providers, k)
|
||||
}
|
||||
r.providers = make(map[PaymentType]Provider)
|
||||
}
|
||||
|
||||
// Clear removes all registered providers.
|
||||
|
||||
@@ -37,38 +37,38 @@ const (
|
||||
// PaymentConfig holds the payment system configuration.
|
||||
type PaymentConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
MinAmount float64 `json:"minAmount"`
|
||||
MaxAmount float64 `json:"maxAmount"`
|
||||
DailyLimit float64 `json:"dailyLimit"`
|
||||
OrderTimeoutMin int `json:"orderTimeoutMinutes"`
|
||||
MaxPendingOrders int `json:"maxPendingOrders"`
|
||||
EnabledTypes []string `json:"enabledTypes"`
|
||||
BalanceDisabled bool `json:"balanceDisabled"`
|
||||
LoadBalanceStrategy string `json:"loadBalanceStrategy"`
|
||||
ProductNamePrefix string `json:"productNamePrefix"`
|
||||
ProductNameSuffix string `json:"productNameSuffix"`
|
||||
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"`
|
||||
LoadBalanceStrategy string `json:"load_balance_strategy"`
|
||||
ProductNamePrefix string `json:"product_name_prefix"`
|
||||
ProductNameSuffix string `json:"product_name_suffix"`
|
||||
}
|
||||
|
||||
// UpdatePaymentConfigRequest contains fields to update payment configuration.
|
||||
type UpdatePaymentConfigRequest struct {
|
||||
Enabled *bool `json:"enabled"`
|
||||
MinAmount *float64 `json:"minAmount"`
|
||||
MaxAmount *float64 `json:"maxAmount"`
|
||||
DailyLimit *float64 `json:"dailyLimit"`
|
||||
OrderTimeoutMin *int `json:"orderTimeoutMinutes"`
|
||||
MaxPendingOrders *int `json:"maxPendingOrders"`
|
||||
EnabledTypes []string `json:"enabledTypes"`
|
||||
BalanceDisabled *bool `json:"balanceDisabled"`
|
||||
LoadBalanceStrategy *string `json:"loadBalanceStrategy"`
|
||||
ProductNamePrefix *string `json:"productNamePrefix"`
|
||||
ProductNameSuffix *string `json:"productNameSuffix"`
|
||||
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"`
|
||||
LoadBalanceStrategy *string `json:"load_balance_strategy"`
|
||||
ProductNamePrefix *string `json:"product_name_prefix"`
|
||||
ProductNameSuffix *string `json:"product_name_suffix"`
|
||||
}
|
||||
|
||||
// MethodLimits holds per-payment-type limits.
|
||||
type MethodLimits struct {
|
||||
PaymentType string `json:"paymentType"`
|
||||
FeeRate float64 `json:"feeRate"`
|
||||
DailyLimit float64 `json:"dailyLimit"`
|
||||
DailyLimit float64 `json:"daily_limit"`
|
||||
SingleMin float64 `json:"singleMin"`
|
||||
SingleMax float64 `json:"singleMax"`
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -62,10 +62,10 @@ func (s *PaymentOrderExpiryService) runOnce() {
|
||||
|
||||
expired, err := s.paymentSvc.ExpireTimedOutOrders(ctx)
|
||||
if err != nil {
|
||||
log.Printf("[PaymentOrderExpiry] failed to expire orders: %v", err)
|
||||
slog.Error("[PaymentOrderExpiry] failed to expire orders", "error", err)
|
||||
return
|
||||
}
|
||||
if expired > 0 {
|
||||
log.Printf("[PaymentOrderExpiry] expired %d timed-out orders", expired)
|
||||
slog.Info("[PaymentOrderExpiry] expired timed-out orders", "count", expired)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user