Documentation
¶
Overview ¶
Package cashier is a multi-gateway billing plugin for Nimbus, modeled on Laravel Cashier but gateway-agnostic. One app can register several payment gateways (Stripe, Razorpay, PayU, …), pick a default, select a gateway per request, verify webhooks, and gate access with a paywall.
app.Use(cashier.NewPlugin(cashier.Config{
FromEnv: true, // register gateways whose env keys are set
Default: "razorpay", // default gateway
OnWebhook: func(e cashier.WebhookEvent) error {
switch events.Normalize(e) {
case events.PaymentSucceeded: // grant paywall access
case events.SubscriptionCancelled: // revoke
}
return nil
},
}))
Layout:
cashier.go – plugin + facade config.go – Config manager.go – GatewayManager paywall.go – Paywall engine routes.go – webhook routes contracts/ – PaymentGateway interface gateways/ – Stripe/Razorpay/PayU models/ – transactions, subscriptions events/ – canonical events views/ – checkout templates
Index ¶
- Constants
- Variables
- type CancelParams
- type Capabilities
- type Cashier
- func (c *Cashier) Cancel(ctx context.Context, gateway, id string, p CancelParams) (*Subscription, error)
- func (c *Cashier) Capabilities(gateway string) (Capabilities, error)
- func (c *Cashier) Charge(ctx context.Context, gatewayName string, p ChargeParams) (*Charge, error)
- func (c *Cashier) CloudEnabled() bool
- func (c *Cashier) Customer(ctx context.Context, gateway string, p CustomerParams) (*Customer, error)
- func (c *Cashier) CustomerInfo(subject string) CustomerInfo
- func (c *Cashier) HasAccess(subject, plan string) bool
- func (c *Cashier) Pause(ctx context.Context, gateway, id string) (*Subscription, error)
- func (c *Cashier) Refund(ctx context.Context, gateway string, p RefundParams) (*Refund, error)
- func (c *Cashier) Resume(ctx context.Context, gateway, id string) (*Subscription, error)
- func (c *Cashier) Subscribe(ctx context.Context, gateway string, p SubscriptionParams) (*Subscription, error)
- func (c *Cashier) SubscribedTo(subject, plan string) bool
- func (c *Cashier) Subscription(ctx context.Context, gateway, id string) (*Subscription, error)
- func (c *Cashier) Swap(ctx context.Context, gateway, id string, p SwapParams) (*Subscription, error)
- func (c *Cashier) VerifyPurchase(ctx context.Context, p ReceiptParams) (*IAPEntitlement, error)
- type Catalog
- func (c *Catalog) CurrentOffering() (Offering, bool)
- func (c *Catalog) EntitlementsFor(productID string) []string
- func (c *Catalog) Offering(id string) (Offering, bool)
- func (c *Catalog) Offerings() []Offering
- func (c *Catalog) Product(id string) (Product, bool)
- func (c *Catalog) Products() []Product
- func (c *Catalog) ProductsUnlocking(entitlementID string) []Product
- func (c *Catalog) RegisterOffering(o Offering)
- func (c *Catalog) RegisterProduct(p Product)
- func (c *Catalog) SetCurrentOffering(id string)
- type Charge
- type ChargeParams
- type Config
- type Customer
- type CustomerInfo
- type CustomerParams
- type Decision
- type Entitlement
- type EntitlementInfo
- type EntitlementStore
- type Gateway
- type GatewayManager
- func (m *GatewayManager) Default() Gateway
- func (m *GatewayManager) DefaultName() string
- func (m *GatewayManager) Gateway(name string) (Gateway, error)
- func (m *GatewayManager) Names() []string
- func (m *GatewayManager) Register(g Gateway) *GatewayManager
- func (m *GatewayManager) SetDefault(name string) *GatewayManager
- func (m *GatewayManager) Use(name string) (Gateway, error)
- type IAPEntitlement
- type IAPManager
- func (m *IAPManager) Notification(platform IAPPlatform, payload []byte) (*StoreNotification, error)
- func (m *IAPManager) Register(v contracts.IAPVerifier) *IAPManager
- func (m *IAPManager) Verifier(p contracts.IAPPlatform) (contracts.IAPVerifier, error)
- func (m *IAPManager) Verify(ctx context.Context, p ReceiptParams) (*IAPEntitlement, error)
- type IAPPlatform
- type IAPVerifier
- type Lifecycle
- func (l *Lifecycle) Catalog() *Catalog
- func (l *Lifecycle) GrantPromotional(subject, entitlementID string, until time.Time) (SubscriberEvent, error)
- func (l *Lifecycle) RecordBillingIssue(subject, productID string) (SubscriberEvent, error)
- func (l *Lifecycle) RecordCancellation(subject, productID, reason string) (SubscriberEvent, error)
- func (l *Lifecycle) RecordExpiration(subject, productID, reason string) (SubscriberEvent, error)
- func (l *Lifecycle) RecordExtension(subject, productID string, newExpiry time.Time) (SubscriberEvent, error)
- func (l *Lifecycle) RecordNonRenewingPurchase(subject, productID string, expiresAt time.Time) (SubscriberEvent, error)
- func (l *Lifecycle) RecordPause(subject, productID string) (SubscriberEvent, error)
- func (l *Lifecycle) RecordPurchase(subject, productID string, expiresAt time.Time, periodType string) (SubscriberEvent, error)
- func (l *Lifecycle) RecordUncancellation(subject, productID string) (SubscriberEvent, error)
- func (l *Lifecycle) RevokePromotional(subject, entitlementID string) (SubscriberEvent, error)
- func (l *Lifecycle) Transfer(from, to string) (SubscriberEvent, error)
- type MemoryEntitlementStore
- type MemorySubscriptionStore
- func (m *MemorySubscriptionStore) ActiveForSubject(subject string) []*contracts.Subscription
- func (m *MemorySubscriptionStore) ForSubject(subject string) []*contracts.Subscription
- func (m *MemorySubscriptionStore) Get(gateway, id string) (*contracts.Subscription, bool)
- func (m *MemorySubscriptionStore) Upsert(sub *contracts.Subscription) error
- type Meter
- type MeteredTransaction
- type Offering
- type Package
- type PaymentProof
- type Paywall
- func (p *Paywall) Grant(subject, plan string, expires time.Time) error
- func (p *Paywall) HasAccess(subject, plan string) bool
- func (p *Paywall) RequirePlan(plan string, subjectFn func(*nhttp.Context) string) router.Middleware
- func (p *Paywall) Revoke(subject, plan string) error
- func (p *Paywall) Store() EntitlementStore
- type Plugin
- type Product
- type ReceiptParams
- type Refund
- type RefundParams
- type StoreNotification
- type SubscriberEvent
- type Subscription
- type SubscriptionParams
- type SubscriptionStatus
- type SubscriptionStore
- type SwapParams
- type WebhookEvent
Constants ¶
const ( PlatformApple = contracts.PlatformApple PlatformGoogle = contracts.PlatformGoogle )
const ( EventInitialPurchase = "initial_purchase" EventRenewal = "renewal" EventNonRenewingPurchase = "non_renewing_purchase" EventCancellation = "cancellation" EventUncancellation = "uncancellation" EventExpiration = "expiration" EventBillingIssue = "billing_issue" EventProductChange = "product_change" EventPaused = "subscription_paused" EventExtended = "subscription_extended" EventPromotionalGrant = "promotional_grant" EventPromotionalRevoke = "promotional_revoke" EventTransfer = "transfer" )
Subscriber event types (RevenueCat parity).
const ( PeriodTrial = "trial" PeriodIntro = "intro" PeriodNormal = "normal" PeriodPromotional = "promotional" PeriodGrace = "grace" )
Period types carried on entitlements and events.
const ( ReasonUnsubscribe = "unsubscribe" ReasonBillingError = "billing_error" ReasonDeveloperInitiated = "developer_initiated" ReasonPriceIncrease = "price_increase" ReasonCustomerSupport = "customer_support" ReasonUnknown = "unknown" )
Cancellation / expiration reasons.
const ( SourcePurchase = "purchase" SourcePromotional = "promotional" )
Entitlement sources.
const DefaultGracePeriod = 72 * time.Hour
DefaultGracePeriod is how long a billing issue keeps access alive while the gateway retries the charge.
Variables ¶
var ErrCloudRequired = errors.New("cashier: this feature requires a Cashier Cloud key (set Config.CloudKey or CASHIER_CLOUD_KEY)")
ErrCloudRequired is returned by features that belong to the Cashier Cloud product — the subscription suite and in-app purchase verification.
var ErrNoLifecycleStore = errors.New("cashier: lifecycle has no paywall store")
ErrNoLifecycleStore is returned when the lifecycle has no paywall to write to.
var ErrUnsupported = contracts.ErrUnsupported
ErrUnsupported is returned when a gateway lacks a requested capability.
Functions ¶
This section is empty.
Types ¶
type CancelParams ¶ added in v1.6.0
type CancelParams = contracts.CancelParams
Re-exported subscription and capability types so callers stay in the cashier package.
type Capabilities ¶ added in v1.6.0
type Capabilities struct {
Subscriptions bool
PlanChanges bool
Pausing bool
Refunds bool
Customers bool
}
Capabilities describes what a gateway can do beyond a one-off charge.
type Cashier ¶
type Cashier struct {
Gateways *GatewayManager
Paywall *Paywall
// Subscriptions mirrors gateway subscriptions locally for fast access
// checks. Nil disables mirroring — the gateway stays the source of truth,
// but every check then has to reach the provider.
Subscriptions SubscriptionStore
// IAP verifies Apple and Google in-app purchases. Nil until a verifier is
// registered.
IAP *IAPManager
// Catalog maps products to entitlements and holds paywall offerings
// (RevenueCat's Products/Entitlements/Offerings).
Catalog *Catalog
// Lifecycle turns payment facts into entitlement changes and canonical
// subscriber events (RevenueCat's event stream).
Lifecycle *Lifecycle
}
Cashier is the facade tying the gateway manager to the paywall. Resolve it from the container ("cashier") or hold the plugin's instance.
func (*Cashier) Cancel ¶ added in v1.6.0
func (c *Cashier) Cancel(ctx context.Context, gateway, id string, p CancelParams) (*Subscription, error)
Cancel ends a subscription. AtPeriodEnd (the humane default) keeps access until the paid period runs out.
func (*Cashier) Capabilities ¶ added in v1.6.0
func (c *Cashier) Capabilities(gateway string) (Capabilities, error)
Capabilities reports which optional capabilities a gateway implements, for a UI that hides what a provider cannot do rather than offering it and failing.
func (*Cashier) Charge ¶
Charge starts a payment on the named gateway (empty → the default gateway).
func (*Cashier) CloudEnabled ¶ added in v1.6.0
CloudEnabled reports whether the Cashier Cloud subscription suite is active on this facade.
func (*Cashier) Customer ¶ added in v1.6.0
func (c *Cashier) Customer(ctx context.Context, gateway string, p CustomerParams) (*Customer, error)
Customer creates a gateway customer, where the gateway has the concept.
func (*Cashier) CustomerInfo ¶ added in v1.6.0
func (c *Cashier) CustomerInfo(subject string) CustomerInfo
CustomerInfo builds the aggregate for a subject from the paywall store and the subscription mirror. Customer management is part of the Cashier Cloud suite: on a payments-only facade (no cloud key, so no lifecycle) the aggregate comes back empty.
func (*Cashier) Subscribe ¶ added in v1.6.0
func (c *Cashier) Subscribe(ctx context.Context, gateway string, p SubscriptionParams) (*Subscription, error)
Subscribe starts a subscription on the named gateway (empty → default), and mirrors the result locally when a store is configured.
The mirror write is best-effort by design: the subscription exists at the gateway the moment it returns, and failing the caller's request because a local cache write hiccuped would be the wrong trade. A missed mirror is reconciled by the next webhook.
func (*Cashier) SubscribedTo ¶ added in v1.6.0
SubscribedTo reports whether a subject has an active subscription, optionally to a specific plan (empty plan → any).
func (*Cashier) Subscription ¶ added in v1.6.0
Subscription fetches a subscription from its gateway and refreshes the mirror.
func (*Cashier) Swap ¶ added in v1.6.0
func (c *Cashier) Swap(ctx context.Context, gateway, id string, p SwapParams) (*Subscription, error)
Swap changes a subscription's plan, where the gateway allows it.
func (*Cashier) VerifyPurchase ¶ added in v1.6.0
func (c *Cashier) VerifyPurchase(ctx context.Context, p ReceiptParams) (*IAPEntitlement, error)
VerifyPurchase verifies an in-app purchase through the metered manager and, when a subscription store is configured, mirrors the entitlement so an access check treats an IAP subscription exactly like a card one.
This is the method an app's receipt-validation endpoint calls: hand it the platform, product and token the client reported, and it returns only what Nimbus Cloud both cryptographically verified and metered.
type Catalog ¶ added in v1.6.0
type Catalog struct {
// contains filtered or unexported fields
}
Catalog is the registry of products, their entitlement mappings, and offerings. It is safe for concurrent use.
func (*Catalog) CurrentOffering ¶ added in v1.6.0
CurrentOffering returns the offering paywalls should present now.
func (*Catalog) EntitlementsFor ¶ added in v1.6.0
EntitlementsFor returns the entitlement ids a product unlocks. An unregistered product (or one registered without entitlements) unlocks an entitlement named after itself, which keeps plan-slug based paywalls working unchanged.
func (*Catalog) Products ¶ added in v1.6.0
Products returns every registered product in registration order.
func (*Catalog) ProductsUnlocking ¶ added in v1.6.0
ProductsUnlocking returns every product that grants an entitlement.
func (*Catalog) RegisterOffering ¶ added in v1.6.0
RegisterOffering adds or replaces an offering. The first registered offering becomes current unless SetCurrentOffering has chosen one.
func (*Catalog) RegisterProduct ¶ added in v1.6.0
RegisterProduct adds or replaces a product.
func (*Catalog) SetCurrentOffering ¶ added in v1.6.0
SetCurrentOffering picks the offering paywalls should present.
type Charge ¶
Re-exported contract types so callers use the cashier package without importing contracts directly.
type ChargeParams ¶
type ChargeParams = contracts.ChargeParams
Re-exported contract types so callers use the cashier package without importing contracts directly.
type Config ¶
type Config struct {
// Manager is the gateway registry. Nil → a new empty one is created.
Manager *GatewayManager
// Default gateway name. Falls back to PAYMENTS_DEFAULT_GATEWAY, then the
// first registered gateway.
Default string
// FromEnv auto-registers gateways whose credentials are present in the
// environment (Stripe: STRIPE_KEY[/STRIPE_WEBHOOK_SECRET]; Razorpay:
// RAZORPAY_KEY_ID/RAZORPAY_KEY_SECRET[/RAZORPAY_WEBHOOK_SECRET]; PayU:
// PAYU_MERCHANT_KEY/PAYU_MERCHANT_SALT).
FromEnv bool
// Paywall backs entitlement checks (nil → in-memory store).
Paywall *Paywall
// WebhookPrefix mounts per-gateway webhooks at "<prefix>/<gateway>/webhook".
// Default "/payments".
WebhookPrefix string
// OnWebhook runs for every verified webhook (any gateway). Use
// events.Normalize(evt) to branch on canonical events and grant/revoke
// paywall access.
OnWebhook func(WebhookEvent) error
// Subscriptions mirrors gateway subscriptions locally for fast access
// checks. Nil → no mirror (the gateway stays authoritative).
Subscriptions SubscriptionStore
// IAP verifies Apple/Google in-app purchases. Nil → in-app purchases are
// not accepted.
IAP *IAPManager
// CloudKey is the Cashier Cloud secret key ("cshr_live_…"). It activates
// the subscription suite — the product→entitlement catalogue, offerings,
// the subscriber lifecycle, and CustomerInfo — and is required for
// Apple/Google in-app purchase verification. Falls back to the
// CASHIER_CLOUD_KEY environment variable. Without it the plugin is
// payments-only: gateways, charges, webhooks, refunds, and the basic
// paywall keep working; the fields below are ignored.
CloudKey string
// Products seeds the catalogue mapping purchasable products onto the
// entitlements they unlock (RevenueCat's Products → Entitlements).
// Cashier Cloud only.
Products []Product
// Offerings seeds the paywall offerings; CurrentOffering picks the one
// paywalls present (default: the first registered). Cashier Cloud only.
Offerings []Offering
CurrentOffering string
// GracePeriod is how long a billing issue keeps entitlements alive while
// the gateway retries the charge. 0 → DefaultGracePeriod (72h).
// Cashier Cloud only.
GracePeriod time.Duration
// OnSubscriberEvent receives every canonical lifecycle event
// (initial_purchase, renewal, cancellation, billing_issue, …) for
// analytics, email, or your own event bus. Cashier Cloud only.
OnSubscriberEvent func(SubscriberEvent)
}
Config configures the Cashier plugin.
type Customer ¶ added in v1.6.0
Re-exported subscription and capability types so callers stay in the cashier package.
type CustomerInfo ¶ added in v1.6.0
type CustomerInfo struct {
Subject string `json:"subject"`
RequestedAt time.Time `json:"requested_at"`
// Entitlements is every known entitlement, keyed by id — active or not.
Entitlements map[string]EntitlementInfo `json:"entitlements"`
// ActiveEntitlementIDs lists the ids currently granting access, sorted.
ActiveEntitlementIDs []string `json:"active_entitlement_ids"`
// ActiveProductIDs lists the products behind active entitlements, sorted.
ActiveProductIDs []string `json:"active_product_ids"`
// ActiveSubscriptions are gateway subscriptions currently granting access,
// from the local mirror (empty when no mirror is configured).
ActiveSubscriptions []*Subscription `json:"active_subscriptions,omitempty"`
// LatestExpiresAt is the furthest-out expiry across active entitlements
// (nil when an active entitlement never expires or none are active).
LatestExpiresAt *time.Time `json:"latest_expires_at,omitempty"`
}
CustomerInfo aggregates a subject's subscription state.
func (CustomerInfo) HasEntitlement ¶ added in v1.6.0
func (ci CustomerInfo) HasEntitlement(id string) bool
HasEntitlement reports whether the aggregate holds an active entitlement.
type CustomerParams ¶ added in v1.6.0
type CustomerParams = contracts.CustomerParams
Re-exported subscription and capability types so callers stay in the cashier package.
type Decision ¶ added in v1.6.0
Meter and its transaction/decision types, re-exported from the iap package.
type Entitlement ¶
type Entitlement struct {
Subject string
Plan string // entitlement id
ExpiresAt time.Time
ProductID string // the product whose purchase granted this ("" for promos)
PeriodType string // trial|intro|normal|promotional|grace
Source string // purchase|promotional
WillRenew bool // false once cancelled/paused (access persists to expiry)
}
Entitlement grants a subject (usually a user id) access to a plan until ExpiresAt. A zero ExpiresAt means it never expires.
Plan is the entitlement id being granted; the fields below carry the RevenueCat-style context around it. Stores that predate them may persist only the core triple — the lifecycle degrades gracefully.
type EntitlementInfo ¶ added in v1.6.0
type EntitlementInfo struct {
ID string `json:"id"`
Active bool `json:"active"`
WillRenew bool `json:"will_renew"`
PeriodType string `json:"period_type,omitempty"` // trial|intro|normal|promotional|grace
ProductID string `json:"product_id,omitempty"`
Source string `json:"source,omitempty"` // purchase|promotional
ExpiresAt *time.Time `json:"expires_at,omitempty"`
}
EntitlementInfo is one entitlement's state inside CustomerInfo.
type EntitlementStore ¶
type EntitlementStore interface {
Grant(e Entitlement) error
Revoke(subject, plan string) error
Active(subject, plan string) (bool, error)
List(subject string) ([]Entitlement, error)
}
EntitlementStore persists entitlements. Implement it to back the paywall with a database; MemoryEntitlementStore is the default in-memory implementation.
type Gateway ¶
type Gateway = contracts.PaymentGateway
Re-exported contract types so callers use the cashier package without importing contracts directly.
type GatewayManager ¶
type GatewayManager struct {
// contains filtered or unexported fields
}
GatewayManager holds the app's registered gateways and the default selection. It is safe for concurrent use — one app can serve many gateways at once and pick per request.
func NewGatewayManager ¶
func NewGatewayManager() *GatewayManager
NewGatewayManager creates an empty gateway registry.
func (*GatewayManager) Default ¶
func (m *GatewayManager) Default() Gateway
Default returns the default gateway, or nil when none are registered.
func (*GatewayManager) DefaultName ¶
func (m *GatewayManager) DefaultName() string
DefaultName returns the default gateway's name.
func (*GatewayManager) Gateway ¶
func (m *GatewayManager) Gateway(name string) (Gateway, error)
Gateway returns the gateway registered under name.
func (*GatewayManager) Names ¶
func (m *GatewayManager) Names() []string
Names returns the registered gateway names, sorted.
func (*GatewayManager) Register ¶
func (m *GatewayManager) Register(g Gateway) *GatewayManager
Register adds a gateway. The first one registered becomes the default until SetDefault is called. Re-registering the same name replaces it.
func (*GatewayManager) SetDefault ¶
func (m *GatewayManager) SetDefault(name string) *GatewayManager
SetDefault selects the default gateway by name (no-op if not registered).
type IAPEntitlement ¶ added in v1.6.0
type IAPEntitlement = contracts.IAPEntitlement
Re-exported IAP types.
type IAPManager ¶ added in v1.6.0
type IAPManager struct {
// contains filtered or unexported fields
}
IAPManager holds the registered store verifiers and routes a request to the right one. Apple and Google are verified through entirely different APIs, so each platform registers its own verifier and this picks by platform.
Verification is a paid Nimbus Cloud feature, so a manager carries a meter and every registered verifier is wrapped by it (see iap.Metered). Constructing a manager with NewIAPManager, which has no meter, is for development and tests only; production uses NewMeteredIAPManager with a Nimbus Cloud meter.
func MeteredFromCloud ¶ added in v1.6.0
func MeteredFromCloud(apiKey string) (*IAPManager, error)
MeteredFromCloud builds a production IAP manager gated by a Nimbus Cloud meter keyed on the given API key. This is the one-liner a developer uses to turn Apple/Google verification into the paid feature: register Apple/Google verifiers on the returned manager and every entitlement is metered.
func NewIAPManager ¶ added in v1.6.0
func NewIAPManager() *IAPManager
NewIAPManager builds an unmetered manager. Verifiers registered on it are NOT gated — use it only for development and tests. Production must use NewMeteredIAPManager so transactions are metered and billed.
func NewMeteredIAPManager ¶ added in v1.6.0
func NewMeteredIAPManager(m Meter) *IAPManager
NewMeteredIAPManager builds a manager whose verifiers are all gated by the meter. This is the supported production path: without a meter, in-app purchases are not a paid feature.
func (*IAPManager) Notification ¶ added in v1.6.0
func (m *IAPManager) Notification(platform IAPPlatform, payload []byte) (*StoreNotification, error)
Notification verifies and decodes a server-to-server store notification.
func (*IAPManager) Register ¶ added in v1.6.0
func (m *IAPManager) Register(v contracts.IAPVerifier) *IAPManager
Register adds a platform verifier, wrapping it in the manager's meter when one is configured so its entitlements are gated.
func (*IAPManager) Verifier ¶ added in v1.6.0
func (m *IAPManager) Verifier(p contracts.IAPPlatform) (contracts.IAPVerifier, error)
Verifier returns the verifier for a platform.
func (*IAPManager) Verify ¶ added in v1.6.0
func (m *IAPManager) Verify(ctx context.Context, p ReceiptParams) (*IAPEntitlement, error)
Verify checks a client-supplied purchase against its store and returns the entitlement the store actually vouches for.
The whole point of server-side verification is that the client is not trusted: the app hands over a token, and only the store's signed answer decides what the user owns. A caller must gate access on the returned entitlement, never on the app's own claim.
type Lifecycle ¶ added in v1.6.0
type Lifecycle struct {
// contains filtered or unexported fields
}
Lifecycle applies subscriber events to entitlements and emits them.
func NewLifecycle ¶ added in v1.6.0
func NewLifecycle(catalog *Catalog, paywall *Paywall, grace time.Duration, onEvent func(SubscriberEvent)) *Lifecycle
NewLifecycle builds the engine. A nil catalog gets an empty one (products then unlock entitlements named after themselves); grace <= 0 uses DefaultGracePeriod; onEvent may be nil.
func (*Lifecycle) Catalog ¶ added in v1.6.0
Catalog returns the product catalogue the lifecycle resolves against.
func (*Lifecycle) GrantPromotional ¶ added in v1.6.0
func (l *Lifecycle) GrantPromotional(subject, entitlementID string, until time.Time) (SubscriberEvent, error)
GrantPromotional gives an entitlement without a purchase (a comp, a beta invite, an outage credit) until `until`; zero = forever. It never renews.
func (*Lifecycle) RecordBillingIssue ¶ added in v1.6.0
func (l *Lifecycle) RecordBillingIssue(subject, productID string) (SubscriberEvent, error)
RecordBillingIssue applies a failed renewal charge: instead of revoking, it opens a grace period — each of the product's entitlements is extended to at least now+grace while the gateway retries. Returns when grace runs out.
func (*Lifecycle) RecordCancellation ¶ added in v1.6.0
func (l *Lifecycle) RecordCancellation(subject, productID, reason string) (SubscriberEvent, error)
RecordCancellation turns renewal off without touching access: the subject keeps every entitlement until it expires on its own. reason is one of the Reason* constants ("" → unsubscribe).
func (*Lifecycle) RecordExpiration ¶ added in v1.6.0
func (l *Lifecycle) RecordExpiration(subject, productID, reason string) (SubscriberEvent, error)
RecordExpiration revokes a product's entitlements now and emits expiration. Call it from the sweep that notices a lapsed period, or on a refund / immediate revocation (pass the fitting reason).
func (*Lifecycle) RecordExtension ¶ added in v1.6.0
func (l *Lifecycle) RecordExtension(subject, productID string, newExpiry time.Time) (SubscriberEvent, error)
RecordExtension pushes a subscription's expiry out (a support credit, an outage make-good) and emits subscription_extended.
func (*Lifecycle) RecordNonRenewingPurchase ¶ added in v1.6.0
func (l *Lifecycle) RecordNonRenewingPurchase(subject, productID string, expiresAt time.Time) (SubscriberEvent, error)
RecordNonRenewingPurchase applies a one-off purchase (a lifetime unlock, a consumable): entitlements are granted but nothing will renew. A zero expiresAt never lapses.
func (*Lifecycle) RecordPause ¶ added in v1.6.0
func (l *Lifecycle) RecordPause(subject, productID string) (SubscriberEvent, error)
RecordPause marks the subscription as pausing at period end (access is kept until then, like a cancellation with intent to return).
func (*Lifecycle) RecordPurchase ¶ added in v1.6.0
func (l *Lifecycle) RecordPurchase(subject, productID string, expiresAt time.Time, periodType string) (SubscriberEvent, error)
RecordPurchase applies a verified subscription payment: it grants the product's entitlements until expiresAt and emits the matching event — initial_purchase for a first-time subscriber, renewal when the subject already held this product, or product_change when they held a different paid product (whose exclusive entitlements are revoked).
periodType is one of the Period* constants; "" means PeriodNormal, and a product with trial days still in the future may pass PeriodTrial.
func (*Lifecycle) RecordUncancellation ¶ added in v1.6.0
func (l *Lifecycle) RecordUncancellation(subject, productID string) (SubscriberEvent, error)
RecordUncancellation re-enables renewal on a cancelled-but-not-expired subscription.
func (*Lifecycle) RevokePromotional ¶ added in v1.6.0
func (l *Lifecycle) RevokePromotional(subject, entitlementID string) (SubscriberEvent, error)
RevokePromotional removes a promotional entitlement.
type MemoryEntitlementStore ¶
type MemoryEntitlementStore struct {
// contains filtered or unexported fields
}
MemoryEntitlementStore is a concurrency-safe in-memory EntitlementStore. Use a database-backed store in production (entitlements should survive a restart).
func NewMemoryEntitlementStore ¶
func NewMemoryEntitlementStore() *MemoryEntitlementStore
NewMemoryEntitlementStore creates an empty in-memory store.
func (*MemoryEntitlementStore) Active ¶
func (s *MemoryEntitlementStore) Active(subject, plan string) (bool, error)
func (*MemoryEntitlementStore) Grant ¶
func (s *MemoryEntitlementStore) Grant(e Entitlement) error
func (*MemoryEntitlementStore) List ¶
func (s *MemoryEntitlementStore) List(subject string) ([]Entitlement, error)
func (*MemoryEntitlementStore) Revoke ¶
func (s *MemoryEntitlementStore) Revoke(subject, plan string) error
type MemorySubscriptionStore ¶ added in v1.6.0
type MemorySubscriptionStore struct {
// contains filtered or unexported fields
}
MemorySubscriptionStore is a process-local SubscriptionStore. It is the default and is intended for tests and single-process apps; a real deployment backs the mirror with the cashier_subscriptions table.
func NewMemorySubscriptionStore ¶ added in v1.6.0
func NewMemorySubscriptionStore() *MemorySubscriptionStore
NewMemorySubscriptionStore builds an empty in-memory store.
func (*MemorySubscriptionStore) ActiveForSubject ¶ added in v1.6.0
func (m *MemorySubscriptionStore) ActiveForSubject(subject string) []*contracts.Subscription
ActiveForSubject returns only the subscriptions that currently grant access.
func (*MemorySubscriptionStore) ForSubject ¶ added in v1.6.0
func (m *MemorySubscriptionStore) ForSubject(subject string) []*contracts.Subscription
ForSubject returns every mirrored subscription for a subject.
func (*MemorySubscriptionStore) Get ¶ added in v1.6.0
func (m *MemorySubscriptionStore) Get(gateway, id string) (*contracts.Subscription, bool)
Get returns one mirrored subscription.
func (*MemorySubscriptionStore) Upsert ¶ added in v1.6.0
func (m *MemorySubscriptionStore) Upsert(sub *contracts.Subscription) error
Upsert inserts or replaces the mirror of a subscription.
type Meter ¶ added in v1.6.0
Meter and its transaction/decision types, re-exported from the iap package.
type MeteredTransaction ¶ added in v1.6.0
type MeteredTransaction = iap.MeteredTransaction
Meter and its transaction/decision types, re-exported from the iap package.
type Offering ¶ added in v1.6.0
type Offering struct {
ID string `json:"id"`
Packages []Package `json:"packages"`
Metadata map[string]string `json:"metadata,omitempty"`
}
Offering is a named group of packages a paywall presents.
type Package ¶ added in v1.6.0
type Package struct {
ID string `json:"id"` // e.g. "monthly", "annual"
ProductID string `json:"product_id"`
}
Package is one product slot inside an offering.
type PaymentProof ¶
type PaymentProof = contracts.PaymentProof
Re-exported contract types so callers use the cashier package without importing contracts directly.
type Paywall ¶
type Paywall struct {
// contains filtered or unexported fields
}
Paywall gates access to plans/features based on entitlements. Grant on a successful payment (typically in a webhook handler); gate routes with RequirePlan.
func NewPaywall ¶
func NewPaywall(store EntitlementStore) *Paywall
NewPaywall builds a paywall over a store (nil → in-memory).
func (*Paywall) RequirePlan ¶
RequirePlan returns middleware that blocks a route unless the current subject has active access to plan. subjectFn extracts the subject (e.g. the user id) from the request; return "" for an anonymous/unknown user. Blocked requests receive HTTP 402 Payment Required.
func (*Paywall) Store ¶
func (p *Paywall) Store() EntitlementStore
Store returns the underlying store.
type Plugin ¶
type Plugin struct {
nimbus.BasePlugin
Cashier *Cashier
// contains filtered or unexported fields
}
Plugin wires Cashier into Nimbus.
func (*Plugin) DefaultConfig ¶
func (*Plugin) Migrations ¶
Migrations creates the cashier tables (transactions, subscriptions).
func (*Plugin) RegisterRoutes ¶
RegisterRoutes mounts a signature-verified webhook endpoint per gateway at "<WebhookPrefix>/<gateway>/webhook" (e.g. /payments/razorpay/webhook).
type Product ¶ added in v1.6.0
type Product struct {
ID string `json:"id"` // plan/price identifier ("pro", "price_123")
Name string `json:"name"` // display name
Amount int64 `json:"amount"`
Currency string `json:"currency"`
PeriodMonths int `json:"period_months"` // billing period; 0 = non-renewing / lifetime
TrialDays int `json:"trial_days"`
Entitlements []string `json:"entitlements"` // entitlement ids this product unlocks
}
Product is one purchasable SKU and the entitlements it unlocks.
type ReceiptParams ¶ added in v1.6.0
type ReceiptParams = contracts.ReceiptParams
Re-exported IAP types.
type Refund ¶ added in v1.6.0
Re-exported subscription and capability types so callers stay in the cashier package.
type RefundParams ¶ added in v1.6.0
type RefundParams = contracts.RefundParams
Re-exported subscription and capability types so callers stay in the cashier package.
type StoreNotification ¶ added in v1.6.0
type StoreNotification = contracts.StoreNotification
Re-exported IAP types.
type SubscriberEvent ¶ added in v1.6.0
type SubscriberEvent struct {
Type string `json:"type"`
Subject string `json:"subject"`
ProductID string `json:"product_id,omitempty"`
OldProductID string `json:"old_product_id,omitempty"` // product_change
FromSubject string `json:"from_subject,omitempty"` // transfer
EntitlementIDs []string `json:"entitlement_ids,omitempty"`
PeriodType string `json:"period_type,omitempty"`
Reason string `json:"reason,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
At time.Time `json:"at"`
}
SubscriberEvent is one canonical moment in a subscriber's life.
type Subscription ¶ added in v1.6.0
type Subscription = contracts.Subscription
Re-exported subscription and capability types so callers stay in the cashier package.
type SubscriptionParams ¶ added in v1.6.0
type SubscriptionParams = contracts.SubscriptionParams
Re-exported subscription and capability types so callers stay in the cashier package.
type SubscriptionStatus ¶ added in v1.6.0
type SubscriptionStatus = contracts.SubscriptionStatus
Re-exported subscription and capability types so callers stay in the cashier package.
type SubscriptionStore ¶ added in v1.6.0
type SubscriptionStore interface {
Upsert(sub *contracts.Subscription) error
Get(gateway, id string) (*contracts.Subscription, bool)
ForSubject(subject string) []*contracts.Subscription
ActiveForSubject(subject string) []*contracts.Subscription
}
SubscriptionStore persists the local mirror of gateway subscriptions.
Upsert is keyed on (gateway, gateway id): a webhook, a verification and a manual refresh all describe the same subscription, and each must update the one row rather than insert a duplicate. Implement it over your database; the in-memory store is the default and is what the tests run against.
type SwapParams ¶ added in v1.6.0
type SwapParams = contracts.SwapParams
Re-exported subscription and capability types so callers stay in the cashier package.
type WebhookEvent ¶
type WebhookEvent = contracts.WebhookEvent
Re-exported contract types so callers use the cashier package without importing contracts directly.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package contracts defines the payment-gateway contract and the shared value types every gateway and the manager operate on.
|
Package contracts defines the payment-gateway contract and the shared value types every gateway and the manager operate on. |
|
Package events defines canonical, gateway-agnostic payment events and maps each gateway's raw webhook types onto them — so paywall logic (grant/revoke) is written once regardless of which gateway delivered the event.
|
Package events defines canonical, gateway-agnostic payment events and maps each gateway's raw webhook types onto them — so paywall logic (grant/revoke) is written once regardless of which gateway delivered the event. |
|
Package gateways holds the concrete payment gateways (Stripe, Razorpay, PayU, …).
|
Package gateways holds the concrete payment gateways (Stripe, Razorpay, PayU, …). |
|
Package iap holds the Apple and Google in-app-purchase verifiers.
|
Package iap holds the Apple and Google in-app-purchase verifiers. |
|
Package models holds the persisted Cashier tables (transactions, subscriptions) and their migrations.
|
Package models holds the persisted Cashier tables (transactions, subscriptions) and their migrations. |