Guides
Guide - Subscription Billing
Maya subscription management for organizations in booki-api — plans, payment history, webhook handling, and the checkout flow.
Organization owners subscribe to a paid plan via Maya. Without an active subscription, organizations default to the free plan. Payment history is tracked in the SubscriptionPayment collection for billing records and analytics.
Data Model
ISubscriptionPayment
| Field | Type | Description |
|---|---|---|
userId | ObjectId | Owner user who holds the subscription |
customerId | string | Maya checkout ID or customer ref |
subscriptionId | string | Internal subscription ID |
invoiceId | string | Maya payment ID |
paymentIntentId | string? | Maya payment ID |
amount | number | Payment amount (in smallest currency unit) |
currency | string | e.g. "php", "usd" |
billingPeriodStart | Date | Start of the billing period |
billingPeriodEnd | Date | End of the billing period |
seats | number | Number of branches/seats paid for |
pricePerSeat | number | Cost per seat |
interval | "monthly" | "annually" | Billing interval |
paymentMethod | string? | Payment method used |
status | string | pending, paid, failed, refunded |
receiptUrl | string? | Maya receipt reference URL |
failureReason | string? | Reason for payment failure |
metadata | object? | { name, serviceName } |
User Model Extension
Owner users have a mayaCustomerId field on their user document. It is created automatically when the first subscription checkout is initiated.
Subscription States
| State | Meaning |
|---|---|
trial | In trial period |
active | Paid and current |
suspended | Payment failed or manually suspended |
cancelled | Subscription ended |
Plans
| Plan | Requires subscription | Features |
|---|---|---|
free | No | Limited (platform-specific) |
pro | Yes — active Maya subscription | Full feature access |
All new organizations default to the free plan.
Subscription Checkout Flow
1. Owner initiates subscription
↓
2. POST /api/v1/maya/create-subscription-checkout
→ API creates Maya checkout session
→ Returns checkout URL
↓
3. Owner completes payment on Maya-hosted page
↓
4. Frontend calls POST /api/v1/maya/verify-session to confirm
↓
5. Maya fires webhook to POST /api/v1/maya/webhook
↓
6. API processes event, records subscription payment
API Endpoints
Get Current Subscription
GET /api/v1/organizations/billing/subscription
Authorization: Bearer <owner_token>
Response — active plan:
{
"id": "sub_1234567890",
"plan": "pro",
"status": "active",
"billingInterval": "monthly",
"currentPeriodEnd": "2026-04-15T00:00:00.000Z",
"trialEnd": null,
"cancelAtPeriodEnd": false,
"amount": 999,
"currency": "php"
}
Response — no subscription (free plan):
{
"plan": "free",
"status": "active",
"billingInterval": "monthly",
"currentPeriodEnd": null,
"trialEnd": null,
"cancelAtPeriodEnd": false
}
Get Payment History
GET /api/v1/subscription/my-payments
Authorization: Bearer <owner_token>
Query: search, page (default 1), limit (default 10), status ("pending"|"paid"|"failed"|"refunded")
Response (200):
{
"stats": {
"totalPaid": 5995,
"totalPending": 0,
"totalFailed": 999,
"totalRefunded": 0
},
"payments": {
"data": [
{
"_id": "507f1f77bcf86cd799439011",
"amount": 999,
"currency": "php",
"billingPeriodStart": "2026-02-15T00:00:00.000Z",
"billingPeriodEnd": "2026-03-15T00:00:00.000Z",
"seats": 1,
"pricePerSeat": 999,
"interval": "monthly",
"status": "paid",
"receiptUrl": "https://payments.maya.ph/receipts/...",
"metadata": { "name": "Codi Solutions", "serviceName": "Photo Studio" },
"createdAt": "2026-02-15T00:00:00.000Z"
}
],
"page": 1,
"limit": 10,
"total": 6,
"totalPages": 1
}
}
Get Single Payment
GET /api/v1/subscription/payments/:id
Authorization: Bearer <owner_token>
Returns the full payment record. Returns 403 Forbidden if the payment belongs to a different user.
Maya Webhook
The webhook handler listens at POST /api/v1/maya/webhook.
| Event | Handling |
|---|---|
payment.completed | Processes completed checkout; records subscription payment |
payment.failed | Marks subscription payment as failed |
payment.expired | Handles expired checkout sessions |
payment.cancelled | Handles cancelled payment sessions |
Configure the webhook endpoint URL in the Maya developer portal and set
MAYA_SECRET_KEYin.env.
Frontend Integration
// Check subscription status
const subscription = await $fetch(
"/api/v1/organizations/billing/subscription",
{
headers: { Authorization: `Bearer ${token}` },
},
);
if (subscription.plan === "free") {
// Show upgrade prompt
} else {
// Show plan details, next billing date, manage button
}
// Paginated payment history
const history = await $fetch("/api/v1/subscription/my-payments", {
headers: { Authorization: `Bearer ${token}` },
params: { page: 1, limit: 10, status: "paid" },
});
