API Reference
Collections API
Three ways to collect a payment, all backed by the same real Selcom Checkout integration and the same reversal-safe crediting lifecycle: hand the customer an Infinity Payment Page, push a prompt straight to their phone, or hand them a QR code to scan.
Which flow should I use?
Infinity Payment Page
Recommended for ecommerce websites, mobile apps, and invoices. You don't pick a payment method — Infinity hosts a page where the customer chooses Mobile Money Push, Selcom Pesa, or Scan QR themselves.
Direct Wallet Push / Selcom Pesa
Best when you already have the customer's phone number and want the fastest checkout — sends a real prompt immediately, no redirect.
Scan QR / TanQR
Best for POS/counter payments, delivery, or any screen where a customer scans instead of typing a phone number.
Endpoints
/v1/collectionsCreate an Infinity Payment Page — returns payment_url.
Idempotency-Key required/v1/collections/wallet-pushSend a Mobile Money Push prompt immediately.
Idempotency-Key required/v1/collections/selcom-pesaSend a Selcom Pesa prompt immediately.
Idempotency-Key required/v1/collections/qrCreate a Scan QR / TanQR collection.
Idempotency-Key required/v1/collections/{collection_id}Check a collection's current status.
API key or dashboard/v1/collections/{collection_id}/refresh-statusForce a fresh check with the provider.
API key or dashboardEvery collection_id above works with both status endpoints, regardless of which of the four creation endpoints returned it.
1. Infinity Payment Page
Create a collection, redirect your customer to the returned payment_url, and let Infinity handle the rest. Your backend never sees a phone number, a QR code, or a "method" field — the customer picks Mobile Money Push, Selcom Pesa, or Scan QR / TanQR on that page.
- Customer checks out on your website/app.
- Your backend calls
POST /v1/collections. - Infinity returns
payment_url. - Redirect the customer there.
- Customer chooses a payment method and pays.
- Infinity sends a webhook when the status changes (see the Webhooks page).
- Mark the order paid only once you see
collection.successful.
{
"merchant_id": "5c1f0b2a-3e21-4b9a-9c33-2f6a1d0e8b71",
"amount": 50000,
"currency": "TZS",
"customer_name": "Grace Mwakalinga",
"customer_phone": "255712345678",
"customer_email": "grace@example.com",
"reference": "ORDER-4821",
"description": "Payment for order ORDER-4821",
"redirect_url": "https://merchantstore.co.tz/thank-you",
"cancel_url": "https://merchantstore.co.tz/payment-failed"
}{
"success": true,
"data": {
"collection_id": "9b7e2c1a-...",
"reference": "ORDER-4821",
"status": "created",
"payment_url": "https://infinityafrica.net/pay/8f3a1c2b"
}
}webhook_url is not per-request
webhook_url field is accepted for forward compatibility but not yet used — configure your webhook URL once for your whole account via PATCH /v1/merchant/webhook-config or the Merchant Portal's Webhooks page. See the Webhooks page for details.2. Direct Wallet Push / Selcom Pesa
Same shape, two endpoints — /wallet-push for a general Mobile Money Push (STK/USSD, Selcom auto-detects the customer's carrier), /selcom-pesa to push specifically to a Selcom Pesa wallet. phone is required for both — a push has nowhere to go without one.
A 202/"processing" response means the prompt was sent — nothing more
collection.successful (webhook) or poll GET /v1/collections/{collection_id} until status is successful.{
"merchant_id": "5c1f0b2a-3e21-4b9a-9c33-2f6a1d0e8b71",
"amount": 50000,
"currency": "TZS",
"phone": "255712345678",
"customer_name": "Grace Mwakalinga",
"reference": "ORDER-4821",
"description": "Payment for order ORDER-4821"
}{
"success": true,
"data": {
"collection_id": "9b7e2c1a-...",
"reference": "ORDER-4821",
"status": "processing",
"message": "Payment prompt sent. Please approve on your phone."
}
}POST /v1/collections/selcom-pesa takes and returns the exact same shape — only the prompt message differs ("Selcom Pesa prompt sent. Please approve in your Selcom Pesa app.").
3. Scan QR / TanQR
customer_phone is optional here — nothing gets pushed to it. qr_payload and payment_token are exactly what Selcom's own order-creation response returned: Infinity never generates its own payment QR. Render qr_payload as a scannable code client-side (any standard QR library) exactly as received — don't re-encode it, and don't build your own payload from the order details.
{
"merchant_id": "5c1f0b2a-3e21-4b9a-9c33-2f6a1d0e8b71",
"amount": 50000,
"currency": "TZS",
"customer_name": "Grace Mwakalinga",
"reference": "ORDER-4821",
"description": "Counter payment"
}{
"success": true,
"data": {
"collection_id": "9b7e2c1a-...",
"reference": "ORDER-4821",
"status": "processing",
"payment_token": "80008000",
"qr_payload": "<exact Selcom-returned qr value>",
"expires_at": null
}
}expires_at is always null today — Selcom's order-creation response doesn't include a QR/token expiry field, so this is never fabricated. Don't assume the code is time-limited unless a future response actually returns one.
Checking status
{
"success": true,
"data": {
"collection_id": "9b7e2c1a-...",
"reference": "ORDER-4821",
"status": "pending_clearance",
"amount": 50000,
"currency": "TZS",
"method": "wallet_push",
"provider_payment_status": "COMPLETED",
"created_at": "2026-08-24T09:00:00+03:00",
"updated_at": "2026-08-24T09:02:00+03:00"
}
}POST /v1/collections/{collection_id}/refresh-status forces a fresh check with Selcom instead of waiting for a webhook — safe to call repeatedly. It never double-credits or double-reverses, and is a no-op if the customer hasn't picked a method yet on an Infinity Payment Page collection.
Status lifecycle
See the Webhooks page for the full lifecycle table and the merchant order-payment rule. In short: only mark an order paid when status is successful — never from created, processing, a QR/token being returned, or a wallet-push resultcode of 000.
Webhooks are the reliable way to resolve a collection
collection.successful/collection.failed/collection.reversed on the Webhooks page instead.