fix(clients): snap EOM 23:59:59 expiry to billing midnight without renew (#6457)

* fix(clients): snap EOM 23:59:59 expiry to billing midnight without renew (#6300)

Inclusive end-of-month expiries share the next calendar billing boundary.
Normalize onto that midnight before the catch-up loop so the first charged
step is a full month and resetMax=1 is not spent on a one-second alignment.

* fix(clients): snap only the EOM 23:59:59 instant, not the whole prior day

The calendar renew guard was matching [boundary-1d, boundary), so a midday
expiry on the day before billing could be snapped past now with renewals=0
and left disabled until midnight. Narrow to [boundary-1s, boundary).

Also clear golangci (gofumpt/QF1001), trim comments to 2 lines, and pin that
midday expiry still charges for alignment.

---------

Co-authored-by: mrchatam <mrchatam@users.noreply.github.com>
Co-authored-by: mrchatam <287639636+mrchatam@users.noreply.github.com>
This commit is contained in:
mrchatam
2026-09-12 12:36:47 +03:30
committed by GitHub
parent f51b0040cf
commit 5cce2464f1
2 changed files with 151 additions and 0 deletions
+9
View File
@@ -448,6 +448,15 @@ func (s *InboundService) autoRenewClients(tx *gorm.DB, mutationBatch *trafficMut
continue
}
at := time.UnixMilli(newExpiryTime)
// Inclusive end-of-day expiries share the next billing midnight; snap without
// spending an allowance so the first charged step is a full month (#6300).
if traffic.ResetDay > 0 {
boundary := nextCalendarRenewal(at, traffic.ResetDay, renewLocation)
if !at.Before(boundary.Add(-time.Second)) && at.Before(boundary) {
at = boundary
newExpiryTime = at.UnixMilli()
}
}
renewals := 0
for newExpiryTime < now {
if traffic.ResetMax > 0 && traffic.ResetCount+renewals >= traffic.ResetMax {