Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func StretchColumns ¶
StretchColumns splits a stretch into the four nullable columns the stores keep it in, in UTC. A nil stretch is four nils.
Types ¶
type DueField ¶
type DueField string
DueField names the date a lifecycle step reads from a subscription.
type DueOpts ¶
type DueOpts struct {
Field DueField
Before time.Time
Statuses []Status
AppID string
Limit int
Offset int
}
DueOpts selects the subscriptions whose Field is at or before Before and whose status is one of Statuses (any status when it is empty), earliest date first, ties broken by id. An empty AppID means every app: the lifecycle clock runs across apps. The database stores index each date behind the status, (status, cancel_at) and so on, so a caller that names its Statuses reads the index and not every row that ever carried the date: an immediate cancel stamps cancel_at too, so that date is set on every canceled row.
type Renewal ¶
type Renewal struct {
Subscription *Subscription `json:"subscription"`
Ended []Period `json:"ended"`
}
Renewal is what OnSubscriptionRenewed receives when the lifecycle clock moves a subscription on: the subscription in its new period, and every period that ended in the move, oldest first, catch-up periods included.
type Resume ¶
type Resume struct {
// PausedAt is the paused_at the engine read, nil when there was none.
PausedAt *time.Time
// Status is what the subscription resumes to: trialing when its trial
// was still running when it was paused, active otherwise.
Status Status
// PeriodEnd is the current period's end moved on by the length of the
// pause, or nil to leave the period alone. A cancel scheduled for the
// old period end (cancel_at equal to current_period_end) moves with it.
PeriodEnd *time.Time
// TrialEnd is the trial end moved on by the length of the pause, or nil
// to leave trial_end as it is.
TrialEnd *time.Time
// Stretch is the stretch record to store, or nil to leave it as it is.
Stretch *Stretch
}
Resume is what a resume writes, in one conditional store write that lands only while the subscription is still paused and still holds the PausedAt the engine read (nil: none). Everything here is worked out by the engine from that read, so a second pause and resume in between must not let it land. The period start never changes.
type Store ¶
type Store interface {
Create(ctx context.Context, s *Subscription) error
Get(ctx context.Context, subID id.SubscriptionID) (*Subscription, error)
GetActive(ctx context.Context, tenantID string, appID string) (*Subscription, error)
List(ctx context.Context, tenantID string, appID string, opts ListOpts) ([]*Subscription, error)
Update(ctx context.Context, s *Subscription) error
Cancel(ctx context.Context, subID id.SubscriptionID, cancelAt time.Time) error
}
type Stretch ¶
Stretch is the most recent billing period a resume stretched: it began at Start, was due to end at OriginalEnd, and ends at End once every pause in it is added on. The periods after it follow the clock's anchor from End (whatever nextPeriod gives); the ones before it ran on the cadence that led to OriginalEnd. Floor is where that earlier cadence began when an older stretch came before it (the older stretch's End): periods before Floor cannot be proved, since only one stretch is remembered. A nil Floor means the earlier cadence runs back to the subscription's creation.
func StretchFromColumns ¶
StretchFromColumns is StretchColumns the other way: nil unless start, end and originalEnd are all set.
type Subscription ¶
type Subscription struct {
types.Entity
ID id.SubscriptionID `json:"id"`
TenantID string `json:"tenant_id"`
PlanID id.PlanID `json:"plan_id"`
Status Status `json:"status"`
CurrentPeriodStart time.Time `json:"current_period_start"`
CurrentPeriodEnd time.Time `json:"current_period_end"`
TrialStart *time.Time `json:"trial_start,omitempty"`
TrialEnd *time.Time `json:"trial_end,omitempty"`
CanceledAt *time.Time `json:"canceled_at,omitempty"`
CancelAt *time.Time `json:"cancel_at,omitempty"`
EndedAt *time.Time `json:"ended_at,omitempty"`
// PausedAt is when the subscription was paused. It is set while the
// status is paused and cleared by a resume, which moves the period end
// and a running trial's end on by the length of the pause. A
// subscription paused before this field existed has none.
PausedAt *time.Time `json:"paused_at,omitempty"`
// Stretch remembers the most recent period a resume stretched, so
// ledger.ForPeriod can still prove the periods around it. Ledger keeps
// it for itself; it is not part of the JSON.
Stretch *Stretch `json:"-"`
AppID string `json:"app_id"`
ProviderID string `json:"provider_id,omitempty"`
ProviderName string `json:"provider_name,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
// Quantity holds the current count for each quantity-priced plan
// feature, keyed by feature key. Seats are the usual case.
//
// A seat count is a level and not a flow, so it is stored here and set
// when the subscription is created or changed, rather than derived
// from usage events. Aggregating a stream would answer "how many seats
// were added this month", which is a different question.
Quantity map[string]int64 `json:"quantity,omitempty"`
}