Documentation menu
Accepting payments

USSD push, Wave redirect, OTP by SMS and OTP by USSD.

View raw

Mobile Money payment flows

The flow depends on the country, operator and provider. Do not hard-code it from the brand name: load /countries, call /collect, and react to the response.

FlowInitial responseCustomer action
USSD push202, status: "pending"Approve the prompt on the phone
Redirect (Wave)202, flow: "wave", wave_urlOpen wave_url and approve
OTP by SMS400, error: "otp_required", referenceRead the SMS code, re-send with otp
OTP by USSD400, error: "otp_required", reference, ussd_codeDial ussd_code, enter the code, re-send with otp

In every case the payment remains pending until confirmed.

1. USSD push

Call /collect → 202 pending. Show a "waiting for approval" screen. Receive the webhook (or poll).

2. Wave and redirects

json
1{
2 "status": "pending",
3 "flow": "wave",
4 "wave_url": "https://pay.wave.com/c/…",
5 "transaction_id": "…",
6 "reference": "PLK-…"
7}

Open wave_url exactly as returned (do not build it yourself), then wait for the webhook.

3. OTP

First request — same body as a normal collect:

json
1{
2 "error": "otp_required",
3 "message": "OTP required. Dial the code shown to get your OTP, then retry with 'otp' and 'reference'.",
4 "reference": "PLK-7XK2M9QPLA",
5 "ussd_code": "#144*82#"
6}

Display ussd_code (when present) to the customer. After they enter the OTP, re-send the same request with:

  • reference = the reference from the otp_required response (replaces your order reference),
  • otp = the code the customer received.
bash
1curl -X POST https://paylinky.vercel.app/api/v1/collect \
2 -H "Authorization: Bearer $PAYLINKY_KEY" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "amount": 5000,
6 "currency": "XOF",
7 "country_code": "CI",
8 "operator": "Orange Money",
9 "phone": "+2250XXXXXXXX",
10 "reference": "PLK-7XK2M9QPLA",
11 "otp": "123456"
12 }'

If the OTP session expired you receive 400 otp_expired/invalid_reference — start a new collection with a new reference.

Handling responses

CodeMeaningWhat to do
202Accepted, pendingWait for webhook / poll
200Final result immediately (sandbox) or idempotent replayRead status
400 otp_requiredNeeds OTPShow OTP form, retry
400 invalid_otpWrong OTPAsk again
409 reference_conflictReference reused with different dataUse a unique reference