feat(payment): balance recharge multiplier and refund amount separation

- Add balance_recharge_multiplier system setting (e.g. 1.2 = charge 100 get 120)
- Separate order_amount (credited balance) from pay_amount (actual payment)
- Refund calculates gateway amount proportionally from pay_amount
- Frontend shows both amounts in order details, payment status, refund dialog
- Admin settings UI for configuring recharge multiplier
This commit is contained in:
erio
2026-04-15 00:14:57 +08:00
parent 7c671b5373
commit 60a4b9316b
24 changed files with 246 additions and 101 deletions

View File

@@ -126,26 +126,28 @@ func (h *PaymentHandler) GetCheckoutInfo(c *gin.Context) {
}
response.Success(c, checkoutInfoResponse{
Methods: limitsResp.Methods,
GlobalMin: limitsResp.GlobalMin,
GlobalMax: limitsResp.GlobalMax,
Plans: planList,
BalanceDisabled: cfg.BalanceDisabled,
HelpText: cfg.HelpText,
HelpImageURL: cfg.HelpImageURL,
StripePublishableKey: cfg.StripePublishableKey,
Methods: limitsResp.Methods,
GlobalMin: limitsResp.GlobalMin,
GlobalMax: limitsResp.GlobalMax,
Plans: planList,
BalanceDisabled: cfg.BalanceDisabled,
BalanceRechargeMultiplier: cfg.BalanceRechargeMultiplier,
HelpText: cfg.HelpText,
HelpImageURL: cfg.HelpImageURL,
StripePublishableKey: cfg.StripePublishableKey,
})
}
type checkoutInfoResponse struct {
Methods map[string]service.MethodLimits `json:"methods"`
GlobalMin float64 `json:"global_min"`
GlobalMax float64 `json:"global_max"`
Plans []checkoutPlan `json:"plans"`
BalanceDisabled bool `json:"balance_disabled"`
HelpText string `json:"help_text"`
HelpImageURL string `json:"help_image_url"`
StripePublishableKey string `json:"stripe_publishable_key"`
Methods map[string]service.MethodLimits `json:"methods"`
GlobalMin float64 `json:"global_min"`
GlobalMax float64 `json:"global_max"`
Plans []checkoutPlan `json:"plans"`
BalanceDisabled bool `json:"balance_disabled"`
BalanceRechargeMultiplier float64 `json:"balance_recharge_multiplier"`
HelpText string `json:"help_text"`
HelpImageURL string `json:"help_image_url"`
StripePublishableKey string `json:"stripe_publishable_key"`
}
type checkoutPlan struct {
@@ -381,6 +383,7 @@ type PublicOrderResult struct {
Amount float64 `json:"amount"`
PayAmount float64 `json:"pay_amount"`
PaymentType string `json:"payment_type"`
OrderType string `json:"order_type"`
Status string `json:"status"`
}
@@ -404,6 +407,7 @@ func (h *PaymentHandler) VerifyOrderPublic(c *gin.Context) {
Amount: order.Amount,
PayAmount: order.PayAmount,
PaymentType: order.PaymentType,
OrderType: order.OrderType,
Status: order.Status,
})
}