HTTP API reference
Use this for any language without an official SDK (Python, Go, Ruby, Elixir…), for the Express Checkout methods the PHP and Node SDKs don’t wrap — InnBucks, O’mari, Zimswitch, card tokens — and when debugging what an SDK is actually sending.
Paynow publishes five official SDKs: PHP, Node.JS, C#/.NET, Python and Java. Check for one before hand-rolling.
Contents
- Transport rules
- Endpoints
- Initiate a transaction
- Mobile money
- Express Checkout
- Passenger ticket transactions
- Status update callback
- Polling
- Trace
Transport rules
- All merchant→Paynow calls are HTTP POST.
- Body URL-encoded, header
Content-Type: application/x-www-form-urlencoded. - Responses are URL-encoded key/value strings, not JSON.
- Security material never appears in client-visible pages or URLs. Store the Integration Key encrypted or outside the main application database.
Endpoints
| Purpose | Method | URL |
|---|---|---|
| Initiate web transaction | POST | https://www.paynow.co.zw/interface/initiatetransaction |
| Initiate Express Checkout / mobile money | POST | https://www.paynow.co.zw/interface/remotetransaction |
| Initiate passenger (air) ticket transaction | POST | https://www.paynow.co.zw/interface/initiatetickettransaction |
| Poll transaction status | POST (empty body) | the pollurl returned to you |
| Trace by merchant reference | POST | https://www.paynow.co.zw/interface/trace |
| Submit O’mari OTP | POST | the remoteotpurl returned to you |
Initiate a transaction
POST /interface/initiatetransaction
| Field | Type | Description |
|---|---|---|
id |
Integer | Your Integration ID. |
reference |
String | Your unique transaction reference. |
amount |
Decimal | Final amount, two decimals, no currency symbol. |
additionalinfo |
String | Extra text shown to the customer. Never confidential data. (optional) |
returnurl |
String | Where the browser is sent afterwards. Include enough to identify the transaction. |
resulturl |
String | Where Paynow POSTs status updates. |
authemail |
String | Auto-logs the customer in. Required for Express Checkout. (optional otherwise) |
authphone |
String | Pre-fills the customer’s mobile number. (optional) |
authname |
String | Pre-fills the customer’s name. (optional) |
tokenize |
Boolean | Return a reusable card token in the status update. Permissioned — apply via support@paynow.co.zw. (optional) |
merchanttrace |
String | Unique per merchant, ≤32 chars. Lets you recover a transaction after a timeout. (optional, but send it) |
status |
String | Literal Message. |
hash |
String | See hashing.md. |
Success response
Status=Ok&BrowserUrl=http%3a%2f%2f...&PollUrl=http%3a%2f%2f...&Hash=8614C21D...
status = Ok, plus browserurl, pollurl, hash — and often paynowreference,
which the official table omits but which is inside the digest. Verify the hash over
every returned value before redirecting the customer anywhere.
Error response
Status=Error&Error=Invalid+amount+field
Mobile money
POST /interface/remotetransaction, same fields as above plus:
| Field | Description |
|---|---|
phone |
Wallet subscriber number to debit. |
method |
ecocash or onemoney. |
A USSD session is pushed to the handset asking for the wallet PIN. EcoCash works with Econet numbers, OneMoney with NetOne numbers. The method must be enabled on the integration in the Paynow dashboard or initiation fails with an error saying exactly that.
Express Checkout
POST /interface/remotetransaction. Captures the payment method inside your application
and completes payment without redirecting.
Methods: zimswitch, vmc (Visa/Mastercard), ecocash, onemoney, innbucks, omari.
| Field | Required for | Description |
|---|---|---|
method |
all | One of the six above. |
phone |
mobile money | Wallet subscriber number. |
token |
vmc / zimswitch | Token from a previous tokenised transaction; enables recurring billing with no cardholder input. |
merchanttrace |
vmc / zimswitch | Unique per request. Prevents duplicate debits on timeout. |
authemail is required for all Express Checkout.
InnBucks
The response carries authorizationcode and authorizationexpires. Show both to the
customer.
authorizationexpires is documented as d-MMM-yyyy HH:mm — e.g. 20-Sep-2026 14:35
— verified against the Express Checkout page on 20 Sep 2026. No timezone is specified,
and Paynow does not say which one it means. So parse the format as documented, but do not
expire a code client-side from a guessed UTC offset: if you assume the wrong zone you kill
live payment codes hours early, which is the worst failure mode available here. Show the
string to the customer, and let Paynow decide when the code is actually dead.
Render the code as a QR code generated locally — don’t hand a payment code to a
third-party chart API, and Google’s Image Charts endpoint that older examples use is
deprecated. npm install qrcode / composer require endroid/qr-code.
Offer a deep link alongside the QR. Two schemes are in circulation and they disagree:
# PRIMARY — what the paynow npm package (v2.2.2) builds
schinn.wbpycode://innbucks.co.zw?pymInnCode=<authorizationcode>
# FALLBACK — what the developer hub documents
com.innbucks.customer://purchase?paymentToken=<authorizationcode>
On the Node SDK, prefer response.innbucks_info?.[0]?.deep_link_url over building either
string. Keep the QR as the primary path in the UI — it works regardless of which scheme
the installed app registered.
O’mari
Initiating sends an OTP by SMS. The response carries otpreference (display it) and
remoteotpurl. Complete with POST <remoteotpurl>:
id=12345&otp=012345&status=Message&hash=8614C21D...
Success returns a normal status update payload; failure returns
status=Error&error=Invalid+OTP. After five failed attempts the transaction is
cancelled and must be re-initiated — surface the attempt count in your UI.
Card tokens
Token transactions are re-tokenised on every charge and the new token comes back in the status update. Overwrite the stored token each time or recurring billing stops working. Tokens last up to six months from issue but never beyond the card’s own expiry: a token issued 3 March 2019 on a card expiring end of April 2019 expires 30 April 2019, not 3 September.
Passenger ticket transactions
POST /interface/initiatetickettransaction — for airlines and travel agents, so airline
addendum data travels with the payment for scheme compliance and fraud scoring. Amount is
in USD.
Beyond the standard fields: primaryticketnumber, passengerfirstname,
passengerlastname, passengerid, passengerstatus, passengertype,
firstdeparturelocationcode, firstarrivallocationcode, pnrnumber, officeiatanumber,
ordernumber, placeofissue, departuredate (yyyymmdd), departuretime
(HH:mm "GMT"zzz), arrivaldate, arrivaltime, journeytype (one way / round
trip), completeroute (CPT-JNB:JNB-NBO).
Passenger types: ADT adult · CNN child · INF infant · YTH youth · STU student ·
SCR senior citizen · MIL military.
Status updates behave identically, and also return payment-instrument detail.
Status update callback
Paynow POSTs to your resulturl whenever a transaction’s status changes.
| Field | Description |
|---|---|
reference |
Your merchant reference. |
amount |
Final amount, two decimals. |
paynowreference |
Paynow’s own reference. |
pollurl |
URL to poll for current status. |
status |
One of the status words below. |
hash |
Verify it. |
Tokenisation fields, when permitted: token, tokenexpiry (31-Dec-2027).
Payment-instrument fields, when enabled and only for successful payments:
paymentchannel, paymentinstrument (masked), paymentinstrumentname,
paymentinstrumentnationality (Domestic/Foreign), paymentchannelreference,
paymentchanneleci, paymentfraudscore, paymentfrauddecision
(Issue / Request Manual Review / Reject).
Status vocabulary
| Group | Statuses | Meaning |
|---|---|---|
| Money good | Paid, Awaiting Delivery, Delivered |
All three mean the customer paid. Awaiting Delivery is held in suspense until you confirm delivery; Delivered is inside the 24-hour confirmation window. |
| In flight | Created, Sent |
Created in Paynow / passed upstream. Not paid. |
| Terminal | Cancelled, Disputed, Refunded |
Cancelled cannot be resumed — recreate. Disputed holds funds pending resolution. |
| Trace only | NotFound |
Returned by /interface/trace. |
Treating all three “money good” states as paid is your job — the SDK helpers get it wrong. See the main SKILL.md, rule 2.
Retry behaviour
No specific response body is required, but if your endpoint returns an HTTP error Paynow
retries up to ten times before giving up. Return 200 quickly and do heavy work
asynchronously; make the handler idempotent because the same update legitimately arrives
more than once.
Polling
Empty POST to the pollurl. The reply has the same shape as a status update.
Paynow asks you to poll in two cases: confirming an important status update, and before deleting old unpaid transactions. In practice you also poll while waiting on a mobile money USSD authorisation — do that on a backoff in a worker, never a tight loop inside an HTTP request, and never expose the poll URL to the browser.
Trace
If you sent an Express Checkout request and never saw the response — timeout, crash,
network drop — you have no pollurl. merchanttrace is the way back.
POST /interface/trace with id, merchanttrace, status=Message, hash.
- Found → a standard status update message.
- Not found →
status=NotFound&hash=... - Error →
status=Error&error=Trace+failed
A trace error does not prove the transaction never existed. Retry before concluding anything, and never auto-refund or re-charge off one.
This page is generated from paynow-skills/paynow-integration/references/raw-http.md in the skill. Spotted something wrong? Open an issue.