stripeflow

package module
v0.1.4 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 20 Imported by: 0

README

StripeFlow

StripeFlow is a pluggable Go library that focuses exclusively on integrating the Stripe Customer Portal into any Go application. It handles billing portal generation, webhook processing, subscription state, product/price catalogue syncing, and built-in usage tracking — with support for PostgreSQL, MySQL, and SQLite.

Features

  • Programmatic PortalCreatePortalSession() returns a URL; you control the redirect
  • Webhook processing — handles the full subscription lifecycle with idempotency guarantees
  • Product & Price sync — sync Stripe catalogue to a local database on startup or via cron
  • Subscription middleware — protect routes requiring an active or trialing subscription
  • Built-in usage tracking — per-user usage_count / usage_limit with atomic increment
  • Typed error sentinelsErrNoSubscription, ErrTrialExpired, ErrUsageLimitReached, etc.
  • Multi-dialect — works with PostgreSQL, MySQL, and SQLite
  • Zero framework dependency — uses only net/http, log/slog, and database/sql

Installation

go get github.com/josuebrunel/stripeflow

Quick Start

1. Run Migrations

StripeFlow uses embedded Goose migrations to create and version all required tables.

import (
    "database/sql"
    _ "github.com/lib/pq"
    "github.com/josuebrunel/stripeflow/migrations"
)

db, _ := sql.Open("postgres", os.Getenv("DATABASE_URL"))

if err := migrations.MigrateUp(db, "postgres"); err != nil {
    log.Fatalf("migration failed: %v", err)
}

Supported dialect values: "postgres", "mysql", "sqlite".

2. Initialise the Client
import (
    "net/http"
    "github.com/josuebrunel/stripeflow"
)

sf, err := stripeflow.New(stripeflow.Config{
    Dialect:         stripeflow.Postgres,      // stripeflow.Postgres | stripeflow.MySQL | stripeflow.SQLite
    DB:              db,
    StripeSecretKey: os.Getenv("STRIPE_SECRET_KEY"),
    WebhookSecret:   os.Getenv("STRIPE_WEBHOOK_SECRET"),
    TrialDays:       14,                       // global trial default (0 = no trial)
    UsageLimitEnabled: true,                   // enforce usage_limit in middleware

    // Tell middleware how to identify the current user.
    GetUserID: func(r *http.Request) (string, error) {
        return sessionUserID(r), nil           // parse JWT, cookie, etc.
    },

    // Optional: called after every successfully processed webhook event.
    OnEvent: func(event *stripe.Event) {
        log.Printf("stripe event: %s", event.Type)
    },
})
if err != nil {
    log.Fatal(err)
}
3. Sync Products & Register Webhook
ctx := context.Background()

// Sync Stripe product catalogue to the local database.
result, err := sf.SyncProducts(ctx)
// result.ProductsUpserted, result.PricesUpserted

mux := http.NewServeMux()

// Webhook endpoint (mount at the URL configured in your Stripe dashboard).
mux.Handle("POST /stripe/webhook", sf.WebhookHandler())

log.Fatal(http.ListenAndServe(":8080", mux))

Checkout & Billing Portal

Both APIs are programmatic — they return a URL and you redirect the user.

Open a Checkout Session
url, err := sf.CreateCheckoutSession(ctx, stripeflow.CheckoutParams{
    UserID:     currentUserID,
    PriceID:    "price_XYZ123",
    SuccessURL: "https://myapp.com/success",
    CancelURL:  "https://myapp.com/cancel",
})
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}
http.Redirect(w, r, url, http.StatusSeeOther)
Open the Billing Portal
url, err := sf.CreatePortalSession(ctx, stripeflow.PortalParams{
    UserID:    currentUserID,
    ReturnURL: "https://myapp.com/account",
})
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}
http.Redirect(w, r, url, http.StatusSeeOther)

Middleware

Middleware injects the resolved *Subscription into the request context and is accessible via SubscriptionFromContext.

Protect Routes
// Allow active subscribers AND users in a valid trial.
mux.Handle("/app/", sf.RequireActiveOrTrial(appHandler))

// Require a fully paid subscription (no trials).
mux.Handle("/api/premium", sf.RequireActiveSubscription(premiumHandler))

