Help us shape the future of Go! Take the 2026 Go Developer Survey

commerce

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: AGPL-3.0 Imports: 53 Imported by: 0

Documentation

Overview

Package commerce sells access to MCP servers: products → Stripe Checkout → orders → customers → entitlements → provisioning on billing servers over the signed mcp-billing/v1 protocol (spec §4.6, docs/COMMERCE.md).

Index

Constants

View Source
const (
	ProductProvisioning = "provisioning"
	ProductService      = "service"
	ProductDigital      = "digital"
)

Product is a provisioning plan, service or private digital download. PriceMinor is in the currency's minor unit (fils for AED). SEO uses the seo.FromMap keys.

View Source
const (
	OrderPending      = "pending"
	OrderPaid         = "paid"
	OrderProvisioning = "provisioning"
	OrderActive       = "active"
	OrderFailed       = "failed"
	OrderRefunded     = "refunded"
	OrderCancelled    = "cancelled"
)

Order statuses.

View Source
const (
	EntitlementPending   = "pending"
	EntitlementActive    = "active"
	EntitlementSuspended = "suspended"
	EntitlementExpired   = "expired"
	EntitlementFailed    = "failed"
)

Entitlement statuses.

View Source
const (
	ServerUnknown = "unknown"
	ServerOK      = "ok"
	ServerError   = "error"
)

Server statuses.

View Source
const (
	SettingCurrency      = "commerce.currency"
	SettingStripeSecret  = "commerce.stripe_secret_key"     // live key; db.Encrypt, AAD = key
	SettingWebhookSecret = "commerce.stripe_webhook_secret" // live webhook; db.Encrypt, AAD = key
	SettingStripeMode    = "commerce.stripe_mode"           // test | live
	SettingStripeTestKey = "commerce.stripe_test_secret_key"
	SettingWebhookTest   = "commerce.stripe_test_webhook_secret"
	SettingGraceDays     = "commerce.grace_days"
	SettingAutomaticTax  = "commerce.automatic_tax"
)

Setting keys.

View Source
const (
	PermManage     = "commerce.manage"
	PermViewOrders = "commerce.view_orders"
)

Permissions.

View Source
const (
	JobProvision            = "commerce.provision"
	JobSuspend              = "commerce.suspend"
	JobMagicMail            = "commerce.magic_mail"
	JobBillingMail          = "commerce.billing_mail"
	JobResume               = "commerce.resume"
	CronExpire              = "commerce.expire"
	CronGrace               = "commerce.billing_grace"
	HookOrderPaid           = "commerce.order.paid"
	HookEntitlementActivate = "commerce.entitlement.activated"
	HookSubActivated        = "subscription.activated"
	HookSubPastDue          = "subscription.past_due"
	HookSubCanceled         = "subscription.canceled"
)

Job kinds and hook names.

View Source
const ObservationKind = "commerce.billing_server"

ObservationKind is the background capability that reads each billing server's catalog. System Health reads the saved observation; it never calls a server while rendering.

View Source
const (
	SettingSettingsVersion = "commerce.settings_version" // bootstrap steps applied to this store

)

Stripe mode settings. commerce.stripe_mode stays "test" | "live" and an absent value still means live (stripeMode); bootstrapStripeMode pins the value explicitly once per store so that rule is only ever a fallback.

Variables

View Source
var (
	ErrNotFound            = errors.New("commerce: not found")
	ErrStripeNotConfigured = errors.New("commerce: Stripe is not configured")
	ErrInvalidSignature    = errors.New("commerce: invalid Stripe webhook signature")
	ErrNotActive           = errors.New("commerce: entitlement is not active")
	ErrNoServer            = errors.New("commerce: entitlement has no reachable billing server")
	ErrPeriodEnded         = errors.New("commerce: the entitlement period has ended")
	ErrPlanRequired        = errors.New("commerce: subscription plan required")
)

Errors returned by the service.

Functions

func FormatPrice

func FormatPrice(minor int64, currency string) string

FormatPrice renders minor units as a major-unit decimal string ("12.50").

func IsExampleProductSlug added in v0.12.0

func IsExampleProductSlug(slug string) bool

IsExampleProductSlug reports inactive learning templates seeded on first boot.

func ParsePrice

func ParsePrice(s, currency string) (int64, error)

ParsePrice converts a major-unit amount ("12.5", "12,50") to minor units of currency. It rejects negatives and more decimals than the currency has.

func RequirePlan added in v0.12.0

func RequirePlan(ctx context.Context, d *kernel.Deps, accountID, plan string) error

RequirePlan looks up the commerce service. Plugins and later modules call it instead of reimplementing billing.

func Slugify

func Slugify(s string) string

