La documentation technique est en anglais, pour vous et pour votre agent IA.

Payments API

A payment charges one customer once. You create it from your server; the customer approves it on their phone (or on the operator's page); you learn the result by webhook or by fetching it.

Lifecycle

pending ──► succeeded
   │
   ├──────► failed
   │
   └──────► requires_review ──► succeeded | failed
  • pending: waiting for the customer or the operator.
  • succeeded: money received. The merchant's share is added to your balance.
  • failed: nothing was charged. failure.code says why.
  • requires_review: rare. The provider's answer did not add up (amount mismatch, late confirmation, a provider with no status API). Our team checks with the provider and closes it as succeeded or failed; you get the matching webhook.

Create a payment

POST /v1/payments

Field Type Required Description
amount integer yes Smallest currency unit. FCFA amounts are whole francs, multiples of 5.
currency string yes XAF, XOF or CDF. XAF and XOF are both FCFA and accepted by either zone's operators.
operator string yes* e.g. mtn_cm. *Optional for Cameroonian numbers. See operators.
phone string for Mobile Money Any format: 670000000, +237 6 70 00 00 00, 237670000000.
reference string no Your order id. Searchable, and sent back in webhooks.
description string no Shown on some operators' prompts.
fee_bearer string no merchant (default: the fee is taken from the amount) or customer (the fee is added to what the customer pays).
customer_email, customer_name string no For your records.
return_url string no Where redirect operators send the customer afterwards.
metadata object no Up to 20 string keys, sent back as is.

Amounts and fees

  • amount: what you asked for.
  • charge: what the customer pays.
  • fee: what Patronise Dev keeps (operator fee + 1%).
  • net: what lands in your balance.

With fee_bearer: "merchant", charge = amount and net = amount − fee. With fee_bearer: "customer", the charge is raised so that net = amount. Use GET /v1/fees/quote to show the total before you create the payment.

Retrieve a payment

GET /v1/payments/{id} returns the payment. If it is still pending, we check with the provider before answering, so polling every 3–5 seconds is fine.

List payments

GET /v1/payments?status=succeeded&reference=order-1042&limit=20&starting_after=pay_…

Results are newest first. When has_more is true, pass the last id as starting_after to get the next page.

Idempotency

Send Idempotency-Key: <unique string> on every POST. If the network fails, send the exact same request with the same key: you get the first answer back (with the header Idempotent-Replayed: true) and no second payment is created. Reusing a key with a different body returns 409 idempotency_key_reused. Keys are kept 24 hours.

Failure codes

Code Meaning
INSUFFICIENT_BALANCE Not enough money in the customer's wallet.
PAYMENT_NOT_APPROVED The customer declined or did not enter their PIN.
PAYER_NOT_FOUND The number has no Mobile Money account with that operator.
PAYER_LIMIT_REACHED The customer's wallet limit is reached.
EXPIRED / expired The customer never confirmed.
provider_unavailable No provider could take the payment. Retry later.
provider_rejected The operator refused the request.

Besoin d’aide pour l’intégrer ?