allow_user_refund:
- Add allow_user_refund field to PaymentProviderInstance ent schema
- Migration 103: ALTER TABLE payment_provider_instances ADD COLUMN
- Cascade logic: disabling refund_enabled auto-disables allow_user_refund
- User refund validation: check provider instance allows user refund
- Admin refund validation: check provider instance allows admin refund
- Subscription refund: deduct days on refund, rollback on failure
- New endpoint: GET /payment/orders/refund-eligible-providers
- Frontend: ToggleSwitch in ProviderCard/Dialog, cascade in SettingsView
Wildcard matching:
- Change findPricingForModel from "longest prefix wins" to "config order
priority (first match wins)", aligning with channel service behavior
- Skip websearch provider when ProxyID is set but proxy not found (prevent
silent direct connection bypass)
- Fix sortByStableRandomWeight: pair factors with items so sort.Slice swap
keeps weights aligned
- Allow empty platform in account_stats_pricing_rules (wildcard matching),
only force anthropic default for main model_pricing
- Add channel_account_stats_pricing_intervals table and repo layer support
for interval-based pricing in account stats rules
- calculateTokenStatsCost now uses interval pricing when available
- Replace smtp.SendMail/tls.Dial with net.Dialer timeout (10s dial, 20s IO)
to prevent goroutine leak on SMTP hang
- Fix gofmt formatting issues
- Web Search label: black text with red warning hint
Priority was wrong:
- Before: custom rules → LiteLLM (when ApplyPricingToAccountStats) → nil
- After: custom rules → totalCost (when ApplyPricingToAccountStats) → LiteLLM → nil
When ApplyPricingToAccountStats is enabled, use the request's actual
client billing cost (before multiplier) as account_stats_cost, instead
of recalculating from LiteLLM per-token prices which produced incorrect
values for per-request billing mode.
LiteLLM model pricing is now the final fallback (priority 3), used only
when neither custom rules nor ApplyPricingToAccountStats apply.
- resolveAccountStatsCost now uses the final upstream model (after
account-level mapping) to match custom pricing rules, fixing the
issue where requested model (e.g. claude-sonnet-4-5) didn't match
rules configured for upstream model (e.g. claude-opus-4-6)
- Remove tryChannelPricing fallback — only custom rules are applied,
unmatched requests use default formula (total_cost × rate)
- Remove unused billingService and serviceTier parameters
- Update description: "启用后将支持自定义账号统计的模型价格"
Allow channels to configure independent model pricing for account
statistics cost calculation, decoupled from user billing.
Backend:
- Migration 101: channels.apply_pricing_to_account_stats toggle,
channel_account_stats_pricing_rules/model_pricing tables,
usage_logs.account_stats_cost column
- resolveAccountStatsCost: match rules by group/account, then channel
pricing, fallback to original formula when unconfigured
- Integrate into both GatewayService.recordUsageCore and
OpenAIGatewayService.RecordUsage
- Update 8 account stats SQL queries to use
COALESCE(account_stats_cost, total_cost) * account_rate_multiplier
- 23 unit tests for matching, pricing lookup, and cost calculation
Frontend:
- Channel edit dialog: toggle + custom rules UI with group/account
multi-select and pricing entry cards
- API types and i18n (zh/en)