Choosing your integration
Hosted payment link or direct API — what each one asks of you, and what it leaves you to build.
Choosing your integration
Two ways to collect with Genuka Pay. Both use the same accounts, the same rates and the same webhooks — what changes is how much of the interface you write.
| Payment link | Direct API | |
|---|---|---|
| You build | Nothing | The payment form |
| The payer | Is sent to a Genuka Pay page | Stays on your site |
| Payer's phone number | Collected on the page | You collect it |
| Operator choice | Made on the page | Yours to handle |
| Bank card | Included | Not available |
| Setup | One API call | One API call + state handling |
Start with the payment link. Move to the direct API when you want to own the experience end to end.
Hosted payment link
You create a checkout session, redirect the customer to the URL returned, and Genuka Pay handles the rest.
POST /api/v1/checkout| Parameter | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Amount to collect |
currency | string | Yes | XAF or XOF |
description | string | No | Shown to the payer |
payer_name | string | No | Pre-fills the page |
payer_email | string | No | Pre-fills the page |
payer_phone | string | No | E.164 format, e.g. +237677001122 |
return_url | string | No | Where to return after a successful payment |
cancel_url | string | No | Where to return after an abandoned payment |
expires_at | date | No | Link expiry. Must be in the future. |
metadata | object | No | Your own references |
curl -X POST "{{BASE_URL}}/api/v1/checkout" \
-H "X-Public-Key: YOUR_PUBLIC_KEY" \
-H "X-Timestamp: UNIX_TIMESTAMP" \
-H "X-Signature: HMAC_SHA256_SIGNATURE" \
-H "Content-Type: application/json" \
-d '{
"amount": 10000,
"currency": "XOF",
"description": "Order #10001",
"return_url": "https://shop.example.com/thanks",
"cancel_url": "https://shop.example.com/cart",
"metadata": { "order_id": "10001" }
}'{
"data": {
"id": "01HR...",
"token": "chk_01HR...",
"amount": 10000,
"currency": "XOF",
"status": "CREATED",
"urls": {
"checkout": "https://api-pay.genuka.com/checkout/chk_01HR...",
"return": "https://shop.example.com/thanks",
"cancel": "https://shop.example.com/cart"
},
"expires_at": null
}
}Redirect the customer to urls.checkout. Read a session's state at any time:
GET /api/v1/checkout/{token}Do not rely on the browser coming back
return_url is for the customer's convenience, not for your books: they may close the tab before reaching it. A payment is confirmed only by the transaction.success webhook.
Direct API
You collect the payer's phone number, create the payment, and the operator pushes a confirmation prompt to the customer's handset.
curl -X POST "{{BASE_URL}}/api/v1/payments" \
-H "X-Public-Key: YOUR_PUBLIC_KEY" \
-H "X-Timestamp: UNIX_TIMESTAMP" \
-H "X-Signature: HMAC_SHA256_SIGNATURE" \
-H "Content-Type: application/json" \
-d '{
"amount": 10000,
"currency": "XOF",
"payer_phone": "+2250700000001",
"external_id": "order_10001",
"return_url": "https://mystore.com/orders/10001/thanks"
}'Full field and response reference: Payments API.
What the payer does next
The response carries redirect_url, which tells you what to do with the payer:
redirect_url | What to do with it |
|---|---|
Non-null | A payment page: redirect the payer. This is the case for bank card, and for Orange, Wave and Djamo in Ivory Coast. |
null | The customer approves on their handset: display payment_token and wait. |
MTN and Moov in Ivory Coast
These two confirm over USSD but still return a URL in payment_token — a waiting page, not a payment page. That is why redirect_url exists and why it is the field to test: the token holds a URL either way and tells the two apart for nobody. Show your own waiting screen and track the status instead.
A redirect needs an address to come back to
As soon as redirect_url can be non-null, send return_url — and cancel_url if failure has its own page — when you create the payment. The payer leaves your site for the operator; without those addresses Genuka Pay has nowhere to bring them back to, and they end up on the Genuka Pay homepage. The parameters appended to your URL are detailed in Payments API.
Tracking the payment
In order of preference:
- the
transaction.success/transaction.failedwebhook — the source of truth; GET /api/v1/payments/status/{track_id}— to refresh a waiting screen or reconcile.
A payment stays in PROCESSING until the customer approves. Never read elapsed time as failure: only the final status settles it.
What about collection links?
Reusable collection links (fixed or open amount, shared over WhatsApp or as a QR code) are created from the dashboard, under Collects. They need no integration at all.
Next
How is this guide?
Last updated on