Skip to content

Core Concepts

Guapocado is built around one idea: billing should be described once, then consumed by your app as product primitives.

The config is local because your billing model is part of your product source code. It deserves review, diffs, code search, and repeatable deploys just like routes, database schemas, and feature flags.

billing.config.ts is the source of truth for your product billing model.

It defines:

  • Entitlement keys.
  • Product plans.
  • Pricing metadata.
  • Which entitlements each product grants.
  • Optional generation settings.
  • Optional webhook forwarding intent.

The CLI pushes the same file to test or live:

Terminal window
npx guap push --test
npx guap push --live

Each organization has isolated environments. Customer integrations normally use:

  • Sandbox for testing.
  • Production for real customers.

Both use https://api.guapocado.dev. The API key determines which environment is used.

A customer is the app identity being billed. Guapocado does not force this to be a Stripe customer ID.

Good customer IDs are stable product identities:

  • user_123
  • org_123
  • team_123
  • workspace_123
  • account_123

Choose the identity that matches how the product is sold. A team SaaS usually bills an organization or workspace. A consumer app may bill a user.

A product is a plan or purchase option in your config.

Products can be:

  • Unpriced, such as a free plan.
  • Recurring, such as a monthly Pro plan.
  • One-time, such as a credit pack or lifetime unlock.

An entitlement is something your app can check at runtime.

Guapocado has three entitlement types:

  • feature: a boolean yes/no permission.
  • meter: usage that Guapocado records and decrements.
  • limit: a numeric allowance that your app compares to local state.

These are covered in detail in Billing Primitives.

Checkout creates a Stripe-hosted payment session for a Guapocado product.

Your app asks Guapocado for checkout:

const session = await guap.checkout.create({
productKey: "pro",
successUrl: "https://app.example.com/billing/success",
cancelUrl: "https://app.example.com/billing",
});

Guapocado knows whether pro is recurring or one-time from the config.

Stripe webhooks are low-level payment events. Guapocado receives them and projects them into product state:

  • customer records
  • subscriptions
  • purchases
  • purchase grants
  • entitlement snapshots
  • usage and invoices

Your app should usually consume Guapocado domain events, not raw Stripe events.

Guapocado does not replace your product logic. It gives that logic a clean billing source of truth.

Your app still owns:

  • Auth and sessions.
  • Which user or organization maps to customerId.
  • UI decisions.
  • App-owned counts, such as number of seats or projects.
  • Route-level access control.
  • Calling consume() when billable work happens.

Guapocado owns:

  • Config validation.
  • Environment sync.
  • Stripe product and price sync.
  • Checkout session creation.
  • Stripe webhook projection.
  • Entitlement and usage APIs.

This is the default. Your app calls Guapocado’s hosted API through the SDK.

Use this when you want the smallest integration:

  • No Guapocado tables in your app database.
  • No webhook projection code in your app.
  • Fast enough for most apps.

This is optional. Guapocado forwards domain events to your app, and your app stores a local projection for hot-path reads.

Use this when:

  • You need entitlement checks without a network round trip.
  • You want local reporting over billing state.
  • You already operate a database and webhook worker.

Writes still go through Guapocado in both modes. Local read model only changes where reads come from.

Do not use a meter for everything. If your app already has the current count, use a limit.

Do not use Stripe price IDs in product code. Use product keys and entitlement keys from your config.

Do not put server keys in browser code. Browser code uses client keys or calls your backend.

Do not treat customerId as always being a user ID. In B2B apps it is usually an organization, workspace, or account.