// Full control via MiddlewareOptions.
mux.Handle("/api/", sf.RequireSubscription(apiHandler, stripeflow.MiddlewareOptions{
    AllowTrialing:   false,
    CheckUsageLimit: true,  // deny when usage_count >= usage_limit
    OnDenied: func(w http.ResponseWriter, r *http.Request, reason error) {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusPaymentRequired)
        json.NewEncoder(w).Encode(map[string]string{
            "error":       "upgrade_required",
            "upgrade_url": "https://myapp.com/pricing",
        })
    },
}))
Read Subscription in Handlers
func appHandler(w http.ResponseWriter, r *http.Request) {
    sub, ok := stripeflow.SubscriptionFromContext(r.Context())
    if ok {
        fmt.Fprintf(w, "plan: %s, usage: %d/%v", sub.StripePriceID, sub.UsageCount, sub.UsageLimit)
    }
}
Default Denial Responses

When OnDenied is not set, the middleware returns structured JSON with HTTP status codes mapped to sentinel errors:

Error HTTP Status JSON error key
ErrNoSubscription 402 no_subscription
ErrTrialExpired 402 trial_expired
ErrSubscriptionInactive 402 subscription_inactive
ErrUsageLimitReached 429 usage_limit_reached

Usage Tracking

stripeflow stores a usage_count and optional usage_limit directly on the subscription row.

// Increment usage after a successful operation.
newCount, err := sf.IncrementUsage(ctx, userID, 1)

// Set a cap (nil = unlimited).
err = sf.SetUsageLimit(ctx, userID, stripeflow.Int64Ptr(1000))

// Reset at the start of a billing period (e.g. via OnEvent hook).
err = sf.ResetUsage(ctx, userID)

Products & Prices

Sync from Stripe
result, err := sf.SyncProducts(ctx)
// Fetches all products + prices from Stripe and upserts them locally.
List Locally Cached Catalogue
products, err := sf.ListProducts(ctx, true /* activeOnly */)
prices, err   := sf.ListPrices(ctx, "prod_ABC123")
Create Programmatically
product, err := sf.CreateProduct(ctx, stripeflow.CreateProductParams{
    Name:        "Pro Plan",
    Description: "All features, unlimited usage",
})

price, err := sf.CreatePrice(ctx, stripeflow.CreatePriceParams{
    StripeProductID: product.ID,
    UnitAmount:      1999, // $19.99
    Currency:        "usd",
    Recurring: &stripeflow.RecurringParams{
        Interval:      stripeflow.IntervalMonth,
        IntervalCount: 1,
    },
})
Provision Products from JSON

Use ProvisionProduct or ProvisionProductsFromJSON to create your products and all their prices in a single call. This is ideal for CLI tools, seed scripts, or any workflow where you define your catalogue as JSON.

Programmatic usage:

result, err := sf.ProvisionProduct(ctx, stripeflow.ProvisionParams{
    Product: stripeflow.ProvisionProductParams{
        Name:        "My SaaS",
        Description: "AI-powered analytics platform",
        MarketingFeatures: []stripeflow.ProvisionFeature{
            {Name: "Real-time dashboards"},
            {Name: "Unlimited team members"},
        },
        Metadata: map[string]string{"category": "analytics"},
    },
    Prices: []stripeflow.ProvisionPriceParams{
        {
            Nickname:      "Starter — monthly",
            Currency:       "usd",
            BillingScheme: "per_unit",
            UnitAmount:    2990,
            Recurring:     &stripeflow.ProvisionRecurringParams{Interval: "month"},
        },
        {
            Nickname:      "Starter — annual (20% off)",
            Currency:       "usd",
            BillingScheme: "per_unit",
            UnitAmount:    28704,
            Recurring:     &stripeflow.ProvisionRecurringParams{Interval: "year"},
        },
    },
})
// result.ProductID  → "prod_ABC123"
// result.Prices[0].PriceID → "price_XYZ001"

From a JSON file (e.g. in a CLI tool):

raw, err := os.ReadFile("products.json")
if err != nil {
    log.Fatal(err)
}
results, err := sf.ProvisionProductsFromJSON(ctx, raw)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Created %d products\n", len(results))

Example products.json:

[
  {
    "product": {
      "name": "My SaaS",
      "description": "AI-powered analytics platform",
      "metadata": { "category": "analytics" },
      "marketing_features": [
        { "name": "Real-time dashboards" },
        { "name": "Unlimited team members" }
      ]
    },
    "prices": [
      {
        "nickname": "Starter — monthly",
        "currency": "usd",
        "billing_scheme": "per_unit",
        "unit_amount": 2990,
        "recurring": {
          "interval": "month",
          "usage_type": "licensed"
        }
      },
      {
        "nickname": "Metered API calls",
        "currency": "usd",
        "billing_scheme": "per_unit",
        "unit_amount": 1,
        "recurring": {
          "interval": "month",
          "usage_type": "metered",
          "meter_event_name": "api_calls"
        },
        "transform_quantity": {
          "divide_by": 1000,
          "round": "up"
        }
      }
    ]
  }
]

