Logo GenukaGenuka Pay
Guides

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 linkDirect API
You buildNothingThe payment form
The payerIs sent to a Genuka Pay pageStays on your site
Payer's phone numberCollected on the pageYou collect it
Operator choiceMade on the pageYours to handle
Bank cardIncludedNot available
SetupOne API callOne API call + state handling

Start with the payment link. Move to the direct API when you want to own the experience end to end.

You create a checkout session, redirect the customer to the URL returned, and Genuka Pay handles the rest.

POST /api/v1/checkout
ParameterTypeRequiredDescription
amountnumberYesAmount to collect
currencystringYesXAF or XOF
descriptionstringNoShown to the payer
payer_namestringNoPre-fills the page
payer_emailstringNoPre-fills the page
payer_phonestringNoE.164 format, e.g. +237677001122
return_urlstringNoWhere to return after a successful payment
cancel_urlstringNoWhere to return after an abandoned payment
expires_atdateNoLink expiry. Must be in the future.
metadataobjectNoYour 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" }
  }'
Response — 201
{
  "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_urlWhat to do with it
Non-nullA payment page: redirect the payer. This is the case for bank card, and for Orange, Wave and Djamo in Ivory Coast.
nullThe 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:

  1. the transaction.success / transaction.failed webhook — the source of truth;
  2. 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.

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