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.
The Main Objects
Section titled “The Main Objects”Billing Config
Section titled “Billing Config”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:
npx guap push --testnpx guap push --liveEnvironment
Section titled “Environment”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.
Customer
Section titled “Customer”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_123org_123team_123workspace_123account_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.
Product
Section titled “Product”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.
Entitlement
Section titled “Entitlement”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
Section titled “Checkout”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.
Webhook Projection
Section titled “Webhook Projection”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.
What Your App Still Owns
Section titled “What Your App Still Owns”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.
Two Runtime Styles
Section titled “Two Runtime Styles”Managed Edge API
Section titled “Managed Edge API”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.
Local Read Model
Section titled “Local Read Model”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.
Common Mistakes
Section titled “Common Mistakes”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.