The wallet model
The wallet is the single idea that makes Trile different from Stripe. Understand it and the rest of the API follows.
Why a wallet?
Section titled “Why a wallet?”Recurring billing elsewhere works by storing a card and pulling from it each month. Nepal has no equivalent rail — banks don’t support Variable Recurring Payments, and eSewa/Khalti/Fonepay can’t schedule auto-charges. So Trile can’t pull on renewal.
Instead, the customer pre-funds a wallet, and Trile deducts from that balance each cycle. The authorization happens once (at top-up/subscribe); renewals are internal ledger movements, not new payment-provider transactions.
Anatomy
Section titled “Anatomy”- Balance — available paisa, as a string (
balance_paisa). - Ledger — append-only entries (
led_…) for every credit (top-up) and debit (billing, withdrawal). The balance is the sum of the ledger; the two are reconciled daily. - Pending — top-ups initiated but not yet settled by the provider (
pending_paisa). - KYC tier & cap — a per-customer maximum balance/top-up set by NRB rules. See KYC & wallet limits.
Read it all from GET /v1/wallet/balance (end-user session):
{ "balance_paisa": "1250000", "currency": "NPR", "kyc_tier": "tier_1", "cap_paisa": "10000000", "pending_paisa": "0", "kyc_status": "verified"}Funding the wallet
Section titled “Funding the wallet”Customers top up via eSewa, Khalti, IME Pay, or bank transfer. A top-up:
POST /v1/wallet/topup(or the checkout top-up step) creates a pending top-up (pnd_…) and returns a provider handoff URL.- The customer pays at the provider.
- The provider redirects back; Trile verifies with the provider’s status API and settles — crediting the wallet ledger and clearing the pending amount.
Settlement is verified server-side against the provider, never trusted from the redirect alone.
How billing draws from the wallet
Section titled “How billing draws from the wallet”When a subscription cycle is due, the billing worker:
- Generates an invoice for the cycle amount.
- Attempts to debit the wallet for that amount.
- Success → invoice
paid, ledger debited,invoice.paidevent/webhook fired. - Insufficient balance → invoice stays
open, subscription goespast_due, andinvoice.payment_failedfires. The customer is prompted to top up; Trile retries.
The very first cycle is charged synchronously at subscription creation — which is why
POST /v1/subscriptions returns insufficient_funds if the wallet can’t cover it.
States you must handle
Section titled “States you must handle”| Situation | What you’ll see |
|---|---|
| Wallet can’t cover first cycle | 400 insufficient_funds on subscription create |
| Renewal fails (empty wallet) | Subscription status: past_due, invoice.payment_failed webhook |
| Customer tops up after going past_due | Trile retries; on success subscription returns to active, invoice.paid fires |
| Top-up exceeds KYC cap | 400 wallet_cap_exceeded |