Merge branch 'main' into rebuild/auth-identity-foundation

This commit is contained in:
IanShaw027
2026-04-22 00:35:34 +08:00
67 changed files with 1550 additions and 613 deletions

View File

@@ -2,6 +2,7 @@ package service
import (
"context"
"errors"
"fmt"
"log/slog"
"math"
@@ -201,7 +202,7 @@ func (s *PaymentService) checkPendingLimit(ctx context.Context, tx *dbent.Tx, us
return fmt.Errorf("count pending orders: %w", err)
}
if c >= max {
return infraerrors.TooManyRequests("TOO_MANY_PENDING", fmt.Sprintf("too many pending orders (max %d)", max)).
return infraerrors.TooManyRequests("TOO_MANY_PENDING", "too_many_pending").
WithMetadata(map[string]string{"max": strconv.Itoa(max)})
}
return nil
@@ -284,7 +285,8 @@ func (s *PaymentService) checkDailyLimit(ctx context.Context, tx *dbent.Tx, user
used += o.Amount
}
if used+amount > limit {
return infraerrors.TooManyRequests("DAILY_LIMIT_EXCEEDED", fmt.Sprintf("daily recharge limit reached, remaining: %.2f", math.Max(0, limit-used)))
return infraerrors.TooManyRequests("DAILY_LIMIT_EXCEEDED", "daily_limit_exceeded").
WithMetadata(map[string]string{"remaining": fmt.Sprintf("%.2f", math.Max(0, limit-used))})
}
return nil
}
@@ -296,10 +298,11 @@ func (s *PaymentService) selectCreateOrderInstance(ctx context.Context, req Crea
}
sel, err := s.loadBalancer.SelectInstance(selectCtx, "", req.PaymentType, payment.Strategy(cfg.LoadBalanceStrategy), payAmount)
if err != nil {
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", fmt.Sprintf("payment method (%s) is not configured", req.PaymentType))
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", "method_not_configured").
WithMetadata(map[string]string{"payment_type": req.PaymentType})
}
if sel == nil {
return nil, infraerrors.TooManyRequests("NO_AVAILABLE_INSTANCE", "no available payment instance")
return nil, infraerrors.TooManyRequests("NO_AVAILABLE_INSTANCE", "no_available_instance")
}
return sel, nil
}
@@ -342,7 +345,18 @@ func (s *PaymentService) usesOfficialWxpayVisibleMethod(ctx context.Context) boo
func (s *PaymentService) invokeProvider(ctx context.Context, order *dbent.PaymentOrder, req CreateOrderRequest, cfg *PaymentConfig, limitAmount float64, payAmountStr string, payAmount float64, plan *dbent.SubscriptionPlan, sel *payment.InstanceSelection) (*CreateOrderResponse, error) {
prov, err := provider.CreateProvider(sel.ProviderKey, sel.InstanceID, sel.Config)
if err != nil {
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", "payment method is temporarily unavailable")
slog.Error("[PaymentService] CreateProvider failed", "provider", sel.ProviderKey, "instance", sel.InstanceID, "error", err)
// If the provider returned a structured ApplicationError (e.g. WXPAY_CONFIG_MISSING_KEY),
// pass it through with provider context added to metadata. Otherwise wrap as PAYMENT_PROVIDER_MISCONFIGURED.
if appErr := new(infraerrors.ApplicationError); errors.As(err, &appErr) {
md := map[string]string{"provider": sel.ProviderKey, "instance_id": sel.InstanceID}
for k, v := range appErr.Metadata {
md[k] = v
}
return nil, appErr.WithMetadata(md)
}
return nil, infraerrors.ServiceUnavailable("PAYMENT_PROVIDER_MISCONFIGURED", "provider_misconfigured").
WithMetadata(map[string]string{"provider": sel.ProviderKey, "instance_id": sel.InstanceID})
}
subject := s.buildPaymentSubject(plan, limitAmount, cfg)
outTradeNo := order.OutTradeNo
@@ -380,6 +394,9 @@ func (s *PaymentService) invokeProvider(ctx context.Context, order *dbent.Paymen
pr, err := prov.CreatePayment(ctx, providerReq)
if err != nil {
slog.Error("[PaymentService] CreatePayment failed", "provider", sel.ProviderKey, "instance", sel.InstanceID, "error", err)
if appErr := new(infraerrors.ApplicationError); errors.As(err, &appErr) {
return nil, appErr
}
return nil, classifyCreatePaymentError(req, sel.ProviderKey, err)
}
_, err = s.entClient.PaymentOrder.UpdateOneID(order.ID).