Subscriptions and instalments
Wave and Orange Money can’t debit a customer automatically. So each billing period, Xaalis issues an invoice with
a payment link; the customer approves it in their wallet like any payment. Xaalis tracks the state; you send the
link (by SMS, WhatsApp or email) when you receive invoice.created.
The same feature gives instalment plans (paiement en plusieurs fois): a fixed number of payments, goods delivered after the last one. No credit is involved.
Create
Section titled “Create”curl -s -X POST http://localhost:4000/v1/subscriptions \ -H "Authorization: Bearer $XAALIS_KEY" -H "Content-Type: application/json" \ -H "Idempotency-Key: abo:customer_77" \ -d '{"customer":{"phone":"+221771234567","name":"Awa Ndiaye"},"amount":5000,"interval":"month","description":"Abonnement Premium"}'| Field | Rules |
|---|---|
customer |
{ phone: "+2217XXXXXXXX", name, email? } |
amount |
integer XOF, 100 – 100 000 000, per period |
interval, interval_count |
day · week · month · year; count 1–12 (default 1). Monthly billing keeps the day of the month, clamped to short months (31 Jan → 28/29 Feb → 31 Mar), in Dakar time |
billing_cycles |
null (until canceled) or 2–24 for an instalment plan |
days_until_due |
1–30, default 3 — after this the invoice is overdue |
cancel_after_days |
1–60, default 14 — grace after the due date before the subscription is canceled |
description, client_reference, metadata |
as on payments |
The response is the subscription with its first invoice (latest_invoice) already issued.
The invoice link
Section titled “The invoice link”Each invoice has a stable hosted_invoice_url (…/v1/public/invoices/{id}?cs=…). It redirects the customer to a
payment for that period. If an attempt fails or expires, opening the same link creates a new attempt — the link
never changes, so you can send it once and remind with the same URL. It stays payable until grace_ends_at.
Treat it like a password for that customer: don’t log it or send it to analytics.
Lifecycle
Section titled “Lifecycle”stateDiagram-v2
[*] --> active: POST /v1/subscriptions (invoice 0 issued)
active --> active: invoice paid → next period's invoice
active --> past_due: due_at passed, unpaid (invoice.overdue)
past_due --> active: paid during grace
past_due --> canceled: grace ended unpaid (invoice uncollectible)
active --> canceled: POST …/cancel
active --> completed: last instalment paid
- One invoice per period, never two, and no new invoice while one is unpaid — a customer is never asked for two periods at once.
- Paying an invoice is an ordinary payment: same fee, same balance credit, same
payment.succeededevent. cycles_paidcounts paid invoices; an instalment plan becomescompletedwhen it reachesbilling_cycles— ship onsubscription.completed.
Cancel
Section titled “Cancel”curl -s -X POST http://localhost:4000/v1/subscriptions/$SUB_ID/cancel \ -H "Authorization: Bearer $XAALIS_KEY" -H "Content-Type: application/json" \ -H "Idempotency-Key: cancel:$SUB_ID" -d '{"at_period_end": false}'at_period_end: falsestops now: an open invoice with no payment in progress is voided and its link stops working. If the customer is paying at that very moment, that payment is allowed to finish (and is credited).at_period_end: truekeeps the current invoice payable through its grace period and issues no next one.
Webhooks
Section titled “Webhooks”invoice.created (carries hosted_invoice_url — send it to the customer) · invoice.paid · invoice.overdue ·
subscription.past_due · subscription.canceled · subscription.completed. The payment.* events of each attempt
still fire. See Webhooks.
GET /v1/subscriptions/{id} · GET /v1/subscriptions?status= · GET /v1/invoices/{id} ·
GET /v1/invoices?subscription=&status= — cursor pagination like payments.
Testing
Section titled “Testing”POST /v1/subscriptions/{id}/test_advance (test keys only, Idempotency-Key required) moves the subscription’s
clock to its next billing event — the next period, the due date, or the end of grace — so you can test months in
seconds. One advance runs at a time (a concurrent one gets 409 invalid_state); a canceled or completed subscription
can’t be advanced.
Instalments: tell your customer
Section titled “Instalments: tell your customer”Before the first instalment, tell the customer what happens to instalments already paid if they stop paying — that’s your policy. Refunds through Xaalis are full refunds of each payment.