Payment failures are normal in recurring billing. This guide shows you how to detect failures, communicate with customers, and implement a retry strategy that recovers revenue without creating a poor customer experience.
Monnify only sends a webhook notification when a transaction is successful. The event type is SUCCESSFUL_TRANSACTION. There is no webhook for failed or pending transactions.
To detect a failure, your server must actively check the transaction status after the expected payment window:
SUCCESSFUL_TRANSACTION webhook on your configured webhook URL.paymentStatus field in the response. A status of PAID means the payment succeeded. Any other status (including PENDING or FAILED) means the transaction did not complete.Never rely solely on client-side callbacks to determine payment outcome. Always confirm the status via the server-side transaction status API before granting or revoking access.
Not every failure should trigger an immediate retry. Classify failures before acting:
| Failure Reason | Retryable? | Recommended Action |
|---|---|---|
| Insufficient funds | Yes | Retry after 4–8 hours; notify customer to fund account. |
| Bank downtime / timeout | Yes | Retry after 1–6 hours. |
| Card expired | No | Do not retry. Prompt customer to add a new card. |
| Mandate limit exceeded | No | A new mandate is needed with the right amount. |
| Account restricted | No | Customer must resolve with their bank before retrying. |
Send a notification as soon as you confirm the failure via the transaction status check. Timely communication significantly improves recovery rates.
Space out retry notifications so customers do not feel harassed. A common pattern: notify immediately, then at 24h, then at 72h, then a final notice before service suspension.
For retryable failures (e.g. insufficient funds, bank downtime), implement a retry schedule on your server. The handler below covers both card token charges and direct debit mandate debits:
setTimeout is for illustration only. In production, use a durable job queue (Bull, Agenda, AWS SQS, etc.) so retries survive server restarts and can be monitored.
After each retry attempt (whether from your scheduler or a customer paying manually), always verify the transaction status server-side before restoring access.
Only restore access when paymentStatus === "PAID" and amountPaid matches the expected amount. See Verify Transactions for the full response reference.
For Direct Debit / Mandates, Monnify sends a MANDATE_UPDATE webhook whenever the mandate's status changes (e.g. activated, suspended, cancelled). A failed debit attempt will not always trigger a webhook. To confirm whether a debit succeeded, poll the Get Debit Status endpoint using the debit reference returned when you initiated the debit.
If the debit did not succeed:
Banks may have restrictions on how frequently a mandate can be debited, Monnify currently sets limit to 2 per day. Check the specific terms of the mandate type to avoid unnecessary bank-side rejections.
Rate this page
How helpful is the content on this page?