Billing Integration
Relevant source files
The following files were used as context for generating this wiki page:
The Billing Integration system is responsible for managing user subscriptions, enforcing tier-based resource limits, and handling payment processing through Stripe. It defines several subscription tiers, each with specific quotas for resources like domains and inboxes. The system exposes API endpoints for users to view plans, manage their subscriptions, and initiate checkout.
This integration is primarily implemented in the API service and is designed to be extensible. While the core logic for Stripe integration (checkout, webhooks) is outlined, the actual Stripe SDK is not installed, and these parts function as placeholders. Limit enforcement is handled via a preHandler hook in the API, ensuring that users cannot exceed their plan's quotas when creating new resources.
Subscription Tiers and Limits
The platform offers multiple subscription tiers, including a free plan and several paid plans. Each tier has defined limits on the number of domains, inboxes, storage, and daily emails. The "Enterprise" tier features unlimited or custom limits.
Sources: services/api/src/routes/billing.ts:13-19, services/web/src/pages/LandingPage.tsx:238-360
| Tier | Domains | Inboxes | Storage | Daily Emails | Price |
|---|---|---|---|---|---|
| FREE | 1 | 3 | 0.1 GB | 50 | 0₫ / tháng |
| STARTER | 3 | 20 | 1 GB | 200 | 3 USD / month |
| PROFESSIONAL | 10 | 100 | 5 GB | 1000 | 8 USD / month |
| ENTERPRISE | Unlimited (-1) | Unlimited (-1) | 50 GB | Unlimited (-1) | Custom |
Sources: services/api/src/routes/billing.ts:14-18, services/web/src/pages/LandingPage.tsx:251-351
Data Model
The User model in the database is extended with fields to manage billing and subscription status. These fields track the user's current tier, their Stripe customer and subscription identifiers, and the status of their subscription.
The diagram below shows the billing-related fields on the User table.
User Table Billing Fields
| Field | Type | Description |
|---|---|---|
tier | String | The user's current subscription tier (e.g., 'FREE', 'STARTER'). |
subscriptionStatus | String | The status of the Stripe subscription (e.g., 'ACTIVE', 'CANCELED'). |
stripeCustomerId | String | The customer ID from Stripe. Nullable. |
stripeSubscriptionId | String | The subscription ID from Stripe. Nullable. |
trialEndsAt | DateTime | The timestamp when the user's trial period ends. Nullable. |
Sources: services/api/src/routes/billing.ts:55-63
API Endpoints
The billing functionality is exposed through a set of RESTful API endpoints. These endpoints are defined in services/api/src/routes/billing.ts.
Sources: services/api/src/routes/billing.ts
GET /billing
Retrieves the current user's subscription status, usage, and tier limits. Requires authentication.
Response (200 OK)
{
"tier": "FREE",
"status": "ACTIVE",
"stripeEnabled": true,
"limits": {
"domains": 1,
"inboxes": 3,
"storageGB": 0.1,
"dailyEmails": 50
},
"usage": {
"domains": 1,
"inboxes": 2
},
"trialEndsAt": null,
"hasSubscription": false
}
Sources: services/api/src/routes/billing.ts:49-92
GET /billing/plans
Returns a list of all available subscription plans and their features.
Response (200 OK)
{
"plans": [
{
"id": "FREE",
"name": "Free",
"price": 0,
"interval": "month",
"domains": 1,
"inboxes": 3,
"storageGB": 0.1,
"dailyEmails": 50,
"features": [
"1 custom domain",
"3 email inboxes",
"100MB storage",
"Basic spam filtering"
]
}
],
"stripeEnabled": true
}
Sources: services/api/src/routes/billing.ts:95-175
POST /billing/checkout
Creates a Stripe checkout session for a user to upgrade their plan. Requires authentication.
Request Body
| Parameter | Type | Description |
|---|---|---|
tier | Enum | The target tier to subscribe to ('STARTER', 'PROFESSIONAL', 'ENTERPRISE'). |
successUrl | String | Optional URL to redirect to on successful payment. |
cancelUrl | String | Optional URL to redirect to on cancellation. |
Note: The Stripe SDK is not installed, so this endpoint currently returns a 503 Service Unavailable error. The intended flow is documented in the sequence diagram below.
Sources: services/api/src/routes/billing.ts:178-235
The following diagram illustrates the intended sequence for creating a checkout session.
POST /billing/cancel
Cancels the user's active subscription. Requires authentication.
Request Body
| Parameter | Type | Default | Description |
|---|---|---|---|
immediately | Boolean | false | If true, cancels immediately. If false, cancels at the end of the period. |
Sources: services/api/src/routes/billing.ts:238-269
POST /billing/webhook
A public endpoint to receive and process webhooks from Stripe.
This endpoint is responsible for updating the user's subscription status in the database based on events from Stripe, such as customer.subscription.created, customer.subscription.updated, and customer.subscription.deleted.
The diagram below shows how a subscription update event is handled.
Limit Enforcement
Resource limits are enforced at two key points in the application: resource creation via the API and message processing in the worker.
Resource Creation Limits
A preHandler hook is attached to all authenticated routes. It specifically checks for requests to create new domains (POST /domains) and inboxes (POST /inboxes). Before the request handler is executed, this hook queries the database to count the user's current resources and compares it against their tier's limits. If the limit is reached, it returns a 403 Forbidden error.
Sources: services/api/src/routes/billing.ts:337-391
The following flowchart shows the logic for the pre-handler hook.
Message Quota Limits
When an email is processed by the worker, it checks the total number of messages for both the inbox and the parent domain. If either count exceeds the configured quota (quotaMessagesPerInbox or quotaMessagesPerDomain), the processing is aborted, and an error is thrown.
Sources: services/api/src/worker.ts:210-218
Summary
The billing system is a critical component for monetizing the email platform and ensuring fair resource usage. It is built around a tier-based subscription model with defined limits, managed via a set of API endpoints and backed by a Stripe integration. Although the payment processing part is currently a placeholder, the surrounding logic for plan definition, limit enforcement, and database schema is in place, providing a solid foundation for a fully functional billing system.