Recurring Charges
Recurring Charges
Recurring charges let you bill merchants on a monthly or yearly cycle. They support optional free trial periods and are automatically renewed by the platform.
Create a Subscription
POST /api/apps/v1/billing/subscriptions
Authorization: Bearer sx_at_...
Content-Type: application/json
{
"name": "Pro Plan",
"description": "Monthly pro subscription",
"amount": 500.00,
"currency": "BDT",
"billing_interval": "monthly",
"trial_days": 14,
"return_url": "https://app.example.com/billing/success",
"metadata": { "plan": "pro" }
}Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name shown to the merchant |
description | string | No | Additional details about the subscription |
amount | number | Yes | Amount per billing cycle in BDT (min 10.00, max 50,000.00) |
currency | string | Yes | Must be "BDT" |
billing_interval | string | Yes | "monthly" or "yearly" |
trial_days | integer | No | Number of free trial days before first charge |
return_url | string | No | URL to redirect the merchant after payment |
metadata | object | No | Arbitrary key-value data stored with the subscription |
Response:
{
"charge_id": 8,
"name": "Pro Plan",
"amount": 500.00,
"base_amount": 500.00,
"currency": "BDT",
"billing_interval": "monthly",
"trial_days": 14,
"fee_payer": "developer",
"commission_rate": 0.1,
"platform_amount": 50.00,
"gateway_fee_rate": 0.025,
"gateway_fee_amount": 12.50,
"developer_amount": 437.50,
"status": "pending",
"confirmation_url": "/22/settings/apps/billing/8",
"created_at": "2026-02-28T10:00:00.000Z"
}See Billing Overview — Fees & Commission for how fee_payer affects the amounts.
Approval Flow
The approval flow is identical to one-time charges:
-
Redirect the merchant to the
confirmation_urlusingpostMessage:window.parent.postMessage({ type: 'selorax:billing-redirect', url: confirmation_url }, '*'); -
Merchant approves and pays via EPS gateway.
-
On success, the subscription becomes
activewith the billing period set:current_period_start— start of the current billing cyclecurrent_period_end— end of the current billing cyclenext_billing_at— when the next charge will be created
If trial_days is set, the first billing period starts after the trial ends and no payment is required upfront during the trial.
Subscription Lifecycle
pending ──→ merchant approves ──→ active
│
trial ends / period ends
│
▼
renewal charge created (pending)
│
merchant pays ──→ active (period extended)
│
merchant doesn't pay ──→ expired
Auto-Renewal
The platform handles renewal automatically:
- A cron job creates a new pending charge 48 hours before
next_billing_at. - A
subscription.renewal_pendingwebhook is fired to your app. - The merchant pays the renewal charge (same EPS flow).
- On successful payment, the billing period is extended and the cycle continues.
The 48-hour window gives merchants time to review and pay the renewal before their subscription lapses.
Cancel a Subscription
To cancel a subscription from your app:
DELETE /api/apps/v1/billing/recurring/:id
Authorization: Bearer sx_at_...Response:
{
"charge_id": 8,
"status": "cancelled",
"cancelled_at": "2026-02-28T10:00:00.000Z"
}After cancellation:
- The subscription status is set to
cancelled. - A
charge.cancelledwebhook is fired to your app. - The merchant retains access until
current_period_end(the subscription is not terminated immediately).
Automatic Cancellation on Uninstall
When a merchant uninstalls your app, all active recurring charges are automatically cancelled by the platform. You will receive a charge.cancelled webhook for each one.
Webhooks
| Event | Fired When |
|---|---|
charge.activated | Merchant approved and initial payment succeeded |
subscription.renewal_pending | Renewal charge created 48h before next billing date |
charge.cancelled | Subscription cancelled by app or merchant uninstall |
charge.declined | Merchant declined the initial charge |
charge.expired | No action within 48 hours (initial or renewal) |
Example: Checking Subscription Status
// Fetch current subscription status
const response = await fetch('https://api.selorax.io/api/apps/v1/billing/subscriptions', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const subscriptions = await response.json();
// Find active subscription
const activeSub = subscriptions.data.find(s => s.status === 'active');
if (activeSub) {
console.log(`Plan: ${activeSub.name}`);
console.log(`Next billing: ${activeSub.next_billing_at}`);
console.log(`Period ends: ${activeSub.current_period_end}`);
} else {
// No active subscription — show upgrade prompt
}Best Practices
- Always check subscription status before granting access to paid features. Use the API or cache the status locally and update it via webhooks.
- Handle renewal failures gracefully. If a renewal charge expires, downgrade the merchant's access but give them a way to re-subscribe.
- Use trial periods to reduce friction. A 7-14 day trial lets merchants evaluate your app before committing.
- Set meaningful metadata (e.g., plan tier) so you can look up what the merchant is paying for without a separate database lookup.