A VTU (Virtual Top-Up) app sells data, airtime and bills to customers and delivers them instantly. This guide builds the core flow against the Plustive API: fund a wallet, list plans, buy, make retries safe, and confirm the result. Every example uses the live base URL https://api.plustiveimpact.com and a Bearer key.
What you’ll need
- A Plustive account and API key (the key looks like
pk_live_…). - A funded prepaid wallet — you draw down from this on every purchase.
- Anything that can send an HTTPS request (curl, Node, Python, your backend).
Step 1 — Confirm auth with your balance
Start with a read-only call so you know the key works before moving money. All amounts come back in kobo.
curl https://api.plustiveimpact.com/api/v1/balance \
-H "Authorization: Bearer pk_live_xxx"
→ { "balance": 24823000, "currency": "NGN" } # ₦248,230.00Step 2 — List plans to get an ID and your price
Data is bought by plan ID, not a free amount. The catalog returns the price you pay, so you can add your margin and show your customer a retail price.
curl https://api.plustiveimpact.com/api/v1/plans \
-H "Authorization: Bearer pk_live_xxx"
→ [
{ "id": "mtn-1gb-30d", "price": 27000 }, # your cost: ₦270.00
{ "id": "glo-2gb-30d", "price": 48000 }
]Step 3 — Make the purchase
Send the plan, the recipient’s phone number, and a clientReference you generate (an order ID works well). The wallet is debited and the bundle delivered.
curl -X POST https://api.plustiveimpact.com/api/v1/data \
-H "Authorization: Bearer pk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"plan": "mtn-1gb-30d",
"phone": "08030000000",
"clientReference": "ord_8812"
}'
→ { "reference": "PLS-9KQ2M7P", "status": "Success" }Airtime is nearly identical, but you pass a network and an amount in kobo:
curl -X POST https://api.plustiveimpact.com/api/v1/airtime \
-H "Authorization: Bearer pk_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "network": "glo", "amount": 50000, "phone": "08050000000", "clientReference": "ord_9920" }'
# amount 50000 = ₦500.00Step 4 — Make every retry safe
Networks time out. The danger is resending a purchase and charging twice. The fix is the clientReference: Plustive enforces idempotency on it, so resending the identical request returns the original transaction instead of buying again.
Generate one clientReference per customer order and persist it before you call. On any error or timeout, retry with the same value — never a new one.Step 5 — Confirm the final state
Most calls return Success immediately. Some return Pending and settle within about a minute; Plustive reconciles these for you and auto-refunds the wallet, to the kobo, if the purchase ultimately fails. Read the outcome by querying the transaction:
curl https://api.plustiveimpact.com/api/v1/transactions/PLS-9KQ2M7P \
-H "Authorization: Bearer pk_live_xxx"
→ { "reference": "PLS-9KQ2M7P", "status": "Success", "amount": 27000 }For production, don’t poll — register a webhook. Plustive POSTs to your URL when a transaction reaches its terminal state (Success, Failed or Refunded), signed so you can verify it. Update your order from the webhook and you have a fully asynchronous, reliable flow. See the webhooks reference.
Go-live checklist
- Persist a unique
clientReferenceper order, and retry with the same one. - Treat
Pendingas not-yet-final; act only on the terminal state. - Verify webhook signatures before trusting them, and make your handler idempotent.
- Keep all money in kobo end-to-end; convert to naira only for display.
- Reconcile your records against
GET /api/v1/transactions/{ref}for anything you didn’t get a webhook for.
That’s the whole pattern. The same shape — authenticate, (verify), buy with a clientReference, confirm — extends to bills; see automating bill payments.