mirror of
https://github.com/hanzoai/commerce.git
synced 2026-10-30 15:08:42 +00:00
Auto-reload charges the org's card with nobody present, so the rule is its admins' to set — and a SuperAdmin's or a service's. The amount is a top-up's own range ($5 to $5,000) and the balance it reloads below is $0 to $5,000, refused with the range in the sentence. The sweep asks the processor to charge one org at most three times a UTC day, a decline included, and names the limit in the row it skips. The day's reload is reserved under compare-and-swap before the card is charged, against the rule as it stands, so a rule switched off mid-sweep is not charged or re-armed, and overlapping sweeps share the limit. A charge refused before the processor was asked, or a replay of one already counted, gives its reload back. An admin's save swaps the rule's own fields and keeps the day's count. Saving, changing, choosing the default of and removing the org's cards, and charging its card on file, are its admins' too. A member whose wallet is their own person account still manages that wallet's cards. A loaded rule is bound to its key, so an edit is the rule on file. Before, an update wrote a second, parentless row the next read never found. The first read now removes such a row, and honours it when it switched the rule off; it never arms a rule or raises what it charges. Co-authored-by: Hanzo Dev <[email protected]>
54 lines
1.9 KiB
Go
54 lines
1.9 KiB
Go
package autorecharge
|
|
|
|
import (
|
|
"github.com/hanzoai/commerce/datastore"
|
|
"github.com/hanzoai/commerce/models/mixin"
|
|
"github.com/hanzoai/commerce/util/val"
|
|
"github.com/hanzoai/orm"
|
|
)
|
|
|
|
func init() { orm.Register[AutoRecharge]("auto-recharge") }
|
|
|
|
// AutoRecharge is a per-org automatic top-up rule. When the org's available
|
|
// balance (balance minus holds) drops below ThresholdCents, the recharge cron
|
|
// charges the org's default payment method AmountCents and credits the balance.
|
|
// Mirrors the Claude/OpenAI "auto-reload" setting. One record per org (keyed by
|
|
// UserId = the org slug, which is the billing key used by topup/balance).
|
|
type AutoRecharge struct {
|
|
mixin.Model[AutoRecharge]
|
|
|
|
UserId string `json:"userId"` // org slug (billing key)
|
|
Enabled bool `json:"enabled"`
|
|
ThresholdCents int64 `json:"thresholdCents"`
|
|
AmountCents int64 `json:"amountCents"`
|
|
Currency string `json:"currency"`
|
|
|
|
// LastRechargedAt stores the ISO timestamp of the last successful auto
|
|
// top-up — used for observability and as a light guard against a runaway
|
|
// recharge loop within a single cron window.
|
|
LastRechargedAt string `json:"lastRechargedAt,omitempty"`
|
|
|
|
// Day is the UTC date ("2006-01-02") Reloads counts, and Reloads how many
|
|
// reloads charged the card on that day. The sweep stops at its daily limit.
|
|
Day string `json:"day,omitempty"`
|
|
Reloads int `json:"reloads,omitempty"`
|
|
// Txn is the transaction of the last reload counted, so a replayed charge
|
|
// (the same reload answered twice) is counted once.
|
|
Txn string `json:"txn,omitempty"`
|
|
}
|
|
|
|
func (a *AutoRecharge) Validator() *val.Validator {
|
|
return val.New()
|
|
}
|
|
|
|
func New(db *datastore.Datastore) *AutoRecharge {
|
|
a := new(AutoRecharge)
|
|
a.Init(db)
|
|
a.Parent = db.NewKey("synckey", "", 1, nil)
|
|
return a
|
|
}
|
|
|
|
func Query(db *datastore.Datastore) datastore.Query {
|
|
return db.Query("auto-recharge")
|
|
}
|