Payments API
Webhooks (outbound to your server)
Configure an HTTPS webhook URL on each API client in the portal (Developers → API credentials). MojoPay POSTs events only for resources that client created to your URL (for example https://your-store.example.com/webhooks/mojopay — not a path on the MojoPay API host). Portal pay links and portal payouts do not send webhooks. Events use this envelope:
id, type, api_version, created, data.object.
Fee and tax line-item breakdowns are not included in webhook payloads. When collection fees apply, API create/status responses include fee_amount, fee_mode, amount_payable, and merchant_settlement_amount; the portal pay-link UI shows the same breakdown. When payout fees apply, payout responses include fee_amount and amount_debited.
Signature verification:
- Header:
MojoPay-Signaturecontainingt=<unix>,v1=<hex>(lowercase hex, 64 characters). - Digest: HMAC-SHA256 over
{t}.{raw_body}with webhook secret — use the exact raw body bytes received. - Reject timestamps outside ±300 seconds (5 minutes) of your server time.
- Compare signatures with a constant-time function (
hash_equalsin PHP,timingSafeEqualin Node). - See Integration quick start — Verify webhook signatures for copy-paste examples.
Example event:
{
"id": "EVT_01JQXYZABCDEFGHIJKLMNOPQR",
"type": "collection.succeeded",
"api_version": "2026-04-15",
"created": "2026-04-15T10:00:00Z",
"data": {
"object": {
"id": "CL_9K2ML0ABCDEF",
"object": "collection",
"status": "succeeded",
"currency": "GHS",
"amount": "200.00",
"merchant_reference": "ORD-2026-001",
"mobile": "233544338841",
"payment_rail": "mobile_money",
"provider": "MTN"
}
}
}
Event types (v1.0 — emitted today):
collection.processing|succeeded|failed,
checkout_session.processing|succeeded|failed,
payout.processing|succeeded|failed,
mandate.processing|succeeded|cancelled,
mandate_debit.processing|succeeded|failed.
Subscription lifecycle events (subscription.created, subscription.active, subscription.cancelled, subscription.failed, subscription.charge_failed) are emitted when mandates are linked to subscription plans.
Delivery: treat webhook delivery as at-least-once. The same logical event may be retried; use the event id to deduplicate before updating order state. Always verify MojoPay-Signature first, then process.
Retries: up to 5 delivery attempts in total. Any non-2xx response, timeout (10s), or connection error is retried after 10s, 60s, 5m, and 15m, so the last attempt happens about 21 minutes after the first. There is no self-serve replay after that; recover missed events with the status endpoints (GET /v1/transactions/verify/{merchant_reference}, GET /v1/payouts/{merchant_reference}, GET /v1/mandate_debits/status) or ask MojoPay support.
Final statuses: succeeded, failed, cancelled, and expired are terminal. processing is not final.