Webhooks

Mount WebhookHandler() and configure the same URL in your Stripe dashboard.

mux.Handle("POST /stripe/webhook", sf.WebhookHandler())

Handled events:

Event Action
customer.subscription.created/updated Updates subscription status & period
customer.subscription.deleted Marks subscription as canceled
customer.subscription.trial_will_end Informational — fire via OnEvent for emails
invoice.payment_succeeded Marks subscription active, updates period
invoice.payment_failed Marks subscription past_due
product.created/updated/deleted Upserts local product
price.created/updated/deleted Upserts local price

All events are idempotent — duplicate deliveries are safely ignored via the stripeflow_webhook_events table.

Use OnEvent for side-effects like cache invalidation or sending emails:

stripeflow.Config{
    OnEvent: func(event *stripe.Event) {
        if event.Type == "customer.subscription.trial_will_end" {
            sendTrialEndingEmail(event)
        }
    },
}

API Reference

Client Methods
Method Description
CreateCheckoutSession(ctx, CheckoutParams) (string, error) Create a Stripe Checkout session
CreatePortalSession(ctx, PortalParams) (string, error) Create a Stripe Billing Portal session
WebhookHandler() http.Handler Verified webhook event handler
Handler() http.Handler Thin convenience mux (checkout + portal + webhook)
RequireSubscription(next, ...opts) http.Handler Subscription-required middleware
RequireActiveOrTrial(next) http.Handler Allows active + trialing users
RequireActiveSubscription(next) http.Handler Paid subscription only (no trials)
GetSubscription(ctx, userID) (*Subscription, error) Fetch subscription state
GetSubscriptionByID(ctx, id) Fetch subscription by ID
GetSubscriptionByCustomerID(ctx, customerID) Fetch subscription by Stripe customer ID
GetSubscriptionByStripeSubID(ctx, subID) Fetch subscription by Stripe subscription ID
GetProductByID(ctx, id) Fetch product by ID
IncrementUsage(ctx, userID, delta) (int64, error) Atomically increment usage counter
SetUsageLimit(ctx, userID, *int64) error Set or remove usage cap
ResetUsage(ctx, userID) error Zero usage counter
SyncProducts(ctx) (*SyncResult, error) Pull Stripe catalogue → local DB
ListProducts(ctx, activeOnly) ([]Product, error) List local products
ListPrices(ctx, productID) ([]Price, error) List local prices for a product
CreateProduct(ctx, CreateProductParams) (*Product, error) Create product in Stripe + local
UpdateProduct(ctx, UpdateProductParams) (*Product, error) Update product in Stripe + local
CreatePrice(ctx, CreatePriceParams) (*Price, error) Create price in Stripe + local
ArchivePrice(ctx, priceID) error Archive price in Stripe
DeleteProduct(ctx, productID) error Delete a product and archive its prices in Stripe, and remove locally
DeleteAllProducts(ctx) error Delete all products and prices, removing them locally and from Stripe
ProvisionProduct(ctx, ProvisionParams) (*ProvisionResult, error) Create product + all prices in one call
ProvisionProductsFromJSON(ctx, []byte) ([]ProvisionResult, error) Provision array of products from JSON
Helpers
stripeflow.SubscriptionFromContext(ctx) (*Subscription, bool)
stripeflow.Int64Ptr(v int64) *int64

Database Tables

Table Purpose
stripeflow_products Stripe products synced locally
stripeflow_prices Stripe prices synced locally
stripeflow_subscriptions One row per user — subscription state + usage
stripeflow_webhook_events Idempotency log of processed Stripe events

Running Tests

# Unit tests (SQLite in-memory, no external services)
go test -v ./...

# Integration tests (requires Docker)
go test -v -run TestPostgresAndMySQLIntegration ./...

CLI Tool

StripeFlow comes with a built-in CLI tool to help you manage your database migrations, products, and syncing.

Installation
go run github.com/josuebrunel/stripeflow/cmd/stripeflow@latest
Configuration

The CLI uses the following environment variables. Note that the variables are prefixed with STRIPEFLOW_.

export STRIPEFLOW_DATABASE_URL="postgres://user:pass@localhost:5432/dbname?sslmode=disable"
export STRIPEFLOW_STRIPE_SECRET_KEY="sk_test_..."
export STRIPEFLOW_WEBHOOK_SECRET="whsec_..."

