As a merchant, you define the checkout experience for your customers, which includes setting the timeout for API requests. It is critical to have a robust strategy when a timeout occurs to ensure you maintain an accurate view of your transaction. A timeout doesn’t automatically mean the payment or refund has failed. It simply means the connection was lost before a definitive response could be returned back to you.
We highly recommend you to use webhooks to get the transaction status combined with idempotency to safely retry the requests. If webhook is not feasible, you may poll for the transaction status by calling GET /transactions endpoint using the exponential back-off strategy.

Use webhooks to get the final status

The recommended approach to get the final status of a transaction or refund after a timeout is to listen for webhooks. Webhooks are automated messages sent to your system to provide real-time updates on events. When a transaction is successfully completed, we will send a webhook notification to your webhook endpoint. This is the recommended approach because it is:
  • Reliable: It provides the definitive, final state of the transaction directly from our system.
  • Efficient: It avoids the need for you to repeatedly poll our systems, reducing network traffic and load on your servers.
  • Asynchronous: Your system can respond to the event in its own time without holding a connection open.
We support webhooks for all transaction and refund events. When you receive a webhook, you can confidently update the order status in your system.

Idempotency: Safely retrying requests

It’s crucial to use idempotency when creating transactions to prevent duplicate actions in the case of a retry from your end in case of a timeout. An idempotent request is one that can be sent multiple times without changing the result beyond the initial execution. You can make your POST requests for transactions idempotent by providing a unique Idempotency-Key in the request header. Please note, if you retry a request with the same Idempotency-Key but a different request body, the request will fail with a 400 bad_request - Idempotency-Key already in use for a different request. to prevent accidental misuse.
Currently, only the POST /transactions endpoint supports idempotent requests.

Alternative strategy: Polling with exponential back-off

If implementing webhooks is not feasible, the alternative is to poll for the transaction or refund status. This involves making a call to the GET /transactions endpoint to perform a transaction search with a combination of external_identifier, created_at and amount. For refund, you may call GET /transactions/{transaction_id}/refunds endpoint. To avoid overwhelming our systems (and your own), you should use an exponential back-off strategy. It works by progressively increasing the delay between retries to give the system time to recover and process the transaction. We also recommend adding a small, random delay (known as jitter) to your back-off timing to prevent many requests all retrying at the same time.

Exception process

While our systems, combined with your webhook and polling strategies, are designed to resolve network timeout issues, some transactions may occasionally remain in an unknown state for an extended time. If a transaction’s status is still unknown after some time, you will need to initiate a manual reconciliation. We recommend you build a report to identify any transactions that have been in an unknown state for over 24 hours. For resolution, please raise a support ticket with our team, providing the details such as external_identifier, amount, and created_at for each transaction requiring investigation.

Common timeout scenarios

Scenario 1: Timeout resolved by webhook

This is the scenario where your system receives the final transaction status asynchronously from webhook before your internal timeout period expires. In this flow we see the following steps.
  1. Initiate transaction: Your server calls POST /transactions with a unique Idempotency-Key and external_identifier.
  2. API call times out. From your perspective, the transaction state is unknown. It’s crucial not to assume the payment has failed. Your system should continue waiting for a webhook to arrive.
  3. Webhook received before your timeout period ends: your server’s webhook listener receives a transaction.* event. Match the webhook event to the transaction using these fields: external_identifier, created_at and amount. Save the transaction status from the webhook.
  4. Before responding to the first API call, check the transaction status and display the final status from the webhook in the confirmation page (e.g. “Payment Successful”).

Scenario 2: Webhook received after the timeout period expires.

Here, the webhook is received after the timeout period expires. The merchant needs to track the transaction that timed out and if necessary perform a reversal. In this flow we see the following steps.
  1. Initiate transaction: Your server calls POST /transactions with a unique Idempotency-Key and external_identifier.
  2. API call times out. From your perspective, the transaction state is unknown. It’s crucial not to assume the payment has failed. Your system should continue waiting for a webhook to arrive.
  3. Customer retries: For this scenario, let’s assume the customer attempts to pay for the same order again by submitting a second transaction successfully. Here, it is critical that you generate a new Idempotency-Key (key-2). You may choose to reuse the same external_identifier as the original attempt or use a new external_identifier.
  4. Webhook for the first transaction arrives. Match the webhook event with the transaction by using these fields: external_identifier, created_at and amount.
    • Save the transaction status from the webhook.
    • If the transaction was approved, submit a void or refund to make the customer good again.
  5. Webhook for Transaction #2 arrives. Do nothing.

Scenario 3: Resolving Timeouts with Polling

This scenario outlines how to handle a transaction timeout when you cannot use webhooks. Your system must take on the responsibility of actively polling for the transaction’s final status to resolve the order and prevent duplicate charges. In this flow we see the following steps.
  1. Initiate transaction: Your server calls POST /transactions with a unique Idempotency-Key and external_identifier.
  2. API call times out. From your perspective, the transaction state is unknown. It’s crucial not to assume the payment has failed. Your system should continue waiting for a webhook to arrive.
  3. Customer retries: For this scenario, let’s assume the customer attempts to pay for the same order again by submitting a second transaction successfully. Here, it is critical that you generate a new Idempotency-Key (key-2). You may choose to reuse the same external_identifier as the original attempt or use a new external_identifier.
  4. Background Polling: Your background job, which is aware of the unknown state of the first transaction, periodically calls the GET /transactions endpoint to search for the transaction using a combination of external_identifier, created_at and amount to determine its final status.
    • Save the transaction status from the webhook.
    • Submit a void or refund to reverse the transaction if required.

Scenario 4: Handling refund, capture and void timeout using webhook

When timeout happens on merchant initiated requests such as a capture, refund, and void, you can listen to the webhook and match it using transaction_id and event.name (e.g. refund.succeeded or transaction.authorized). In this flow we see the following steps.
  1. Initiate transaction: Your server calls the void, capture, or refund APIs.
  2. API call times out. From your perspective, the action is unknown.
  3. Webhook received after some time. Your server’s webhook listener receives a transaction.* or refund.* event. Match the webhook event to the transaction using transaction_id and event.name.
  4. Save the status of capture, void or refund from the webhook (e.g. “Capture successful”).

Scenario 5: Handling refund, capture and void timeout with polling

When webhook is not an option, you must take the responsibility of actively polling for the final status of capture, void, and refund. In this flow we see the following steps.
  1. Initiate transaction: Your server calls the void, capture, or refund APIs.
  2. API call times out. From your perspective, the action is unknown.
  3. Background Polling: Your background job, which is aware of the unknown state of the transaction, periodically calls the GET /transactions endpoint to search for the transaction using a combination of external_identifier, created_at and amount to determine its final status. For refunds, use GET /transactions/{transactionId}/refunds endpoint.
  4. The loop continues until the transaction or refund status moves from an indeterminate state like “in-progress” to a final state, such as approved, declined, or failed.