Payment And Entitlement Tables
Payment customers, subscriptions, orders, webhook idempotency, entitlements, and quota transactions.
Payment and entitlement schemas live in:
src/core/db/schema/sqlite/payment.schema.ts
src/core/db/schema/pg/payment.schema.tsThese tables cover the full chain from payment provider customer to subscription/order, webhook idempotency, entitlements, and quota audit logs.
Groups
| Group | Tables | Description |
|---|---|---|
| Payment identity | payment_customers | Maps app users to provider customers |
| Subscriptions and orders | payment_subscriptions, payment_orders | Local facts for recurring and one-time payments |
| Webhook idempotency | payment_webhook_events | Prevents repeated event processing |
| Entitlement assets | user_entitlements | Current usable resources |
| Quota audit | quota_transactions | Grant, consume, rollback, and expiration records |
Plan definitions are not stored in the database. They live in:
src/modules/billing/config/plan.tspayment_customers
| Field | Description |
|---|---|
user_id | App user ID, unique |
customer_id | Provider customer ID, unique |
email | Email used to create the customer |
metadata | JSON string with extra context |
created_at / updated_at | Timestamps |
Checkout and webhook flows ensure a customer exists.
payment_subscriptions
| Field | Description |
|---|---|
user_id | Owner user |
subscription_id | Provider subscription ID, unique |
plan_id | Local plan ID such as starter or pro |
price_id | Provider Price ID |
status | active, trialing, canceled, etc. |
current_period_start / current_period_end | Current cycle |
cancel_at_period_end / canceled_at | Cancellation state |
trial_end | Trial end timestamp |
metadata | JSON context |
Checkout creation uses subscription checks to avoid duplicate active subscriptions.
payment_orders
| Field | Description |
|---|---|
order_id | Provider order/payment ID, unique |
plan_id / price_id | Local plan and provider price |
amount / currency | Amount in the smallest currency unit |
status | completed, failed, refunded, etc. |
metadata | JSON context |
Subscriptions and orders should be synchronized by webhook, not only Checkout redirect.
payment_webhook_events
| Field | Description |
|---|---|
id | {provider}:{eventId} |
provider | Payment provider such as stripe |
event_id | Provider event ID |
event_type | Provider event type |
status | processing, processed, or failed |
error | Failure message |
Handled in src/modules/billing/webhooks/webhook-processor.ts.
user_entitlements
| Field | Description |
|---|---|
user_id | Owner user |
type | DURATION or QUOTA |
resource_type | CREDIT, STORAGE, etc. |
balance | Remaining balance; DURATION usually uses -1 |
priority | Lower numbers consumed first |
valid_from / valid_until | Validity period |
source | SUBSCRIPTION, TOPUP, LIFETIME |
source_id | Subscription ID, order ID, or source ID |
DURATION means unlimited usage while valid. QUOTA means consumable balance.
quota_transactions
| Field | Description |
|---|---|
transaction_id | Groups related quota rows |
action | BUY_TOPUP, SUB_GRANT, CONSUME, REFUND, EXPIRE |
amount | Change amount; consumption is usually negative |
balance_snapshot | Balance after the operation |
entitlement_id | Related entitlement |
status | PENDING, SUCCESS, FAILED |
description | Human-readable detail |
Use:
prepareQuota(userId, resourceType, amount);
commitQuota(transactionId);
rollbackQuota(transactionId);for work that may fail after reserving quota.
Lifecycle
User starts Checkout
POST /api/payment/checkout checks the user and resolves planId + priceId.
Provider sends webhook
POST /api/payment/webhook verifies the signature and writes payment_webhook_events.
Sync local facts
The processor updates customers, subscriptions, or orders.
Grant or refresh entitlements
The matching price's entitlements create user_entitlements and quota_transactions.
Business consumes quota
Quota services consume active QUOTA entitlements by priority and expiration.
Guidance
- Add plans in
plan.ts, not directly in the database. - Add new resource types in
EntitlementResourceTypefirst. - Webhook URL is
/api/payment/webhookwithout locale prefixes. - Store money amounts in the smallest currency unit, such as cents for USD.