Skip to content

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-Signature containing t=<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_equals in PHP, timingSafeEqual in 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.