Skip to content

Payment methods

The payment_method field on /payments/collect selects how the customer pays. The fields each method requires on /payments/submit differ; this page is the reference table.

payment_methodRequired submit fieldsAsync?OTP?SDK widget
walletewallet_number, ewallet_pinSyncSometimesYes
mobile_moneymobile_money_phone_numberAsync (poll)NoYes
card (Mastercard hosted)mastercard_payment_method: "HOSTED_SESSION", session_id, card_dataAsync (3DS)Possible (3DS)Yes
flashflash_number, flash_pinSyncSometimesYes
bank_transferbank_account_id and friendsAsyncNoPartial — bank list via SDK
google_paynative_pay_token (set on /collect)SyncNoRoadmap
apple_paynative_pay_token (set on /collect)SyncNoRoadmap
none(defer choice)Yes
  • none — pass this on /collect when you want the SDK or the customer to pick later. You’ll then call GET /payments/transaction/{encrypted_payment_id} to fetch the available options for this transaction (computed from the merchant’s enabled methods plus the customer country).
  • wallet — Pdirect’s e-wallet. Fastest path to a successful test.
  • mobile_money — Onafriq-backed mobile money rails. Always asynchronous; poll GET /payments/mobile-money/status/{payment_id} or wait for a webhook.
  • card — Mastercard. Currently HOSTED_SESSION only; DIRECT_CAPTURE is on the roadmap. The hosted flow requires a WebView to handle the 3DS challenge — both SDKs ship one.
  • flash — Voucher-based payments. Treat the same as wallet from the integrator’s perspective.
  • bank_transfer — Customer scans a list of supported banks (fetched via GET /payments/get-config-bank) and uploads proof. Settles asynchronously.
  • google_pay / apple_pay — Native pay tokens passed at /collect time. The SDKs do not yet expose a public method to produce these; for now, the host app is responsible.

A synchronous flow lands in approved or declined within the same /submit call. Wallet and flash paths typically resolve this way unless an OTP step kicks in.

An asynchronous flow returns pending or processing from /submit and settles later. Mobile money, card 3DS, and bank transfer always go this route. Use webhooks for production; poll if you must.

Sync /submit → 200 status:approved
Async /submit → 200 status:processing
(seconds to minutes later)
webhook → POST notify_url status:approved
OR
GET /payments/status/{txn} → status:approved

Both require a number and a PIN. Format constraints are loose at the gateway level — your SDK or UI is responsible for input validation.

{
"payment_method_id": "pm_wallet_001",
"ewallet_number": "3005710720108954",
"ewallet_pin": "048698"
}

Some wallet flows trigger an OTP after /submit — the response will include status: "pending_otp_verification" and you should call /payments/verify-otp.

Requires only the customer’s phone number in E.164 format. The processor pushes the customer a USSD or app-prompt; once they authorise, the gateway updates and dispatches the webhook.

{
"payment_method_id": "pm_mobile_money_001",
"mobile_money_phone_number": "+243998857000"
}

See Mobile money status polling.

Three-step:

  1. /payments/collect with payment_method: "card" and mastercard_payment_method: "HOSTED_SESSION". The response includes a payment_url or session id you’ll hand to the hosted form.
  2. Customer enters card details directly in Mastercard’s hosted form. Their card data never touches your servers — that’s the point.
  3. /payments/submit with session_id and card_data from the hosted session. The gateway then drives the 3DS challenge if the issuer requires one.

See Card payments with 3DS.

Two-step:

  1. /payments/get-config-bank — fetch the supported bank list, render the picker.
  2. /payments/submit with bank_account_id (the customer’s chosen bank entity), bank_account_number, and bank_account_label.

The customer then receives instructions to wire funds to a generated beneficiary account. Status starts at pending_bank_proof_upload, moves to bank_payment_validated once the operator confirms the incoming wire, and eventually approved.

The token lives on /payments/collect itself, not on /submit:

{
"payment_method": "google_pay",
"native_pay_token": "<opaque token from Google Pay button>",
"native_pay_token_type": "PAYMENT_GATEWAY"
}

/submit is then a no-op (the token already authorised the amount). The status is approved synchronously when the token validates.

If a customer pays with a wallet denominated in a different currency than the merchant priced in, set transactional_currency on /payments/collect:

{
"amount": "12.50",
"currency": "usd",
"payment_method": "wallet",
"transactional_currency": "cdf"
}

The gateway converts at the rate active at session-mint time and debits in transactional_currency. The merchant settles in currency.