Documentation
¶
Overview ¶
Package subscription manages tenant plan subscriptions, renewals, expiration, grace periods, and billing hooks.
包 subscription 管理租户套餐订阅、续订、到期、宽限期和计费钩子。
Index ¶
- Constants
- Variables
- type BillingEvent
- type BillingHook
- type LifecycleService
- type ListFilter
- type MemoryService
- func (service *MemoryService) Create(ctx context.Context, subscription Subscription) error
- func (service *MemoryService) Delete(ctx context.Context, tenantID types.TenantID) error
- func (service *MemoryService) Downgrade(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
- func (service *MemoryService) Expire(ctx context.Context, tenantID types.TenantID) (Subscription, error)
- func (service *MemoryService) ExpireDue(ctx context.Context) ([]Subscription, error)
- func (service *MemoryService) Get(ctx context.Context, tenantID types.TenantID) (Subscription, error)
- func (service *MemoryService) List(ctx context.Context, filter ListFilter) ([]Subscription, error)
- func (service *MemoryService) ListPage(ctx context.Context, filter PageFilter) ([]Subscription, error)
- func (service *MemoryService) Renew(ctx context.Context, tenantID types.TenantID, currentPeriodEnd time.Time) (Subscription, error)
- func (service *MemoryService) Subscribe(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
- func (service *MemoryService) SubscribeWithPeriod(ctx context.Context, tenantID types.TenantID, planID string, ...) (Subscription, error)
- func (service *MemoryService) Unsubscribe(ctx context.Context, tenantID types.TenantID) (Subscription, error)
- func (service *MemoryService) Update(ctx context.Context, subscription Subscription) error
- func (service *MemoryService) Upgrade(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
- type MemoryStore
- type Option
- type PageFilter
- type PagedStore
- type SQLDialect
- type SQLStore
- func (store *SQLStore) Create(ctx context.Context, subscription Subscription) error
- func (store *SQLStore) Delete(ctx context.Context, tenantID types.TenantID) error
- func (store *SQLStore) Get(ctx context.Context, tenantID types.TenantID) (Subscription, error)
- func (store *SQLStore) List(ctx context.Context, filter ListFilter) (subscriptions []Subscription, err error)
- func (store *SQLStore) ListPage(ctx context.Context, filter PageFilter) (subscriptions []Subscription, err error)
- func (store *SQLStore) Update(ctx context.Context, subscription Subscription) error
- type SQLStoreOption
- type Service
- type Status
- type Store
- type Subscription
Constants ¶
const ( // SQLDialectMySQL uses question-mark placeholders and is the default. SQLDialectMySQL = sqlutil.DialectMySQL // SQLDialectSQLite uses question-mark placeholders. SQLDialectSQLite = sqlutil.DialectSQLite // SQLDialectPostgres uses numbered placeholders. SQLDialectPostgres = sqlutil.DialectPostgres )
const (
// DefaultSQLTableName is the default subscription table name.
DefaultSQLTableName = "saas_subscriptions"
)
Variables ¶
var ( // ErrSubscriptionNotFound reports that a subscription does not exist. ErrSubscriptionNotFound = errors.New("saas/subscription: subscription not found") // ErrSubscriptionAlreadyExists reports that a tenant already has a subscription. ErrSubscriptionAlreadyExists = errors.New("saas/subscription: subscription already exists") // ErrSubscriptionConflict reports a concurrent replacement during update. ErrSubscriptionConflict = errors.New("saas/subscription: subscription update conflict") // ErrInvalidSubscription reports invalid subscription metadata. ErrInvalidSubscription = errors.New("saas/subscription: invalid subscription") // ErrInvalidTransition reports an invalid subscription lifecycle transition. ErrInvalidTransition = errors.New("saas/subscription: invalid transition") // ErrBillingHookReentrantMutation reports a billing hook attempting to // mutate a subscription that already has an in-flight billing event. ErrBillingHookReentrantMutation = errors.New("saas/subscription: billing hook cannot mutate pending subscription") // ErrInvalidListFilter reports an invalid subscription list filter. ErrInvalidListFilter = errors.New("saas/subscription: invalid list filter") // ErrNilDB reports that a SQL store was created with a nil database handle. ErrNilDB = errors.New("saas/subscription: nil db") // ErrInvalidTableName reports an unsafe SQL table name. ErrInvalidTableName = errors.New("saas/subscription: invalid table name") // ErrUnsupportedSQLDialect reports an unsupported SQLStore dialect. ErrUnsupportedSQLDialect = errors.New("saas/subscription: unsupported sql dialect") )
Functions ¶
This section is empty.
Types ¶
type BillingEvent ¶
type BillingEvent struct {
TenantID types.TenantID
Action string
FromPlan string
ToPlan string
Status Status
CurrentPeriodEnd *time.Time
}
BillingEvent describes a subscription change for external billing systems.
type BillingHook ¶
type BillingHook func(context.Context, BillingEvent) error
BillingHook receives subscription lifecycle changes. Hooks that call back into MemoryService must propagate the received context: it carries the staged-read and re-entrant-mutation guard for the in-flight event. Replacing it with context.Background can make a same-tenant mutation wait on itself.
type LifecycleService ¶
type LifecycleService interface {
Service
SubscribeWithPeriod(ctx context.Context, tenantID types.TenantID, planID string, currentPeriodEnd time.Time) (Subscription, error)
Renew(ctx context.Context, tenantID types.TenantID, currentPeriodEnd time.Time) (Subscription, error)
Expire(ctx context.Context, tenantID types.TenantID) (Subscription, error)
ExpireDue(ctx context.Context) ([]Subscription, error)
}
LifecycleService extends Service with billing-period renewal and expiration operations.
type ListFilter ¶
type ListFilter struct {
TenantIDs []types.TenantID
PlanIDs []string
Statuses []Status
Limit int
Offset int
}
ListFilter restricts subscription list queries.
type MemoryService ¶
type MemoryService struct {
// contains filtered or unexported fields
}
MemoryService is a thread-safe in-memory subscription service. Billing hooks receive a context that can read the staged state, while other readers keep seeing the last committed state. Same-tenant writers wait for the hook to finish; a hook attempting that write itself receives ErrBillingHookReentrantMutation instead of deadlocking. Get observes the hook overlay; List and ListPage intentionally return committed state only.
func NewMemoryService ¶
func NewMemoryService(opts ...Option) *MemoryService
NewMemoryService creates an empty subscription service.
func (*MemoryService) Create ¶
func (service *MemoryService) Create(ctx context.Context, subscription Subscription) error
Create inserts a subscription.
func (*MemoryService) Downgrade ¶
func (service *MemoryService) Downgrade(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
Downgrade changes an active subscription to a lower plan.
func (*MemoryService) Expire ¶
func (service *MemoryService) Expire(ctx context.Context, tenantID types.TenantID) (Subscription, error)
Expire marks an active subscription expired immediately.
func (*MemoryService) ExpireDue ¶
func (service *MemoryService) ExpireDue(ctx context.Context) ([]Subscription, error)
ExpireDue marks active subscriptions expired after their current period and grace window end.
func (*MemoryService) Get ¶
func (service *MemoryService) Get(ctx context.Context, tenantID types.TenantID) (Subscription, error)
Get returns a subscription by tenant ID.
func (*MemoryService) List ¶
func (service *MemoryService) List(ctx context.Context, filter ListFilter) ([]Subscription, error)
List returns subscriptions matching filter.
func (*MemoryService) ListPage ¶
func (service *MemoryService) ListPage(ctx context.Context, filter PageFilter) ([]Subscription, error)
ListPage returns subscriptions after the cursor while preserving List filtering semantics.
func (*MemoryService) Renew ¶
func (service *MemoryService) Renew(ctx context.Context, tenantID types.TenantID, currentPeriodEnd time.Time) (Subscription, error)
Renew reactivates an active or expired subscription with a new current period end.
func (*MemoryService) Subscribe ¶
func (service *MemoryService) Subscribe(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
Subscribe creates an active subscription for a tenant.
func (*MemoryService) SubscribeWithPeriod ¶
func (service *MemoryService) SubscribeWithPeriod(ctx context.Context, tenantID types.TenantID, planID string, currentPeriodEnd time.Time) (Subscription, error)
SubscribeWithPeriod creates an active subscription with a current billing period end.
func (*MemoryService) Unsubscribe ¶
func (service *MemoryService) Unsubscribe(ctx context.Context, tenantID types.TenantID) (Subscription, error)
Unsubscribe cancels an active subscription.
func (*MemoryService) Update ¶
func (service *MemoryService) Update(ctx context.Context, subscription Subscription) error
Update replaces a subscription.
func (*MemoryService) Upgrade ¶
func (service *MemoryService) Upgrade(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
Upgrade changes an active subscription to a higher plan.
type MemoryStore ¶
type MemoryStore = MemoryService
MemoryStore is kept as a Store-oriented name for MemoryService.
func NewMemoryStore ¶
func NewMemoryStore(opts ...Option) *MemoryStore
NewMemoryStore creates an empty subscription store.
type Option ¶
type Option func(*MemoryService)
Option configures MemoryService.
func WithBillingHook ¶
func WithBillingHook(hook BillingHook) Option
WithBillingHook sets the billing hook.
func WithGracePeriod ¶
WithGracePeriod sets the post-period grace window before ExpireDue marks a subscription expired.
type PageFilter ¶
type PageFilter struct {
TenantIDs []types.TenantID
PlanIDs []string
Statuses []Status
Limit int
Offset int
// Cursor returns rows ordered after the tenant ID cursor.
Cursor types.TenantID
}
PageFilter restricts cursor-based subscription list queries.
type PagedStore ¶
type PagedStore interface {
Store
ListPage(ctx context.Context, filter PageFilter) ([]Subscription, error)
}
PagedStore extends Store with cursor-based subscription listing.
type SQLDialect ¶
SQLDialect controls SQL placeholder rendering for SQLStore.
type SQLStore ¶
type SQLStore struct {
// contains filtered or unexported fields
}
SQLStore persists tenant subscriptions through database/sql.
The table is expected to contain these columns: tenant_id, plan_id, status, start_date, end_date, current_period_end, grace_period_end.
func NewSQLStore ¶
func NewSQLStore(db *sql.DB, opts ...SQLStoreOption) (*SQLStore, error)
NewSQLStore creates a SQL-backed subscription store.
func (*SQLStore) Create ¶
func (store *SQLStore) Create(ctx context.Context, subscription Subscription) error
Create inserts a subscription.
func (*SQLStore) List ¶
func (store *SQLStore) List(ctx context.Context, filter ListFilter) (subscriptions []Subscription, err error)
List returns subscriptions matching filter.
func (*SQLStore) ListPage ¶
func (store *SQLStore) ListPage(ctx context.Context, filter PageFilter) (subscriptions []Subscription, err error)
ListPage returns subscriptions after the cursor while preserving List filtering semantics.
type SQLStoreOption ¶
SQLStoreOption configures SQLStore.
func WithSQLDialect ¶
func WithSQLDialect(dialect SQLDialect) SQLStoreOption
WithSQLDialect configures SQL placeholder rendering.
func WithTableName ¶
func WithTableName(table string) SQLStoreOption
WithTableName overrides the default subscription table name.
type Service ¶
type Service interface {
Subscribe(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
Unsubscribe(ctx context.Context, tenantID types.TenantID) (Subscription, error)
Upgrade(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
Downgrade(ctx context.Context, tenantID types.TenantID, planID string) (Subscription, error)
Get(ctx context.Context, tenantID types.TenantID) (Subscription, error)
}
Service manages tenant subscriptions.
type Store ¶
type Store interface {
Create(ctx context.Context, subscription Subscription) error
Get(ctx context.Context, tenantID types.TenantID) (Subscription, error)
List(ctx context.Context, filter ListFilter) ([]Subscription, error)
Update(ctx context.Context, subscription Subscription) error
Delete(ctx context.Context, tenantID types.TenantID) error
}
Store persists tenant subscriptions.