Slugify lowercases s and joins its letter/digit runs with "-" (Arabic letters are kept).

Types

type AccountSettings

type AccountSettings struct {
	RegistrationEnabled bool `json:"registration_enabled"`
}

AccountSettings controls native customer registration only. Registration does not grant paid access or claim verified ownership of an email address.

type ActionResponse

type ActionResponse struct {
	Entitlement *Entitlement `json:"entitlement"`
}

ActionResponse is returned by entitlement actions.

type Cart

type Cart struct {
	Lines      []CartLine `json:"lines"`
	TotalMinor int64      `json:"total_minor"`
	Currency   string     `json:"currency"`
	Key        string     `json:"key"`
}

type CartCheckout

type CartCheckout struct {
	Intake         map[string]map[string]string `json:"intake,omitempty"`
	Items          []CartItem                   `json:"items"`
	Email          string                       `json:"email"`
	Name           string                       `json:"name"`
	Notes          string                       `json:"notes"`
	IdempotencyKey string                       `json:"idempotency_key"`
}

type CartItem

type CartItem struct {
	ProductID string `json:"product_id"`
	Quantity  int    `json:"quantity"`
}

type CartLine

type CartLine struct {
	Product    Product `json:"product"`
	Quantity   int     `json:"quantity"`
	TotalMinor int64   `json:"total_minor"`
}

type Catalog

type Catalog struct {
	Product map[string]any  `json:"product,omitempty"`
	Plans   []Plan          `json:"plans"`
	Tools   []any           `json:"tools,omitempty"`
	Raw     json.RawMessage `json:"raw,omitempty"`
}

Catalog is a billing server's GET /catalog data. Raw keeps the full payload (tools, product description…).

type CurrencyPrice added in v0.12.0

type CurrencyPrice struct {
	PriceID     string `json:"price_id"`
	AmountMinor int64  `json:"amount_minor"`
}

CurrencyPrice is one plan price in a single ISO currency.

type DeleteResponse

type DeleteResponse struct {
	ID      string `json:"id"`
	Deleted bool   `json:"deleted"`
}

DeleteResponse is returned by DELETE operations.

type Entitlement

type Entitlement struct {
	ID          string         `json:"id"`
	CustomerID  string         `json:"customer_id"`
	Email       string         `json:"email"`
	OrderID     string         `json:"order_id"`
	OrderNumber string         `json:"order_number"`
	ProductID   string         `json:"product_id"`
	ProductName string         `json:"product_name"`
	ServerID    string         `json:"server_id"`
	ServerName  string         `json:"server_name"`
	PlanCode    string         `json:"plan_code"`
	ExternalID  string         `json:"external_id"`
	Status      string         `json:"status"`
	StartsAt    string         `json:"starts_at"`
	EndsAt      string         `json:"ends_at"`
	LastSyncAt  string         `json:"last_sync_at"`
	Meta        map[string]any `json:"meta"`
	CreatedAt   string         `json:"created_at"`
}

Entitlement is a customer's access to a plan on a billing server, created by exactly one order.

type IntakeField

type IntakeField struct {
	Key       string `json:"key"`
	Label     string `json:"label"`
	Required  bool   `json:"required"`
	MaxLength int    `json:"max_length"`
}

type Invoice added in v0.12.0

type Invoice struct {
	ID               string `json:"id"`
	StripeInvoiceID  string `json:"stripe_invoice_id"`
	HostedInvoiceURL string `json:"hosted_invoice_url"`
	PDFURL           string `json:"pdf_url"`
	Status           string `json:"status"`
	AmountMinor      int64  `json:"amount_minor"`
	Currency         string `json:"currency"`
	CreatedAt        string `json:"created_at"`
}

Invoice is one Stripe invoice stored for /account.

type ListQuery

type ListQuery struct {
	Search, Status string
	CustomerID     string
	Page, PerPage  int
}

ListQuery filters Orders and Entitlements. Search matches the email or order number; Status filters exactly. Page/PerPage default 1/20 (max 100).

type MCBBError

type MCBBError struct {
	Status  int
	Message string
}

MCBBError is a failed billing-server call. Status is the HTTP status (0 for transport failures). Retryable reports transient failures (transport, 408, 429, 5xx), mirroring the PHP client's retry rule.

func (*MCBBError) Error

func (e *MCBBError) Error() string

func (*MCBBError) Retryable

func (e *MCBBError) Retryable() bool

Retryable reports whether the call may succeed when retried.

type Module

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

Module is the commerce kernel.Module.

func New

func New() *Module

New returns the commerce module.

func (*Module) Catalogs

func (*Module) Catalogs() fs.FS

Catalogs implements kernel.CatalogProvider.

func (*Module) Enabled added in v0.16.0

