Repository navigation
systems billing and subscriptions
Active contributors: ferdiiskandar
Billing decides what an authenticated user may do: which daily quota applies, which models are unlocked, and whether a paid tier is active. It creates a Xendit invoice for upgrades, records the payment, and activates the subscription when the gateway webhook confirms payment. Free usage is governed by the same code, so a guest or an unpaid user runs on the GRATIS tier.
lib/billing/plans.ts tier limits, pricing, labels
lib/billing/guard.ts quota checks and usage tracking
lib/billing/subscription-service.ts invoice, activation, expiry
lib/billing/xendit-client.ts Xendit HTTP calls and webhook token check
desktop/ipc/subscription.ts subscription:upgrade handler
| Symbol | Kind | Path |
|---|---|---|
TIER_LIMITS, TIER_PRICING, TIER_LABELS, getLimit, isModelAvailable
|
tier definitions | lib/billing/plans.ts |
checkUsageQuota, checkAndTrackUsage, trackUsage
|
quota functions | lib/billing/guard.ts |
checkModelAccess, getUserTier, getUsageSummary
|
access and summary | lib/billing/guard.ts |
createSubscriptionInvoice, activateSubscription, handlePaymentFailed, cancelSubscription, processExpiredSubscriptions
|
lifecycle service | lib/billing/subscription-service.ts |
verifyWebhookToken, createInvoice, getInvoice
|
gateway client | lib/billing/xendit-client.ts |
handleSubscriptionCommand |
desktop IPC handler | desktop/ipc/subscription.ts |
Four tiers exist. GRATIS allows 20 transforms, 3 optimizations, 3 evaluations, and 3 recommends per day, no cloud history, and only openai-gpt4o and claude-sonnet. PRO lifts transforms to unlimited with 50/30/20 for the LLM tools. TIM raises those to 200/100/50 and allows 10 team members. ENTERPRISE is unlimited across the board with API access and 100 team members. A limit of -1 means unlimited. Pricing is PRO at 49,000 IDR monthly or 399,000 yearly, TIM at 149,000 or 1,249,000, and ENTERPRISE on custom pricing; trial lengths are 7, 14, and 30 days.
getUserTier reads the Subscription row and returns GRATIS unless the status is ACTIVE. checkUsageQuota computes daily usage from UsageRecord keyed by (userId, type, date). checkAndTrackUsage does the check and increment in one atomic upsert, so the count is read after increment and allowed is count <= limit; unlimited tiers still call trackUsage for analytics. checkModelAccess delegates to isModelAvailable, which honors the ["*"] wildcard.
graph TD
Renderer[Desktop renderer] -->|subscription:upgrade| IPC[desktop/ipc/subscription.ts]
IPC --> Invoice[createSubscriptionInvoice]
Invoice --> XenditCreate[xendit-client createInvoice]
Invoice --> PaymentPending[Payment row PENDING]
Xendit[("Xendit webhook")] --> Activate[activateSubscription]
Activate --> PaymentPaid[Payment row PAID]
Activate --> SubActive[Subscription ACTIVE with period]
Activate --> Admin[createSupabaseAdminClient app_metadata tier]
Guard[guard.ts] --> UsageRecord[(usage_records)]
Guard --> Subscription[(subscriptions)]
handleSubscriptionCommand requires tier and interval, then calls createSubscriptionInvoice. That function computes the amount from TIER_PRICING, builds an invoice number CTE-<TIER>-<timestamp>, calls Xendit with IDR currency, a 24-hour duration, and a fixed payment-method list, and inserts a PENDING Payment whose gatewayResponse records requestedTier and requestedInterval for later webhook lookup. The redirect URLs come from NEXT_PUBLIC_APP_URL.
activateSubscription looks the payment up by gatewayPaymentId, returns early if already PAID, reads the requested tier and interval from gatewayResponse, maps the Xendit channel to a PaymentMethod, sets the payment to PAID, and updates the subscription to ACTIVE with a monthly or yearly period. It then writes the tier into Supabase app_metadata through the service-role client so JWT-based checks agree with Prisma.
handlePaymentFailed marks a payment FAILED or EXPIRED. cancelSubscription sets the subscription to CANCELED and records canceledAt; the tier stays until period end. processExpiredSubscriptions is the cron entry point: it finds CANCELED or PAST_DUE subscriptions past currentPeriodEnd, sets them to GRATIS and EXPIRED, and syncs the Supabase metadata. verifyWebhookToken compares the callback token with timingSafeEqual, and createInvoice/getInvoice authenticate with a Basic header built from XENDIT_SECRET_KEY.
The Optimizer and Evaluator IPC paths call getUserTier and checkAndTrackUsage before running a paid tool, and call checkModelAccess when a model is selected. Region-free read-only Transform does not. See Optimizer and Evaluator. Payment and quota rows are defined in Data and Prisma, required environment variables in Configuration, and secret handling in Security.
- Change limits, pricing, or model lists:
lib/billing/plans.ts. - Change quota or tracking behavior:
lib/billing/guard.ts. - Change invoice fields or payment-method mapping:
lib/billing/subscription-service.tsandlib/billing/xendit-client.ts. - Change the desktop upgrade payload:
desktop/ipc/subscription.ts.
| File | What it holds |
|---|---|
lib/billing/plans.ts |
Tier limits, pricing, labels, model access |
lib/billing/guard.ts |
Quota checks, atomic usage tracking, tier lookup |
lib/billing/subscription-service.ts |
Invoice creation, activation, cancellation, expiry |
lib/billing/xendit-client.ts |
Xendit HTTP client and webhook token verification |
desktop/ipc/subscription.ts |
subscription:upgrade command handler |
Open-source prompt engineering and multi-LLM tooling by Sentra Artificial Intelligence.
MyPrompt develops practical approaches to prompt engineering, multi-LLM optimisation, reusable prompt systems, and AI-native workflows — with an emphasis on structured, interoperable, and real-world AI use.
Built in Indonesia as part of the Sentra Artificial Intelligence ecosystem.
Sentra Artificial Intelligence · Source Repository · Official Website
Dr Ferdi Iskandar — Creator & Maintainer
LinkedIn ·
ORCID ·
Hugging Face ·
Kaggle ·
Medium ·
Substack ·
X ·
Threads
MyPrompt · Sentra Artificial Intelligence · Indonesia