payment

package
v0.0.0-...-ddef314 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: MIT Imports: 18 Imported by: 0

README

payment

Stripe integration for membership subscriptions and one-time donations.

Routes

  • POST /webhooks/stripe — receives customer.subscription.{created,updated,deleted} events. All other event types return 204 without processing.
  • GET /payment/checkout (authn) — redirects active members to the Stripe billing portal; redirects everyone else into a new subscription Checkout Session.
  • GET /donations/checkout?price_id=... (authn) — creates a one-time payment Checkout Session for a configured donation item.

Configuration

Stored in the stripe_config table (versioned, append-only; the latest row wins). Managed through the generic engine/config UI under the "stripe" module. Fields:

  • api_key — Stripe secret key. Set as the global stripe.Key on each request that needs the API.
  • webhook_key — webhook signing secret used by webhook.ConstructEvent.
  • donation_items_json — JSON array of {name, price_id} entries. The price_id allow-list gates /donations/checkout.

The donation_items_json column is added to existing DBs via an idempotent ALTER TABLE whose error is intentionally ignored.

Subscription state reconciliation

applySubscriptionUpdate (module.go:193) is the only writer of stripe_subscription_* columns from webhooks. It guards against out-of-order Stripe events using a single UPDATE with a WHERE clause that accepts the event only if:

  1. The event's subscription ID matches the member's currently-tracked one, OR
  2. The member has no subscription on file, OR
  3. The event status is active or trialing (a new subscription legitimately taking over).

Any other event (notably a non-active status for a subscription ID that doesn't match what's tracked) is dropped as stale. This prevents a delayed customer.subscription.deleted for a replaced subscription from clobbering a newer active one and deactivating the member. See webhook_test.go for the full matrix and the regression case.

Member lookup is by lowercased email. Unknown emails are a silent no-op.

Checkout behavior

  • Subscription price is selected by lookup_key = monthly or yearly, controlled by the member's bill_annually flag.
  • Discounts: iterates all Stripe coupons and applies the first valid one whose metadata.discountTypes (comma-separated, case-insensitive) contains the member's discount_type.
  • Existing stripe_customer_id is reused on Checkout/Portal sessions to avoid duplicate Stripe customer objects. Donation flow lazily creates a customer if none exists and persists the new ID.
  • Donation Checkout sets payment_intent_data.setup_future_usage=on_session so the payment method is saved on the customer for prefill on subsequent sessions.

Logging

All meaningful outcomes (webhook receipt, ignored stale events, API errors, checkout/portal/donation creation) go through engine.EventLogger with the member ID resolved from the customer email when available.

Documentation

Overview

templ: version: v0.3.1001

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	APIKey        string         `` /* 145-byte string literal not displayed */
	WebhookKey    string         `` /* 183-byte string literal not displayed */
	DonationItems []DonationItem `json:"donation_items" config:"label=Donation Items,item=Donation Item,key=Name"`
}

Config holds Stripe-related configuration.

type DonationItem

type DonationItem struct {
	Name    string `json:"name" config:"label=Name,placeholder=e.g. 3D Printing Materials,required"`
	PriceID string `json:"price_id" config:"label=Stripe Price ID,placeholder=price_...,required"`
}

DonationItem represents a configurable donation option that maps to a Stripe Price.

type Module

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

func New

func New(db *sql.DB, self *url.URL, eventLogger *engine.EventLogger) *Module

func (*Module) AttachRoutes

func (m *Module) AttachRoutes(router *engine.Router)

func (*Module) ConfigSpec

func (m *Module) ConfigSpec() config.Spec

ConfigSpec returns the Stripe configuration specification.

Jump to

Keyboard shortcuts

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