Subscriptions
Recurring billing on Inkress — plans, trials, renewals, card updates, and the webhooks that tell you what happened.
How it fits together
Plan
What you sell: a name, an amount, a currency, and a billing cycle. Created once and shared with many customers.
Subscriber
A customer attached to a plan, with their own status, start date, and next billing date.
Charge
Each successful renewal creates an order, so subscription revenue shows up alongside everything else.
Plan options
| Option | What it does |
|---|---|
| Billing cycle | Daily, weekly, monthly, or yearly. |
| Trial period | A number of days before the first charge. Cancel during the trial and the customer is never charged. |
| Auto-charge | Whether renewals charge the stored card automatically, or the customer is emailed a payment link each cycle. |
| Usage-based billing | Define custom metrics and price per unit, on top of or instead of a flat rate. |
| Redirect URL | Where the customer lands after subscribing. |
| Public / private | Whether the plan is openly subscribable via its link. |
Auto-charge changes behaviour elsewhere. With it off, actions that would charge the stored card instead email the customer a payment link. Worth knowing before you assume a charge was taken.
Subscription webhooks
Recurring billing happens without a browser in the loop, so webhooks are the only way to know what happened. Subscribe to the subscriptions wildcard to receive all of them.
subscriptions.created
A new subscription was created.
subscriptions.trial_started
A trial began. No charge taken yet.
subscriptions.trial_ending
The trial is nearly over — a good prompt point.
subscriptions.trial_ended
The trial finished and billing begins.
subscriptions.activated
The subscription became active.
subscriptions.payment_success
A renewal charge succeeded.
subscriptions.payment_failed
A renewal charge failed — usually a dead card.
subscriptions.adhoc_charge
A one-off charge was raised outside the cycle.
subscriptions.cancelled
The subscription was cancelled.
app.post("/webhooks/inkress", async (req, res) => {
const { event, subscription } = req.body;
if (event === "subscriptions.payment_failed") {
// Almost always an expired or replaced card.
// Prompt the customer to update it rather than cancelling —
// the subscription and its billing date survive a card change.
await emailCardUpdatePrompt(subscription.customer_id);
}
res.sendStatus(200);
});Full reference: Webhook Events.
When a customer's card stops working
Customers can replace the card on a subscription themselves. Previously this meant cancelling and re-subscribing, which lost the billing date and the subscription history.
Send the update link
The merchant sends the customer their update payment method link.
Customer enters a new card
On a secure page, with the usual card protections.
A small authorisation verifies it
A temporary authorisation of about one dollar confirms the card is good. It is not a charge and it drops off — but customers do ask about it, so say so up front in your own messaging.
Future renewals use the new card
The subscription, its status, and its next billing date are all preserved.
One-off charges outside the cycle
An active subscriber can be charged an extra amount — an add-on, an overage, a one-time fee — without disturbing their schedule.
With auto-charge on
The stored card is charged immediately.
With auto-charge off
The customer is emailed a payment link for the amount.
In both cases the next billing date does not move, and a subscriptions.adhoc_charge event is emitted. Only active subscribers can be charged this way.
3-D Secure and recurring charges
The customer completes 3-D Secure once, when they set the subscription up. Later renewals charge the stored credential without asking them to verify again — if every renewal needed a live challenge, renewals would fail whenever the customer was asleep or away from their phone.
See 3-D Secure.
Cancellation
Customers cancel from their subscription page. The subscription stops at the end of the current billing period, so the customer keeps access for what they have already paid for. A subscriptions.cancelled event is emitted.
Pausing is not currently supported — cancelling and re-subscribing is the workaround.