Pay-in
Collect money from a customer — hosted checkout or a direct charge.
There are two ways to collect a payment, and they exist for different situations.
Checkout creates a hosted payment page and gives you back a URL — redirect the customer to it, Orivanta Pay handles the payment-method UI, the customer lands back on your successUrl or cancelUrl when they're done. Use this when you don't want to build or maintain a payment form.
Charge debits a mobile money account directly, server-side, using a phone number you already collected. No redirect, no hosted page — but you're responsible for capturing the payer's details safely.
Both create a transaction you can poll for status, and both fire the same webhook events when the money actually moves.
Create a checkout session
POST /api/v1/pay-in/checkout
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Integer amount in the currency's base unit. Minimum 1. |
currency | string | Yes | ISO 4217 currency code, e.g. "XAF". |
merchantReference | string | No | Your own order/invoice id, echoed back untouched. Max 255 characters. |
description | string | No | Shown to the customer on the checkout page. Max 255 characters. |
successUrl | string | No | Where the customer lands after paying. Falls back to the application's default. |
cancelUrl | string | No | Where the customer lands if they back out. Falls back to the application's default. |
curl -X POST https://api.orivantapay.com/api/v1/pay-in/checkout \
-H "X-API-KEY: sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"merchantReference": "CMD-2024-001",
"amount": 5000,
"currency": "XAF",
"description": "Order #001",
"successUrl": "https://your-app.com/success",
"cancelUrl": "https://your-app.com/cancel"
}'Response — 201 Created:
{
"success": true,
"code": "SUCCESS",
"message": "Session de paiement créée.",
"data": {
"reference": "PI-A1B2C3D4E5F6",
"status": "PENDING",
"amount": 5000,
"currency": "XAF",
"description": "Order #001",
"paymentUrl": "https://checkout.orivantapay.com/pay/cs_9f1e2..."
},
"timestamp": "2026-09-05T10:00:00.000"
}paymentUrl is valid for 30 minutes. There's no idempotencyKey field on this endpoint — see Idempotency for which endpoint has one and why.
Create a direct charge
POST /api/v1/pay-in/charge
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Integer amount in the currency's base unit. Minimum 1. |
currency | string | Yes | ISO 4217 currency code, e.g. "XAF". |
paymentMethod | string | Yes | The rail's provider code — see Supported rails, e.g. "MTN_MOMO_CM". |
payerAccount | string | Yes | The phone number to debit. Max 100 characters. |
merchantReference | string | No | Your own order id, echoed back. |
description | string | No | Max 255 characters. |
payerName | string | No | Max 255 characters. |
payerEmail | string | No | Max 255 characters. |
idempotencyKey | string | No | Max 100 characters. A repeated request with the same key returns the original result instead of charging again. See Idempotency. |
curl -X POST https://api.orivantapay.com/api/v1/pay-in/charge \
-H "X-API-KEY: sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "XAF",
"paymentMethod": "MTN_MOMO_CM",
"payerAccount": "670000000",
"payerName": "Amina Ndongo",
"merchantReference": "CMD-2024-002",
"idempotencyKey": "cmd-2024-002-attempt-1"
}'Response — 201 Created:
{
"success": true,
"code": "SUCCESS",
"message": "Paiement initié.",
"data": {
"reference": "PI-B2C3D4E5F6A1",
"status": "PENDING",
"amount": 5000,
"currency": "XAF",
"description": null,
"paymentMethod": "MTN_MOMO_CM",
"payerAccount": "670000000",
"payerName": "Amina Ndongo",
"payerEmail": null
},
"timestamp": "2026-09-05T10:00:00.000"
}Checking status
GET /api/v1/pay-in/check_status/{reference}
Poll this if you're not relying on webhooks (or as a reconciliation check even if you are — the platform itself does both: it delivers a webhook the moment status changes, and separately re-polls every pending transaction as a backstop).
curl https://api.orivantapay.com/api/v1/pay-in/check_status/PI-A1B2C3D4E5F6 \
-H "X-API-KEY: sk_live_..."{
"success": true,
"code": "SUCCESS",
"message": "Statut récupéré.",
"data": {
"reference": "PI-A1B2C3D4E5F6",
"type": "CHECKOUT",
"status": "SUCCESS",
"amount": 5000,
"currency": "XAF",
"description": "Order #001",
"paymentMethod": "MTN_MOMO_CM",
"payerAccount": "670000000",
"payerName": null,
"payerEmail": null,
"paymentUrl": null,
"failureCode": null,
"failureReason": null
},
"timestamp": "2026-09-05T10:02:14.000"
}paymentUrl is only present while a checkout session is still PENDING. failureCode/failureReason are only present once a transaction has actually failed.
Status values
| Value | Meaning |
|---|---|
PENDING | Created, not yet resolved. The normal state right after you create it. |
SUCCESS | Money has moved. Terminal. |
FAILED | The provider declined it, or it timed out without a response. Terminal — check failureCode/failureReason. |
CANCELLED | The customer backed out of a hosted checkout session. Terminal. |
REFUNDED | Reserved for a future refund flow — no code path sets this value today. Don't build logic that expects to see it yet. |
There is no PROCESSING state — a transaction is PENDING right up until it resolves to one of the terminal states above.
Errors specific to this endpoint
| HTTP | code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | A field failed validation — check data for which one. |
| 400 | CURRENCY_MISMATCH | currency doesn't match what the application or provider expects. |
| 400 | AMOUNT_BELOW_MINIMUM / AMOUNT_ABOVE_MAXIMUM | Outside the rail's configured range — see Supported rails. |
| 404 | PROVIDER_NOT_FOUND | paymentMethod isn't a recognized, active provider code. |
| 404 | TRANSACTION_NOT_FOUND | The reference in a status check doesn't exist. |
See Errors for the full envelope shape and every code the API can return.