fix(billing): harden tiered retry group-switch billing (#6570)

Follow-up to #6518 (issue #6480) addressing three review findings:

- Document and lock in arrears semantics for the wallet Reserve top-up:
  when an auto-group retry lands on a more expensive group, the full
  reservation delta is deducted unconditionally (balance may go
  negative), mirroring settlement, so the logged pre-consumed quota
  always reconciles with the actual balance movement. Genuine DB
  errors still fail the attempt with update_data_error. Subscription
  funding keeps its insufficient-quota behavior: subscriptions enforce
  a hard used<=total cap and do not support arrears.
- PriceData.FreeModel is cleared when a retry switches from a free
  group to a paid one, keeping it consistent with the billing session
  created at that point.
- getChannel refreshes GroupRatioInfo only after channel selection
  succeeds, and the retry loop records the channel in use_channel
  before PrepareTieredBillingForSelectedGroup can fail.
This commit is contained in:
Calcium-Ion
2026-08-01 09:35:51 +08:00
committed by GitHub
parent df43f80153
commit cfaba1dd67
4 changed files with 127 additions and 5 deletions
+11 -1
View File
@@ -132,9 +132,19 @@ func PrepareTieredBillingForSelectedGroup(c *gin.Context, relayInfo *relaycommon
types.ErrOptionWithSkipRetry(),
)
}
if snap == nil || snap.GroupRatio == 0 {
if snap == nil {
return nil
}
if snap.GroupRatio == 0 {
// Paid-to-free keeps FreeModel as-is: FreeModel means "pre-consume was
// skipped", which is not true once a session exists, and settlement
// already yields 0 for a zero group ratio.
return nil
}
// The selected group is paid; clear a FreeModel flag frozen when the
// initial group was free so downstream state stays consistent.
relayInfo.PriceData.FreeModel = false
if relayInfo.Billing == nil {
return PreConsumeBilling(c, snap.EstimatedQuotaAfterGroup, relayInfo)