Documentation
¶
Overview ¶
Package pricingspec is a machine-readable description of a product's pricing and packaging: the tiers of its ladder, the plans in them with their prices, the features and limits each plan sets, its one-time products, an optional marketplace fee, and the grants each tier gives an account.
A Spec is the source of truth. A pricing page, a limit check, a payment provider's prices and an account's entitlements are computed from it. The normative description is SPEC.md at the root of the repository; the types in this package are its Go form, and the JSON Schema in the schema package is generated from them.
Nothing here depends on a payment provider or an application framework.
Index ¶
- Constants
- Variables
- type Application
- type Change
- type ChangeKind
- type Decision
- type EntitlementMap
- type Feature
- type FeatureCell
- type FeatureRow
- type FeatureValue
- type Grant
- type Interval
- type Issue
- type Limit
- type LimitCell
- type LimitRow
- type LimitValue
- type Marketplace
- type Period
- type Plan
- type PlanColumn
- type Price
- type PricingView
- type Product
- type Severity
- type Spec
- func (s *Spec) Allowance(planID, limit string) (max int, unlimited, fairUse bool)
- func (s *Spec) AtLeast(planID, feature, level string) bool
- func (s *Spec) Check(planID, limit string, after int) Decision
- func (s *Spec) EntitlementMap() EntitlementMap
- func (s *Spec) GrantsFor(planID string) []TierGrant
- func (s *Spec) Has(planID, feature string) bool
- func (s *Spec) Level(planID, feature string) string
- func (s *Spec) Lint() []Issue
- func (s *Spec) Plan(id string) (Plan, bool)
- func (s *Spec) PlanOrDefault(id string) Plan
- func (s *Spec) PricingView() PricingView
- func (s *Spec) TierRank(tier string) int
- func (s *Spec) Validate() []Issue
- type Tier
- type TierEntitlements
- type TierGrant
- type ValidationError
Constants ¶
const FormatVersion = "1"
FormatVersion is the document format version this package reads and writes.
Variables ¶
var ErrUnknownFormat = errors.New("pricingspec: unknown format version")
ErrUnknownFormat is returned for a document whose formatVersion this package does not read.
Functions ¶
This section is empty.
Types ¶
type Application ¶
type Application struct {
ID string `json:"id" jsonschema:"pattern=^[a-z][a-z0-9_]*$"`
Name string `json:"name"`
}
Application is an application an account can be granted access to.
type Change ¶
type Change struct {
Kind ChangeKind `json:"kind"`
// Subject is what changed, for example "plan pro" or "grant free/reports".
Subject string `json:"subject"`
Detail string `json:"detail"`
// AffectsExisting is true when the change worsens the offer for accounts
// already on it: a higher price, a lower allowance, a removed feature, a
// removed grant, fewer seats, a lowered level, or a removed plan.
AffectsExisting bool `json:"affectsExisting,omitempty"`
}
Change is one difference between two Specs.
type ChangeKind ¶
type ChangeKind string
ChangeKind says what happened to the thing that changed.
const ( Added ChangeKind = "added" Removed ChangeKind = "removed" Changed ChangeKind = "changed" )
Change kinds.
type Decision ¶
type Decision string
Decision is the answer to a limit check.
const ( // Allowed means the action is within the allowance. Allowed Decision = "allowed" // Warn means the allowance is exceeded but marked fair use: allow the // action and flag the account for review. Warn Decision = "warn" // Refuse means the allowance is exceeded. Refuse Decision = "refuse" )
Decisions.
type EntitlementMap ¶
type EntitlementMap struct {
Tiers []TierEntitlements `json:"tiers"`
}
EntitlementMap is what each tier grants, in tier rank order.
type Feature ¶
type Feature struct {
ID string `json:"id" jsonschema:"pattern=^[a-z][a-z0-9_]*$"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
// Levels, when present, make the feature a ladder: at least two distinct
// names, lowest first, each including the one below. Without levels the
// feature is a switch.
Levels []string `json:"levels,omitempty" jsonschema:"description=Ordered levels of a ladder feature; absent for a switch"`
}
Feature is something a plan switches on, or sets to a level.
type FeatureCell ¶
type FeatureCell struct {
Plan string `json:"plan"`
Included bool `json:"included"`
Level string `json:"level,omitempty"`
}
FeatureCell is a plan's setting of a feature. Included is true for an enabled switch or a ladder at any level; Level is the ladder's level.
type FeatureRow ¶
type FeatureRow struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Levels []string `json:"levels,omitempty"`
Cells []FeatureCell `json:"cells"`
}
FeatureRow is one feature across the plans, in the same order as the plans.
type FeatureValue ¶
type FeatureValue struct {
Feature string `json:"feature"`
// Enabled states a switch. It must be false for a ladder.
Enabled bool `json:"enabled,omitempty"`
// Level names one of a ladder's levels. It must be empty for a switch.
Level string `json:"level,omitempty"`
}
FeatureValue sets one feature for a plan.
type Grant ¶
type Grant struct {
Application string `json:"application"`
ApplicationName string `json:"applicationName"`
Seats int `json:"seats,omitempty"`
UnlimitedSeats bool `json:"unlimitedSeats,omitempty"`
}
Grant is access to one application.
type Issue ¶
type Issue struct {
Code string `json:"code"`
Severity Severity `json:"severity"`
// Path locates the finding, for example "plans[2].limits[0]".
Path string `json:"path"`
Message string `json:"message"`
}
Issue is one finding about a Spec. Its Code is stable (E001 to E013 for errors, W001 to W006 for warnings) and documented in SPEC.md.
type Limit ¶
type Limit struct {
ID string `json:"id" jsonschema:"pattern=^[a-z][a-z0-9_]*$"`
Name string `json:"name"`
Unit string `json:"unit,omitempty"`
Period Period `json:"period" jsonschema:"enum=none,enum=day,enum=month"`
Description string `json:"description,omitempty"`
}
Limit is something a plan caps.
type LimitCell ¶
type LimitCell struct {
Plan string `json:"plan"`
Max int `json:"max"`
Unlimited bool `json:"unlimited,omitempty"`
FairUse bool `json:"fairUse,omitempty"`
}
LimitCell is a plan's allowance for a limit. Max is zero when the plan does not set it.
type LimitRow ¶
type LimitRow struct {
ID string `json:"id"`
Name string `json:"name"`
Unit string `json:"unit,omitempty"`
Period Period `json:"period"`
Description string `json:"description,omitempty"`
Cells []LimitCell `json:"cells"`
}
LimitRow is one limit across the plans, in the same order as the plans.
type LimitValue ¶
type LimitValue struct {
Limit string `json:"limit"`
// Max is the allowance; zero means zero. It must be zero when Unlimited.
Max int `json:"max,omitempty" jsonschema:"minimum=0"`
// Unlimited means no numeric limit.
Unlimited bool `json:"unlimited,omitempty"`
// FairUse: with Max, exceeding it warns and flags the account for review
// instead of refusing; with Unlimited, the allowance is advertised as
// unlimited and subject to the host's fair-use policy.
FairUse bool `json:"fairUse,omitempty"`
}
LimitValue sets one limit for a plan.
type Marketplace ¶
type Marketplace struct {
// PlatformFeeBps is the share in basis points, 0 to 10000.
PlatformFeeBps int `json:"platformFeeBps" jsonschema:"minimum=0,maximum=10000"`
}
Marketplace is the platform's share of sales by sellers.
type Plan ¶
type Plan struct {
ID string `json:"id" jsonschema:"pattern=^[a-z][a-z0-9_]*$"`
Name string `json:"name"`
// Tier is the plan's tier. Exactly one.
Tier string `json:"tier"`
// SelfServe means the plan can be bought through checkout; false means arranged.
SelfServe bool `json:"selfServe"`
// TrialDays is the length of a free trial. Only on a plan with prices.
TrialDays int `json:"trialDays,omitempty" jsonschema:"minimum=0"`
// Prices is empty for a free or arranged plan.
Prices []Price `json:"prices,omitempty"`
// Features are the features the plan sets; an unset feature is off.
Features []FeatureValue `json:"features,omitempty"`
// Limits are the limits the plan sets; an unset limit is zero.
Limits []LimitValue `json:"limits,omitempty"`
// PlatformFeeBps overrides the marketplace fee for sales by an account on this plan.
PlatformFeeBps *int `json:"platformFeeBps,omitempty" jsonschema:"minimum=0,maximum=10000"`
}
Plan is what an account can be on.
type PlanColumn ¶
type PlanColumn struct {
ID string `json:"id"`
Name string `json:"name"`
Tier string `json:"tier"`
TierName string `json:"tierName"`
TierRank int `json:"tierRank"`
SelfServe bool `json:"selfServe"`
Free bool `json:"free"`
Default bool `json:"default"`
TrialDays int `json:"trialDays,omitempty"`
Prices []Price `json:"prices,omitempty"`
}
PlanColumn is one plan in a pricing view.
type Price ¶
type Price struct {
Interval Interval `json:"interval" jsonschema:"enum=month,enum=year"`
// AmountCents is a positive integer in the currency's minor units.
AmountCents int64 `json:"amountCents" jsonschema:"minimum=1"`
}
Price is a plan's price for one billing interval.
type PricingView ¶
type PricingView struct {
App string `json:"app"`
Currency string `json:"currency"`
Plans []PlanColumn `json:"plans"`
Features []FeatureRow `json:"features"`
Limits []LimitRow `json:"limits"`
Products []Product `json:"products,omitempty"`
}
PricingView is a Spec arranged for a page to render: plans in order, and a matrix of features and limits across them. It carries data, not display text.
type Product ¶
type Product struct {
ID string `json:"id" jsonschema:"pattern=^[a-z][a-z0-9_]*$"`
Name string `json:"name"`
// AmountCents is the fixed price; zero when SellerSet.
AmountCents int64 `json:"amountCents,omitempty" jsonschema:"minimum=0"`
// SellerSet means the seller sets the price.
SellerSet bool `json:"sellerSet,omitempty"`
// MinAmountCents is the floor for a seller-set price.
MinAmountCents int64 `json:"minAmountCents,omitempty" jsonschema:"minimum=0"`
}
Product is a one-time purchase.
type Spec ¶
type Spec struct {
// FormatVersion is "1". A reader rejects a version it does not know.
FormatVersion string `json:"formatVersion" jsonschema:"description=The document format version,enum=1"`
// App identifies the product; everything a billing system holds for it is tagged with it.
App string `json:"app" jsonschema:"description=Identifies the product,pattern=^[a-z][a-z0-9-]*$"`
// Currency is a lowercase ISO 4217 code. One currency per Spec.
Currency string `json:"currency" jsonschema:"description=Lowercase ISO 4217 currency code,pattern=^[a-z]{3}$"`
// DefaultPlan is the plan for any unrecognised plan value and for an account
// when billing is not configured.
DefaultPlan string `json:"defaultPlan" jsonschema:"description=Plan ID used for any unrecognised plan and when billing is not configured"`
// Applications are the applications an account can be granted.
Applications []Application `json:"applications" jsonschema:"description=The applications a tier can grant"`
// Tiers are the product's ladder, lowest rank first.
Tiers []Tier `json:"tiers" jsonschema:"description=The product's ladder"`
// Features are what a plan can switch on or set to a level.
Features []Feature `json:"features,omitempty" jsonschema:"description=What a plan can switch on or set to a level"`
// Limits are what a plan can cap.
Limits []Limit `json:"limits,omitempty" jsonschema:"description=What a plan can cap"`
// Plans are what an account can be on.
Plans []Plan `json:"plans" jsonschema:"description=What an account can be on"`
// Products are one-time purchases.
Products []Product `json:"products,omitempty" jsonschema:"description=One-time purchases"`
// Marketplace is the platform's share of sales by sellers, when there are any.
Marketplace *Marketplace `json:"marketplace,omitempty" jsonschema:"description=The platform's share of sales by sellers"`
// Entitlements are what each tier grants an account.
Entitlements []TierGrant `json:"entitlements" jsonschema:"description=What each tier grants an account"`
}
Spec describes a product's pricing and packaging.
func Parse ¶
Parse decodes a Spec from JSON, rejecting unknown fields, and validates it. A Spec that breaks an error rule returns a *ValidationError.
func (*Spec) Allowance ¶
Allowance returns the plan's allowance for a limit. A limit the plan does not set has a maximum of zero. unlimited is true when the plan sets it unlimited, and fairUse when it is marked fair use.
func (*Spec) AtLeast ¶
AtLeast reports whether the plan sets a ladder feature at or above level. It is false for an unset ladder, a switch, an unknown feature or an unknown level.
func (*Spec) Check ¶
Check answers whether an action may proceed. after is the usage after the action: a plan with a maximum of 3 and 2 items in use checks adding a third as 3. The result is Allowed when the limit is unlimited or after is within the maximum; over the maximum it is Warn for a fair-use limit and Refuse otherwise. A limit the plan does not set has a maximum of zero. An unlimited fair-use limit is always Allowed: the Spec sets no threshold and the host applies its own policy.
func (*Spec) EntitlementMap ¶
func (s *Spec) EntitlementMap() EntitlementMap
EntitlementMap returns what each tier grants. Tiers with no grant are listed with an empty Grants.
func (*Spec) GrantsFor ¶
GrantsFor returns what the plan's tier grants an account, in the Spec's order. An unknown plan resolves to the default plan.
func (*Spec) Has ¶
Has reports whether the plan (or the default plan, for an unknown ID) has the feature on, or at any level.
func (*Spec) Level ¶
Level returns the level the plan sets for a ladder feature, or "" when the plan does not set it, or the feature is a switch or unknown.
func (*Spec) Lint ¶
Lint returns the warnings (W001 to W006) for a Spec. Warnings are for review and never reject a Spec. Call Validate first: Lint assumes references resolve.
func (*Spec) PlanOrDefault ¶
PlanOrDefault returns the plan with the given ID, or the default plan when the ID is not a plan, so an unrecognised value never grants more. It returns the zero Plan only for a Spec with no default plan, which Validate rejects.
func (*Spec) PricingView ¶
func (s *Spec) PricingView() PricingView
PricingView arranges the Spec for display. Plans are ordered by tier rank, then by monthly price (plans without one first), then by their order in the Spec.
type Tier ¶
type Tier struct {
ID string `json:"id" jsonschema:"pattern=^[a-z][a-z0-9_]*$"`
Name string `json:"name"`
// Rank is a positive integer, unique within the Spec, lowest first. Ranks
// of different products are not comparable.
Rank int `json:"rank" jsonschema:"minimum=1"`
}
Tier is a position on the product's ladder.
type TierEntitlements ¶
type TierEntitlements struct {
Tier string `json:"tier"`
Name string `json:"name"`
Rank int `json:"rank"`
Grants []Grant `json:"grants"`
}
TierEntitlements is one tier's grants.
type TierGrant ¶
type TierGrant struct {
Tier string `json:"tier"`
Application string `json:"application"`
// Seats is the number of members, at least 1, unless UnlimitedSeats.
Seats int `json:"seats,omitempty" jsonschema:"minimum=0"`
UnlimitedSeats bool `json:"unlimitedSeats,omitempty"`
}
TierGrant is what a tier gives an account for one application.
type ValidationError ¶
type ValidationError struct {
Issues []Issue
}
ValidationError is returned by Load and Parse for a Spec that breaks an error rule. It carries every error found, not only the first.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string