ZenoPay
Collect payments with ZenoPay
ZENOPAY pushes a payment request to the payer’s mobile-money wallet. The payer
authorizes it on their handset, and the order is polled to completion.
Available to ISPs in Tanzania (TZ), alongside ClickPesa, AzamPay, PalmPesa, PawaPay, PesaPal, Relworx, PayPal, and DPO.
Before you start
- Complete ZenoPay merchant onboarding in an account owned by the ISP.
- Collect the API key from the ZenoPay merchant dashboard.
- Confirm your workspace is reachable from the public internet, so ZenoPay can deliver its webhook.
Configure the gateway
Open Settings → Payments → ZenoPay
Select ZenoPay from the payment marketplace.
Enter the API key
API key (required) is stored as a secret and is not echoed back into the form after saving. It is sent as the
x-api-keyheader on every request.Leave the API host blank unless ZenoPay told you otherwise
API host (optional) overrides the endpoint Centipid Billing calls. Leave it empty to use the default ZenoPay host.
Run a controlled test payment
Pay a small amount from the captive portal. Verify the wallet prompt arrives, the payment lands in the ledger with method ZenoPay, and the subscriber’s package is applied.
Credentials
| Field | Required | Notes |
|---|---|---|
| API key | Yes | Secret. From the ZenoPay merchant dashboard. The only field required to switch to ZenoPay. |
| API host | No | Falls back to the default ZenoPay host. Enter a full URL. |
Callback URLs
The webhook address is sent with every payment request, so there is nothing to
register in the ZenoPay dashboard. Settings → Payments still shows it under
Callback URLs (https://<your-host>/api/zenopay/webhook) so you can confirm
the address ZenoPay will call, and check it is not blocked by a firewall or WAF
rule.
What the payer must provide
- Email address (supplied automatically). ZenoPay rejects an order without one, but no payer is asked to type an address. The subscriber’s saved profile email is used when there is one; otherwise an address is derived from the phone number being paid with, on an unroutable domain. A hotspot payer with no account can therefore pay normally. If ZenoPay ever emails a receipt, only payers with a real profile email will receive it.
- Tanzanian phone number (required). Accepted in local or international form; it is normalized before it is sent.
- Amount. Rounded to a whole number before the request is sent.
Because authorization happens on the payer’s handset rather than in a hosted checkout page, no ZenoPay domain needs to be added to the captive walled garden.
How a payment completes
Request
Centipid Billing posts the order to
/api/payments/mobile_money_tanzaniawith an order id it generates itself. That id is the local transaction reference, so the handle everything reconciles against exists before ZenoPay answers.Authorization
The payer approves the request on their phone.
Confirmation
A background job polls
/api/payments/order-statusevery 5 seconds, for up to 40 attempts (about 3 minutes 20 seconds), until the order reaches a terminal state. This poller — not the webhook — is what completes the payment, so a payment still completes when the payer closes the captive browser mid-checkout.Provisioning
On
COMPLETED, a payment is recorded with method ZenoPay and the subscriber’s package is applied asynchronously. OnFAILED, the reason is stored against the transaction and nothing is credited.
A failed request is deliberately not retried automatically. An automatic retry after a lost response could raise a second prompt on the same order and charge the payer twice. The payer retries by choice instead.
Troubleshooting
| Symptom | What it means | What to do |
|---|---|---|
| “ZenoPay API key not configured.” | No API key is saved. | Enter it in Settings → Payments → ZenoPay. |
| “ZenoPay authentication failed. Check the API key.” | ZenoPay rejected the key. | Re-copy it from the merchant dashboard. Confirm the account is live. |
| “A valid Tanzanian phone number is required for ZenoPay payments.” | The number could not be normalized. | Re-enter it in local or international form. |
| “Could not reach ZenoPay. Please try again.” | The request could not reach ZenoPay. | Check outbound connectivity from the application host, and any custom API host value. |
| A message from ZenoPay on a failed request | ZenoPay refused the order and its own reason is shown. | Read the message — it usually names the field at fault. |
| Payer paid, but the transaction sits pending | The order never reached a terminal state within the polling window. | Confirm the payment on the ZenoPay dashboard, then raise it with support with the order id and the payer’s phone number. |
For symptoms that are not specific to ZenoPay, see Payment troubleshooting. For every other gateway’s fields, see the credential reference.
