YYayaw

Billing Products

Product catalog model and admin management workflow.

Catalog Model

Billing products are stored in billing_products with non-secret metadata:

  • product key
  • type (subscription or one_time)
  • plan slug
  • interval (monthly/yearly when applicable)
  • optional entitlement slug
  • Stripe price ID, managed internally after sync
  • mirrored Stripe Product/Price metadata
  • active flag
  • sort order

Admins manage product prices in Yayaw. When a product amount is saved, Yayaw creates or updates the matching Stripe Product, creates a new Stripe Price when the amount, currency, or interval changes, archives the previous Price for new checkouts, and stores the resulting Stripe price ID plus mirrored Product/Price metadata. Stripe secrets stay in environment variables.

Products & Services is the source of truth for billing product names, prices, types, intervals, Stripe sync status, and checkout availability. CMS data models must not duplicate those commercial fields.

Stripe coupons and promotion codes are mirrored in:

  • billing_stripe_coupons
  • billing_stripe_promotion_codes

Stripe remains the source of truth for discounts; Yayaw mirrors them for CMS variables and MCP inspection.

Admin Workflow

Use the dashboard admin page:

  • /dashboard/admin/billing-products
  • /dashboard/admin/billing-settings

Current V1 capabilities:

  • toggle product active state
  • edit product amount and currency
  • create and mirror the Stripe Product/Price through the Stripe API
  • edit sort order
  • keep product keys stable

Billing settings currently cover:

  • grace period days
  • GitHub code-access repository settings

Plan feature limits live in billing_plans and are read by runtime billing services. Media quotas and seat limits should be changed through the runtime billing plan model, not hard-coded into product cards.

CMS Product Content

The seed creates one global billing-product-content data model with one entry per billing catalog product. Each entry is linked by the locked catalog_product_key field and can hold only editorial additions such as badge, summary, feature bullets, CTA label, or highlight state.

Use this model to complement catalog variables in page or section content. Use {billing.product.<productKey>.*} and {billing.price.<productKey>.*} for product names, prices, Stripe price IDs, and checkout availability.

Organization Workflow

Use the organization billing page:

  • /dashboard/organization/billing
  • /dashboard/organization/plans

Current V1 capabilities:

  • /billing focuses on active plan summary and internal activity
  • /plans focuses on plan selection and checkout actions
  • start subscription checkout
  • start one-time checkout (pro_lifetime)
  • open Stripe billing portal
  • view disabled checkout reasons before clicking actions

The billing portal action is disabled until the organization has a recorded Stripe customer. This prevents a confusing portal failure before the first successful checkout creates the customer in Stripe.

Operational Notes

  • If a product has no synced Stripe price ID, inactive mirrored Stripe Price/Product, or a sync error, checkout is disabled for that product.
  • Subscription and one-time Stripe Checkout sessions allow Stripe promotion codes.
  • Seed only creates missing product rows and does not overwrite operator-managed catalog changes.
  • Product keys are stable contracts. Add a new product key for a new commercial offer rather than repurposing an existing key with different semantics.
  • Stripe Price IDs are immutable for amount/currency/interval. Amount changes create a new active Price and archive the old Price for new checkouts.
  • Discount data is mirrored from Stripe for read-side usage only. Create and govern coupons/promotion codes in Stripe.
  • One-time lifetime checkout grants pro_lifetime and creates a durable code-access grant keyed by Stripe checkout session.
  • Subscription checkout creates a durable code-access grant keyed by Stripe subscription ID, then refreshes or revokes that grant on subscription events.

MCP Workflow

Control-plane tools expose the catalog and Stripe discount mirror:

  • yayaw_billing_products_list
  • yayaw_billing_product_update
  • yayaw_stripe_discounts_list
  • yayaw_stripe_discounts_sync

Writes require control-plane:admin, Yayaw billing-product:update authorization, and a reason.

Validation

Useful checks after billing catalog changes:

bun test src/lib/server/services/billing/billing-products.test.ts
bun test src/lib/server/services/billing/stripe-catalog-sync.test.ts
bun test src/lib/server/services/billing/billing-organization-view.test.ts
bun run test