Skip to content

Recurring Subscriptions & Smart Dunning

In Africa, credit card penetration is under 5%. Traditional subscriptions relying on silent card autopay tokenization fail because 90%+ of African payments run on mobile money wallets (Telebirr, CBE Birr, Safaricom M-Pesa).

Zirzir provides a purpose-built Smart Dunning & Recurring Billing Engine engineered specifically for mobile money prompt flows.


graph TD
A[Customer Enrolls in Plan] --> B[1. Pre-Dunning Notice<br/>SMS / Webhook 24h prior]
B --> C[2. Renewal Date: STK Push Dispatched<br/>Prompt appears on customer phone]
C -->|Customer Authorizes PIN| D[✅ Cycle Renewed<br/>next_billing_at = +30d]
C -->|Fail: Offline / Low Balance| E[⚠️ Smart Dunning Retry Engine]
E --> F[Retry 1: +12h Evening Prompt]
F -->|Success| D
F -->|Fail| G[Retry 2: +24h Morning Prompt]
G -->|Success| D
G -->|Fail| H[Retry 3: Payday Window]
H -->|Success| D
H -->|Exhausted > 3 Retries| I[🛑 Transition to 'past_due'<br/>Trigger subscription.past_due webhook]
style A fill:#047857,stroke:#10b981,color:#fff
style D fill:#047857,stroke:#10b981,color:#fff
style E fill:#d97706,stroke:#f59e0b,color:#fff
style I fill:#b91c1c,stroke:#ef4444,color:#fff

sequenceDiagram
autonumber
actor Customer
participant App as Merchant Backend
participant Zirzir as Zirzir Rust Engine
participant Telco as Mobile Money (Telebirr/M-Pesa)
Note over Customer,Zirzir: Day 0: Initial Subscription
Customer->>App: Click 'Subscribe to Pro' (500 ETB/mo)
App->>Zirzir: POST /api/v1/subscriptions
Zirzir->>Telco: STK Push (500 ETB)
Telco->>Customer: Phone PIN Prompt
Customer->>Telco: Confirms with PIN
Telco-->>Zirzir: Webhook (Status: Completed)
Zirzir-->>App: Event 'subscription.created'
Note over Customer,Zirzir: Day 30: Automated Renewal & Smart Dunning
Zirzir->>Zirzir: Background Worker detects due cycle
Zirzir->>Telco: Dispatch Renewal STK Push
Telco-->>Zirzir: Failed (Insufficient Balance)
Zirzir->>Zirzir: Schedule Retry 1 in 12h
Note over Customer,Zirzir: Day 31: Retry 1
Zirzir->>Telco: Re-dispatch STK Push
Customer->>Telco: Confirms with PIN
Telco-->>Zirzir: Webhook (Status: Completed)
Zirzir->>Zirzir: Advance cycle to Day 60
Zirzir-->>App: Event 'subscription.renewed'

Create recurring plans via CLI, Dashboard, or REST API:

Terminal window
zirzir sub plan create --name "Pro Monthly" --amount 500.0 --currency ETB --interval month
import { Zirzir } from '@zirzir/sdk';
const zirzir = new Zirzir({
baseUrl: 'http://localhost:8080',
apiKey: process.env.ZIRZIR_API_KEY!,
});
// Create recurring plan
const response = await fetch('http://localhost:8080/api/v1/plans', {
method: 'POST',
headers: {
'X-API-Key': process.env.ZIRZIR_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Pro Tier Monthly',
amount: 500.0,
currency: 'ETB',
interval: 'month',
interval_count: 1,
}),
});

When a user signs up on your website:

Terminal window
curl -X POST http://localhost:8080/api/v1/subscriptions \
-H "X-API-Key: zz_test_..." \
-H "Content-Type: application/json" \
-d '{
"plan_id": "plan_9f81a2b",
"provider": "telebirr",
"customer_phone": "0911223344"
}'
  • Immediate Action: Dispatches an initial mobile money STK push prompt to the customer’s phone.
  • Ledger State: Sets current_period_start, computes current_period_end (+30 days), and schedules next_billing_at.

The Zirzir server runs a built-in background dunning cron worker:

  • Automatically detects when next_billing_at <= NOW().
  • Dispatches recurring STK prompts to user wallets.
  • Increments retry_count and reschedules failed charges with intelligent backoff windows.
  • Triggers webhook events (subscription.renewed, subscription.past_due).