(Note: SQLite (sqlite://...) and MySQL (mysql://...) URLs are also supported).

Usage

Run Database Migrations:

stripeflow -migrate=up
stripeflow -migrate=down

Sync Products from Stripe:

Fetches all products and their prices from your Stripe account and upserts them locally.

stripeflow -sync

Provision Products from JSON:

Creates one or more products and their prices in Stripe, and syncs them to your local database.

stripeflow -provision=products.json

Delete a Product or All Products:

stripeflow -delete="prod_12345" # Deletes a single product and archives its prices
stripeflow -delete="all"        # Deletes all products and prices locally and from Stripe

Documentation

Overview

Package stripeflow provides a pluggable Go library for integrating Stripe subscriptions into your application. It focuses on billing portal access, webhook processing, subscription state management, and product catalogue syncing — with support for PostgreSQL, MySQL, and SQLite.

Quick start:

sf, err := stripeflow.New(stripeflow.Config{
    Dialect:         stripeflow.Postgres,
    DB:              db,
    StripeSecretKey: "sk_live_...",
    WebhookSecret:   "whsec_...",
    GetUserID: func(r *http.Request) (string, error) {
        return sessionUserID(r), nil
    },
})

// Register webhook handler
http.Handle("/stripe/webhook", sf.WebhookHandler())

// Protect routes
http.Handle("/app/", sf.RequireActiveOrTrial(appHandler))

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoSubscription       = errors.New("stripeflow: no subscription found")
	ErrSubscriptionInactive = errors.New("stripeflow: subscription is not active")
	ErrUsageLimitReached    = errors.New("stripeflow: usage limit reached")
	ErrTrialExpired         = errors.New("stripeflow: trial has expired")
)

Sentinel errors returned by middleware and programmatic helpers.

Functions

func Int64Ptr

func Int64Ptr(v int64) *int64

Int64Ptr is a convenience helper that returns a pointer to an int64 value.

Types

type CheckoutParams

type CheckoutParams struct {
	UserID     string
	PriceID    string
	SuccessURL string
	CancelURL  string
	TrialDays  *int64
	Metadata   map[string]string
}

CheckoutParams holds options for creating a Stripe Checkout session.

type Client

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

Client is the main stripeflow object. Create one via New().

func New

func New(cfg Config) (*Client, error)

New creates and initialises a stripeflow Client. The Stripe API key is set globally at initialisation time.

func (*Client) ArchivePrice

func (c *Client) ArchivePrice(ctx context.Context, priceID string) error

ArchivePrice marks a price as inactive in Stripe (prices cannot be deleted).

func (*Client) CreateCheckoutSession added in v0.0.11

func (c *Client) CreateCheckoutSession(ctx context.Context, p CheckoutParams) (string, error)

CreateCheckoutSession creates a Stripe Checkout session to subscribe a user to a specific price. Returns the checkout URL to redirect the user to.

func (*Client) CreatePortalSession

func (c *Client) CreatePortalSession(ctx context.Context, p PortalParams) (string, error)

CreatePortalSession creates a Stripe Billing Portal session so the user can manage their subscription, update payment methods, and download invoices. Returns the portal URL to redirect the user to.

func (*Client) CreatePrice

func (c *Client) CreatePrice(ctx context.Context, p CreatePriceParams) (*Price, error)

CreatePrice creates a price in Stripe and stores it locally. Calling it again for the same product with the same currency/amount/interval reuses the existing Stripe price instead of creating a duplicate.

func (*Client) CreateProduct

func (c *Client) CreateProduct(ctx context.Context, p CreateProductParams) (*Product, error)

CreateProduct creates a product in Stripe and stores it locally. Calling it again with the same Name reuses the existing Stripe product instead of creating a duplicate.

func (*Client) DeleteAllProducts added in v0.0.7

func (c *Client) DeleteAllProducts(ctx context.Context) error

DeleteAllProducts deletes all products and prices from the local database and attempts to archive them in Stripe.

func (*Client) DeleteProduct added in v0.0.7

func (c *Client) DeleteProduct(ctx context.Context, productID string) error

DeleteProduct deletes a product and all of its associated prices from the local database. In Stripe, prices are archived, and the product itself is archived (made inactive) because Stripe does not allow deleting products that have ever had prices.

func (*Client) GetProductByID added in v0.0.6

func (c *Client) GetProductByID(ctx context.Context, id string) (*Product, error)

GetProductByID retrieves a product by its ID.

func (*Client) GetSubscription

func (c *Client) GetSubscription(ctx context.Context, userID string) (*Subscription, error)

GetSubscription retrieves the current subscription state for a user.

func (*Client) GetSubscriptionByCustomerID added in v0.0.6

func (c *Client) GetSubscriptionByCustomerID(ctx context.Context, customerID string) (*Subscription, error)

GetSubscriptionByCustomerID retrieves a subscription by Stripe Customer ID.

func (*Client) GetSubscriptionByID added in v0.0.6

func (c *Client) GetSubscriptionByID(ctx context.Context, id int64) (*Subscription, error)

GetSubscriptionByID retrieves a subscription by its primary key ID.

func (*Client) GetSubscriptionByStripeSubID added in v0.0.6

func (c *Client) GetSubscriptionByStripeSubID(ctx context.Context, subID string) (*Subscription, error)

GetSubscriptionByStripeSubID retrieves a subscription by Stripe Subscription ID.

func (*Client) Handler

func (c *Client) Handler() http.Handler

Handler returns an http.Handler that mounts the checkout, portal and webhook routes. For full control over routing, call CreateCheckoutSession, CreatePortalSession and WebhookHandler directly instead.

POST /checkout  — creates a Checkout session, redirects to Stripe
GET  /portal    — creates a Billing Portal session, redirects to Stripe
POST /webhook   — receives and processes Stripe webhook events

func (*Client) IncrementUsage

func (c *Client) IncrementUsage(ctx context.Context, userID string, delta int64) (int64, error)

IncrementUsage adds delta to the user's usage counter and returns the new total. Typically called after a successful API operation.

newCount, err := sf.IncrementUsage(ctx, userID, 1)

func (*Client) ListPrices

func (c *Client) ListPrices(ctx context.Context, productID string) ([]Price, error)

ListPrices returns locally cached prices for a product.

func (*Client) ListProducts

func (c *Client) ListProducts(ctx context.Context, activeOnly bool) ([]Product, error)

ListProducts returns locally cached products.

func (*Client) ProvisionProduct added in v0.0.3

func (c *Client) ProvisionProduct(ctx context.Context, params ProvisionParams) (*ProvisionResult, error)

ProvisionProduct creates a product and all its associated prices in Stripe, syncing each resource to the local database. The operation is sequential: the product is created first, then each price is created in order.

If any price creation fails, the product and any previously created prices will remain in Stripe — check your Stripe dashboard to clean up.

func (*Client) ProvisionProductsFromJSON added in v0.0.10

func (c *Client) ProvisionProductsFromJSON(ctx context.Context, data []byte) ([]ProvisionResult, error)

ProvisionProductsFromJSON is a convenience wrapper that unmarshals a JSON array of ProvisionParams and calls ProvisionProduct for each.

raw, _ := os.ReadFile("products.json")
results, err := client.ProvisionProductsFromJSON(ctx, raw)

func (*Client) ReportMeterEvent added in v0.0.5

func (c *Client) ReportMeterEvent(ctx context.Context, stripeCustomerID string, eventName string, value int64) error

ReportMeterEvent pushes a high-throughput usage event to Stripe's Billing v2 engine. The eventName must match the EventName of a Stripe Meter.

err := sf.ReportMeterEvent(ctx, stripeCustomerID, "api_check", 1)

func (*Client) RequireActiveOrTrial

func (c *Client) RequireActiveOrTrial(next http.Handler) http.Handler

RequireActiveOrTrial allows users who are actively subscribed OR in a valid trial.

func (*Client) RequireActiveSubscription

func (c *Client) RequireActiveSubscription(next http.Handler) http.Handler

RequireActiveSubscription requires a fully paid (non-trial) active subscription.

func (*Client) RequireSubscription

func (c *Client) RequireSubscription(next http.Handler, opts ...MiddlewareOptions) http.Handler

RequireSubscription is an http.Handler middleware that rejects requests from users without an active subscription (or valid trial, depending on options).

The resolved *Subscription is stored in the context and accessible via SubscriptionFromContext. Config.GetUserID must be set.

mux.Handle("/app/", sf.RequireSubscription(appHandler))
mux.Handle("/api/", sf.RequireSubscription(apiHandler, stripeflow.MiddlewareOptions{
    AllowTrialing: false,
    CheckUsageLimit: true,
}))

func (*Client) ResetUsage

func (c *Client) ResetUsage(ctx context.Context, userID string) error

ResetUsage zeroes the usage counter for a user. Typically called at the start of each billing period.

func (*Client) SetUsageLimit

func (c *Client) SetUsageLimit(ctx context.Context, userID string, limit *int64) error

SetUsageLimit sets or removes the usage cap for a user. Pass nil to remove the limit (unlimited).

err := sf.SetUsageLimit(ctx, userID, stripeflow.Int64Ptr(1000))

func (*Client) SyncProducts

func (c *Client) SyncProducts(ctx context.Context) (*SyncResult, error)

SyncProducts fetches all products and their prices from Stripe and upserts them into the local database. Call this on startup or via a cron job.

func (*Client) UpdateProduct

func (c *Client) UpdateProduct(ctx context.Context, p UpdateProductParams) (*Product, error)

UpdateProduct updates a product in Stripe and refreshes the local copy.

func (*Client) WebhookHandler

func (c *Client) WebhookHandler() http.Handler

WebhookHandler returns an http.Handler that verifies and processes Stripe webhook events. Mount it at the endpoint configured in the Stripe dashboard.

http.Handle("/stripe/webhook", sf.WebhookHandler())

type Config

type Config struct {
	// Dialect specifies the SQL dialect (Postgres, MySQL, SQLite).
	Dialect Dialect

	// DB is the *sql.DB connection to use. stripeflow manages its own tables
	// under the "stripeflow_" namespace.
	DB *sql.DB

	// StripeSecretKey is your Stripe secret API key (sk_live_... or sk_test_...).
	StripeSecretKey string

	// WebhookSecret is the signing secret for your Stripe webhook endpoint (whsec_...).
	WebhookSecret string

	// GetUserID extracts the authenticated user's identifier from an HTTP request.
	// Required when using any middleware. Typically reads a JWT or session cookie.
	GetUserID func(r *http.Request) (string, error)

	// OnEvent is an optional hook called after every successfully processed webhook
	// event. Useful for cache invalidation, audit logging, etc.
	OnEvent func(event *stripe.Event)

	// TrialDays sets the default number of free trial days for new subscriptions.
	// Can be overridden per-checkout via CheckoutParams.TrialDays. Zero = no trial.
	TrialDays int64

	// UsageLimitEnabled toggles the built-in usage-limit check globally.
	// When true, middleware will deny requests once usage_count >= usage_limit.
	UsageLimitEnabled bool
}

Config holds all configuration needed to initialise a StripeFlow client.

type CreatePriceParams

type CreatePriceParams struct {
	// StripeProductID is the parent product (prod_...).
	StripeProductID string
	// UnitAmount is in the smallest currency unit (e.g. cents for USD).
	UnitAmount int64
	// Currency is a 3-letter ISO code, e.g. "usd".
	Currency string
	// Recurring – if nil, a one-time price is created.
	Recurring *RecurringParams
	Metadata  map[string]string
}

CreatePriceParams defines a new recurring or one-time price.

type CreateProductParams

type CreateProductParams struct {
	Name        string
	Description string
	// Images are URLs to product images.
	Images   []string
	Metadata map[string]string
}

CreateProductParams defines a new product to create in Stripe (and sync locally).

type DeniedFunc

type DeniedFunc func(w http.ResponseWriter, r *http.Request, reason error)

DeniedFunc is called by middleware when access is denied. It should write an appropriate HTTP response and return. If nil in MiddlewareOptions, a default JSON response is used.

type Dialect added in v0.0.5

type Dialect string

Dialect specifies the SQL dialect.

const (
	Postgres Dialect = "postgres"
	MySQL    Dialect = "mysql"
	SQLite   Dialect = "sqlite"
)

type MiddlewareOptions

type MiddlewareOptions struct {
	// OnDenied overrides the default HTTP response when access is denied.
	// If nil, a default JSON error response is used.
	OnDenied DeniedFunc

	// AllowTrialing permits requests from users in a valid (non-expired) trial.
	// Defaults to true when using RequireActiveOrTrial.
	AllowTrialing bool

	// CheckUsageLimit enables the usage-limit check for this specific route,
	// regardless of the global Config.UsageLimitEnabled setting.
	CheckUsageLimit bool
}

MiddlewareOptions customises the behaviour of subscription middleware.

type PortalParams

type PortalParams struct {
	// UserID is your internal user identifier.
	UserID string
	// ReturnURL is where the customer lands after leaving the portal.
	ReturnURL string
}

PortalParams holds options for creating a Billing Portal session.

type Price

type Price struct {
	ID                string
	ProductID         string
	Currency          string
	UnitAmount        *int64
	RecurringInterval string
	RecurringCount    *int
	// UsageType is "licensed" for flat-rate subscription prices and "metered"
	// for per-unit prices billed via meter events. Empty for one-time prices.
	UsageType string
	// Type is "recurring" or "one_time".
	Type string
	// Nickname is an optional human-readable label set in Stripe (e.g. "Starter — monthly").
	Nickname string
	// LookupKey is an optional stable string key assigned in Stripe that lets
	// you reference this price without hardcoding its ID.
	LookupKey       string
	Active          bool
	Metadata        *json.RawMessage
	StripeCreatedAt *time.Time
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

Price mirrors a Stripe price stored locally.

func (*Price) IsMetered added in v0.0.12

func (p *Price) IsMetered() bool

IsMetered reports whether this is a metered/per-unit price (as opposed to a flat-rate licensed subscription price).

func (*Price) IsRecurring added in v0.0.12

func (p *Price) IsRecurring() bool

IsRecurring reports whether this is a recurring (subscription) price as opposed to a one-time price.

type PriceInterval

type PriceInterval string

PriceInterval represents billing recurrence.

const (
	IntervalDay   PriceInterval = "day"
	IntervalWeek  PriceInterval = "week"
	IntervalMonth PriceInterval = "month"
	IntervalYear  PriceInterval = "year"
)

type Product

type Product struct {
	ID              string
	Name            string
	Description     string
	Active          bool
	Metadata        *json.RawMessage
	Features        *json.RawMessage
	StripeCreatedAt *time.Time
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

Product mirrors a Stripe product stored locally.

type ProvisionFeature added in v0.0.3

type ProvisionFeature struct {
	Name string `json:"name"`
}

ProvisionFeature is a marketing feature displayed on Stripe-hosted surfaces.

type ProvisionParams added in v0.0.3

type ProvisionParams struct {
	Product ProvisionProductParams `json:"product"`
	Prices  []ProvisionPriceParams `json:"prices"`
}

ProvisionParams describes a complete product with all its prices to create in Stripe in a single call. This is designed for use cases like a CLI that reads a JSON file and provisions an entire product catalogue at once.

Example usage with JSON:

raw, _ := os.ReadFile("product.json")
result, err := client.ProvisionProductFromJSON(ctx, raw)

Example usage programmatically:

result, err := client.ProvisionProduct(ctx, stripeflow.ProvisionParams{
    Product: stripeflow.ProvisionProductParams{
        Name:        "My SaaS",
        Description: "A great product",
    },
    Prices: []stripeflow.ProvisionPriceParams{
        {
            Nickname:   "Monthly",
            Currency:   "usd",
            UnitAmount: 2999,
            Recurring:  &stripeflow.ProvisionRecurringParams{Interval: "month"},
        },
    },
})

type ProvisionPriceInfo added in v0.0.3

type ProvisionPriceInfo struct {
	PriceID  string `json:"price_id"`
	Nickname string `json:"nickname,omitempty"`
}

ProvisionPriceInfo describes a single price created during provisioning.

type ProvisionPriceParams added in v0.0.3

type ProvisionPriceParams struct {
	// Nickname is a human-readable label for the price (e.g. "Growth — monthly").
	Nickname string `json:"nickname,omitempty"`
	// Currency is a 3-letter ISO 4217 code, e.g. "usd" (required).
	Currency string `json:"currency"`
	// BillingScheme is "per_unit" (default) or "tiered".
	BillingScheme string `json:"billing_scheme,omitempty"`
	// UnitAmount is the price in the smallest currency unit (e.g. cents).
	UnitAmount int64 `json:"unit_amount"`
	// Recurring configures billing recurrence. Nil for one-time prices.
	Recurring *ProvisionRecurringParams `json:"recurring,omitempty"`
	// TransformQuantity configures billing per N units (e.g. per 1000 API calls).
	TransformQuantity *ProvisionTransformQtyParams `json:"transform_quantity,omitempty"`
	// Metadata is optional key-value metadata attached to the price.
	Metadata map[string]string `json:"metadata,omitempty"`
}

ProvisionPriceParams describes a price to create for the product.

type ProvisionProductParams added in v0.0.3

type ProvisionProductParams struct {
	// Name is the product name (required).
	Name string `json:"name"`
	// Description is an optional product description.
	Description string `json:"description,omitempty"`
	// Images are optional URLs to product images.
	Images []string `json:"images,omitempty"`
	// Metadata is optional key-value metadata attached to the product.
	Metadata map[string]string `json:"metadata,omitempty"`
	// MarketingFeatures lists feature bullet points shown on Stripe-hosted pages.
	MarketingFeatures []ProvisionFeature `json:"marketing_features,omitempty"`
}

ProvisionProductParams describes the product to create.

type ProvisionRecurringParams added in v0.0.3

type ProvisionRecurringParams struct {
	// Interval is "day", "week", "month", or "year" (required for recurring prices).
	Interval string `json:"interval"`
	// IntervalCount defaults to 1 (every interval).
	IntervalCount int64 `json:"interval_count,omitempty"`
	// UsageType is "licensed" (default) or "metered".
	UsageType string `json:"usage_type,omitempty"`
	// Meter is the ID of the meter tracking usage for metered prices (stripe-go v82+).
	// This replaces the legacy aggregate_usage field.
	Meter string `json:"meter,omitempty"`
	// AggregateUsage is accepted in JSON input for backward compatibility but
	// is no longer sent to Stripe in v82+. Use Meter instead for metered billing.
	AggregateUsage string `json:"aggregate_usage,omitempty"`
	// MeterEventName will auto-create a meter with this event name during provisioning.
	MeterEventName string `json:"meter_event_name,omitempty"`
	// MeterDisplayName is the display name for the auto-created meter.
	MeterDisplayName string `json:"meter_display_name,omitempty"`
}

ProvisionRecurringParams configures the billing cycle for a price.

type ProvisionResult added in v0.0.3

type ProvisionResult struct {
	ProductID string               `json:"product_id"`
	Prices    []ProvisionPriceInfo `json:"prices"`
}

ProvisionResult contains the IDs of all resources created by ProvisionProduct.

type ProvisionTransformQtyParams added in v0.0.3

type ProvisionTransformQtyParams struct {
	// DivideBy is the divisor (e.g. 1000 to bill per 1000 units).
	DivideBy int64 `json:"divide_by"`
	// Round is "up" or "down".
	Round string `json:"round"`
}

ProvisionTransformQtyParams configures billing per N units.

type RecurringParams

type RecurringParams struct {
	Interval      PriceInterval
	IntervalCount int64 // 1 = every interval, 3 = every 3 intervals, etc.
}

RecurringParams configures the billing cycle for a price.

type Subscription

type Subscription struct {
	ID                   int64
	UserID               string
	StripeCustomerID     string
	StripeSubscriptionID string
	StripePriceID        string
	StripeProductID      string
	Status               SubscriptionStatus
	TrialEndsAt          *time.Time
	CurrentPeriodStart   *time.Time
	CurrentPeriodEnd     *time.Time
	CanceledAt           *time.Time
	UsageCount           int64
	UsageLimit           *int64
	Metadata             *json.RawMessage
	CreatedAt            time.Time
	UpdatedAt            time.Time
}

Subscription represents a user's Stripe subscription state as stored locally.

func SubscriptionFromContext

func SubscriptionFromContext(ctx context.Context) (*Subscription, bool)

SubscriptionFromContext retrieves the Subscription stored in the request context by the RequireSubscription middleware.

func (*Subscription) IsActive

func (s *Subscription) IsActive() bool

IsActive reports whether the subscription is in an active or trialing state.

func (*Subscription) TrialExpired

func (s *Subscription) TrialExpired() bool

TrialExpired reports whether the user's trial period has ended.

func (*Subscription) UsageLimitReached

func (s *Subscription) UsageLimitReached() bool

UsageLimitReached reports whether the user has exhausted their usage allowance.

type SubscriptionStatus

type SubscriptionStatus string

SubscriptionStatus mirrors Stripe's subscription statuses plus internal sentinels.

const (
	StatusActive            SubscriptionStatus = "active"
	StatusTrialing          SubscriptionStatus = "trialing"
	StatusPastDue           SubscriptionStatus = "past_due"
	StatusCanceled          SubscriptionStatus = "canceled"
	StatusIncomplete        SubscriptionStatus = "incomplete"
	StatusIncompleteExpired SubscriptionStatus = "incomplete_expired"
	StatusUnpaid            SubscriptionStatus = "unpaid"
	StatusPaused            SubscriptionStatus = "paused"
	// StatusNone means no Stripe subscription exists yet for this user.
	StatusNone SubscriptionStatus = "none"
)

func (SubscriptionStatus) IsActive

func (s SubscriptionStatus) IsActive() bool

IsActive reports whether the status is billable / accessible.

type SyncResult

type SyncResult struct {
	ProductsUpserted int
	PricesUpserted   int
}

SyncResult summarises a full catalogue synchronisation.

type UpdateProductParams

type UpdateProductParams struct {
	// StripeProductID is the Stripe product ID (prod_...).
	StripeProductID string
	Name            *string
	Description     *string
	Active          *bool
	Metadata        map[string]string
}

UpdateProductParams describes editable fields on an existing product.

Directories

Path Synopsis
cmd
stripeflow command

Jump to

Keyboard shortcuts

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