func (*Module) Enabled(ctx context.Context, d *sql.DB) (bool, error)

Enabled implements kernel.OptionalModule. The store is on unless the operator turned it off (addons.commerce in the config, or the addons.commerce.enabled setting). Disabled, it registers no routes, jobs, theme kinds, admin pages or hooks; products, orders and entitlements stay in the database for when it is enabled again.

func (*Module) Migrations

func (*Module) Migrations() fs.FS

Migrations implements kernel.Module.

func (*Module) Name

func (*Module) Name() string

Name implements kernel.Module.

func (*Module) Register

func (m *Module) Register(d *kernel.Deps) error

Register publishes kernel.ServiceCommerce and wires jobs, theme kinds and funcs, public routes, the Stripe webhook, admin screens, API operations and health checks.

type Order

type Order struct {
	ReceiptStatus       string         `json:"receipt_status,omitempty"`
	ReceiptError        string         `json:"receipt_error,omitempty"`
	ReceiptSentAt       string         `json:"receipt_sent_at,omitempty"`
	RequestStatus       string         `json:"request_status"`
	FulfillmentNote     string         `json:"fulfillment_note"`
	ID                  string         `json:"id"`
	Number              string         `json:"number"`
	CustomerID          string         `json:"customer_id"`
	Email               string         `json:"email"`
	Status              string         `json:"status"`
	Currency            string         `json:"currency"`
	TotalMinor          int64          `json:"total_minor"`
	ProductID           string         `json:"product_id"`
	ProductName         string         `json:"product_name"`
	ProductSnapshot     map[string]any `json:"product_snapshot"`
	StripeSessionID     string         `json:"stripe_session_id"`
	StripePaymentIntent string         `json:"stripe_payment_intent"`
	StripeMode          string         `` /* 136-byte string literal not displayed */
	PaidAt              string         `json:"paid_at"`
	CreatedAt           string         `json:"created_at"`
	UpdatedAt           string         `json:"updated_at"`
}

Order freezes a single Stripe product or the line items of a manual request.

type OrderItem

type OrderItem struct {
	Intake       map[string]string `json:"intake,omitempty"`
	IntakeSchema []IntakeField     `json:"intake_schema,omitempty"`
	ID           string            `json:"id"`
	ProductID    string            `json:"product_id"`
	Quantity     int               `json:"quantity"`
	UnitMinor    int64             `json:"unit_minor"`
	ProductName  string            `json:"product_name"`
	Kind         string            `json:"kind"`
	DownloadURL  string            `json:"download_url,omitempty"`
}

type Plan

type Plan struct {
	Code   string `json:"code"`
	Name   string `json:"name"`
	NameAr string `json:"name_ar,omitempty"`
}

Plan is one plan advertised by a billing server's GET /catalog.

type Product

type Product struct {
	Intake           []IntakeField            `json:"intake,omitempty"`
	Kind             string                   `json:"kind"`
	DigitalFile      string                   `json:"digital_file,omitempty"`
	DownloadDays     int                      `json:"download_days"`
	BillingMode      string                   `json:"billing_mode,omitempty"`
	StripePriceID    string                   `json:"stripe_price_id,omitempty"`
	Interval         string                   `json:"interval,omitempty"`
	TrialDays        int                      `json:"trial_days,omitempty"`
	Prices           map[string]CurrencyPrice `json:"prices,omitempty"`
	ID               string                   `json:"id"`
	Slug             string                   `json:"slug"`
	Name             string                   `json:"name"`
	Description      string                   `json:"description"`
	ShortDescription string                   `json:"short_description"`
	PriceMinor       int64                    `json:"price_minor"`
	Currency         string                   `json:"currency"`
	Active           bool                     `json:"active"`
	GroupSlug        string                   `json:"group_slug"`
	GroupName        string                   `json:"group_name"`
	PlanCode         string                   `json:"plan_code"`
	ServerID         string                   `json:"server_id"`
	AccessMonths     int                      `json:"access_months"`
	Features         []string                 `json:"features"`
	ImageMediaID     string                   `json:"image_media_id"`
	Sort             int                      `json:"sort"`
	SEO              map[string]any           `json:"seo"`
	WPProductID      string                   `json:"wp_product_id"`
}

type ProductGroup

type ProductGroup struct {
	Slug, Name string
	Products   []Product
}

ProductGroup is a shop section: active products sharing GroupSlug.

type RequestDetail

type RequestDetail struct {
	Order *Order      `json:"order"`
	Items []OrderItem `json:"items"`
}

type RequestUpdate

type RequestUpdate struct {
	Status string `json:"status"`
	Note   string `json:"note"`
}

type Server

type Server struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	BaseURL     string `json:"base_url"`
	ClientID    string `json:"client_id"`
	Secret      string `json:"secret,omitempty"`
	SecretSet   bool   `json:"secret_set"`
	Protocol    string `json:"protocol"`
	Status      string `json:"status"`
	LastCheckAt string `json:"last_check_at"`
	LastError   string `json:"last_error"`
}

Server is a billing server speaking the mcp-billing/v1 protocol. Secret is write-only: SaveServer encrypts it when non-empty (empty keeps the stored secret) and reads never return it; SecretSet reports whether one exists.

type ServerTestResponse

type ServerTestResponse struct {
	OK      bool    `json:"ok"`
	Error   string  `json:"error,omitempty"`
	Catalog Catalog `json:"catalog"`
}

ServerTestResponse is returned by POST /v1/commerce/servers/{id}/test.

type Service

type Service interface {
	Products(ctx context.Context, activeOnly bool) ([]Product, error)
	Product(ctx context.Context, slug string) (*Product, error)
	SaveProduct(ctx context.Context, p Product) (*Product, error)
	DeleteProduct(ctx context.Context, id string) error

	UpsertFromWP(ctx context.Context, wp WPProduct) (*Product, error)
	// ReplaceMediaRef re-points every product image from media oldID to
	// newID (the migrator replaced a re-imported file).
	ReplaceMediaRef(ctx context.Context, oldID, newID string) error

	SubmitCart(ctx context.Context, request CartCheckout) (*Order, error)
	UpdateRequest(ctx context.Context, id string, request RequestUpdate) (*Order, error)
	StartCheckout(ctx context.Context, productID, email string) (redirectURL string, orderID string, err error)
	HandleStripeEvent(ctx context.Context, payload []byte, sigHeader string) error

	Orders(ctx context.Context, q ListQuery) ([]Order, int, error)
	Entitlements(ctx context.Context, q ListQuery) ([]Entitlement, int, error)
	CustomerEntitlements(ctx context.Context, userID string) ([]Entitlement, error)

	Suspend(ctx context.Context, entitlementID string) error
	Resume(ctx context.Context, entitlementID string) error
	Reconcile(ctx context.Context, entitlementID string) error
	LoginLink(ctx context.Context, entitlementID string) (string, error)
	// RequirePlan allows the call when accountID has an on or in-grace
	// subscription for plan (empty plan: any such subscription).
	RequirePlan(ctx context.Context, accountID, plan string) error

	Servers(ctx context.Context) ([]Server, error)
	SaveServer(ctx context.Context, s Server) error
	TestServer(ctx context.Context, id string) (Catalog, error)
}

Service is the commerce API published as kernel.ServiceCommerce.

type SubscriptionEvent added in v0.12.0

type SubscriptionEvent struct {
	SubscriptionID       string `json:"subscription_id"`
	StripeSubscriptionID string `json:"stripe_subscription_id"`
	CustomerID           string `json:"customer_id"`
	AccountID            string `json:"account_id"`
	Plan                 string `json:"plan"`
	SiteID               string `json:"site_id"`
	Status               string `json:"status"`
	PriceID              string `json:"price_id"`
	CurrentPeriodEnd     string `json:"current_period_end"`
	GraceUntil           string `json:"grace_until,omitempty"`
	Reason               string `json:"reason,omitempty"`
}

SubscriptionEvent is emitted on subscription.activated, subscription.past_due and subscription.canceled so other modules and webhook subscribers can react without talking to Stripe.

type ValidationError

type ValidationError struct{ Fields map[string]string }

ValidationError carries per-field i18n keys (implements api.FieldErrorer).

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) FieldErrors

func (e *ValidationError) FieldErrors() map[string]string

FieldErrors implements api.FieldErrorer.

type WPProduct

type WPProduct struct {
	ID                                        int
	Slug, Name, Description, ShortDescription string
	PriceMinor                                int64
	Currency                                  string
	Categories                                []string
	ImageURLs                                 []string
	Meta                                      map[string]string
}

WPProduct is a WooCommerce catalog product as read by the migrator (its adapter maps an identical struct 1:1). Meta holds WooCommerce meta_data (e.g. _mcbb_server_id, _mcbb_plan_code, _mcbb_months).

Directories

Path Synopsis
Package fakebilling is an in-memory billing server implementing the mcp-billing/v1 protocol (see docs/COMMERCE.md) for tests and local e2e runs: signature verification (same scheme as the real servers), Idempotency-Key replay on mutations, and failure injection.
Package fakebilling is an in-memory billing server implementing the mcp-billing/v1 protocol (see docs/COMMERCE.md) for tests and local e2e runs: signature verification (same scheme as the real servers), Idempotency-Key replay on mutations, and failure injection.

Jump to

Keyboard shortcuts

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