expiry

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package expiry expires credit that has gone unused, after warning its owner.

An account's credit expires after Policy.InactiveMonths without a purchase, charge or adjustment. A warning goes out NoticeDays before that, and counts only once the email provider reports it delivered: credit expires once a full notice period from delivery has passed, and never before the date the warning announced. Each warning's deadline is stored, so a later policy change never shortens it. New activity starts over, and a hold the app reports defers expiry.

Warnings go only to an account's primary address while one of its identities verifies it. Credit never expires while no identity does, and a new primary address needs a new warning.

Expiry runs hourly while the app serves, and on demand with the credits expire command. A PostgreSQL lock keeps replicas from running it twice at once.

Index

Constants

This section is empty.

Variables

View Source
var Migrations = app.Migrations{Namespace: "credits_expiry", Files: sub(migrationFiles, "migrations")}

Migrations create the warnings table. List them after credits.Migrations.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Sender sends the warnings and reports their delivery. It is required: credit
	// never expires without a delivered warning.
	Sender email.TrackingSender
	Policy Policy
	// AppName names the app in the default warning, such as "Acme".
	AppName string
	// CreditsURL is the absolute URL of the credits page, linked from the warning.
	CreditsURL string
	// Message writes the warning. Zero uses a plain default that names AppName. The
	// warning always goes to Notice.To, whatever the message's To says.
	Message func(Notice) email.Message
	// OnRun, if set, receives the outcome of every run: the hourly ones, the credits
	// expire command's and the app's own calls to Run. Use it to record metrics. It
	// runs after the run released its lock, and must return quickly.
	OnRun func(ctx context.Context, r Result, err error)
}

Config configures expiry.

type Expiry

type Expiry struct {
	// contains filtered or unexported fields
}

Expiry is the credit expiry module.

func New

func New(a *app.App, cr *credits.Credits, cfg Config) (*Expiry, error)

New returns the expiry module. It runs expiry hourly while the app serves, and registers the credits expire command.

func (*Expiry) Run

func (x *Expiry) Run(ctx context.Context) (Result, error)

Run checks the delivery of sent warnings, sends due warnings and expires due credit, once, and reports the outcome to Config.OnRun.

type Notice

type Notice struct {
	To           string
	AppName      string
	BalanceCents int64
	LastActivity time.Time
	ExpiresAt    time.Time
	CreditsURL   string
}

Notice is what a warning tells its recipient.

type Policy

type Policy struct {
	// InactiveMonths without activity make credit expire, in calendar months.
	InactiveMonths int
	// NoticeDays is the warning before expiry. It runs from the warning's delivery.
	NoticeDays int
}

Policy sets when credit expires. Both values are required.

type Result

type Result struct {
	// Skipped means another run held the lock, so this one did nothing.
	Skipped bool
	// Warned counts warnings the email provider accepted, and WarningsFailed those it
	// did not.
	Warned         int
	WarningsFailed int
	// Expired counts accounts whose credit expired, and ExpiredCents that credit.
	Expired      int
	ExpiredCents int64
	// Failed counts everything else that failed: checking a warning's delivery,
	// recording a warning that was sent, and expiring an account's credit.
	Failed int
}

Result counts what one run did. The next run tries again whatever failed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL