Skip to main content
The questions on this page come directly from real merchant conversations. The single most important rule:
A redirect only means the user’s browser came back to your site. It is never proof of payment. The authoritative source of a payment result is the Webhook notification, with the Order Inquiry API as the reconciliation fallback.

The user was redirected back to my site — is the payment successful?

Not necessarily. successRedirectUrl is UI navigation only. Confirm the result through either of these, in order of preference:
  1. Webhook — Waffo posts the payment result to your notifyUrl when the payment completes or fails.
  2. Order Inquiry — call /api/v1/order/inquiry to actively query the order when you need to reconcile.
Mark an order as paid only when one of these reports a final success.

The user closed the browser before being redirected — did the payment fail?

No. The payment may still complete on the payment method’s side. The webhook fires regardless of what the user’s browser does. If in doubt, call Order Inquiry.

What is the difference between webUrl and successRedirectUrl?

They travel in opposite directions: failedRedirectUrl and cancelRedirectUrl cover the failure and user-cancellation returns in the same way.

What does each orderStatus value mean?

Only PAY_SUCCESS and ORDER_CLOSE are final states.

The order timed out — TIMEOUT_CLOSE vs TIMEOUT_CHANNEL_CLOSE?

Both are failure codes on orders closed by timeout. They tell you where the user dropped off: Use the split to analyse your conversion funnel: TIMEOUT_CLOSE means drop-off before authorization started, TIMEOUT_CHANNEL_CLOSE means the flow stalled after it started. When Waffo cannot determine whether a request was initiated, the order falls back to TIMEOUT_CLOSE. The distinction applies to all payment methods; older orders keep their original code.

Bank transfer / over-the-counter payments: why two notifications?

Payment methods where the user pays outside the checkout session — bank transfer / virtual account (VA) and over-the-counter (OTC) — report in two stages:
  1. Acceptance — after order creation you receive an acceptance result. This is not a final payment status: the user has not paid yet.
  2. Payment result — the final notification arrives only after the user completes the payment at their bank, ATM, or the counter.
The gap between the two can be long, and the automatic close time differs per payment method. Treat only PAY_SUCCESS / ORDER_CLOSE as final, exactly as for synchronous methods.

I did not receive a webhook. What should I check?

  1. notifyUrl must be HTTPS (TLS 1.2+) and reachable from the public internet.
  2. Verify the X-SIGNATURE header with the Waffo public key — and make sure your endpoint isn’t rejecting valid requests.
  3. Respond with {"message": "success"} after processing. If Waffo receives failed or unknown, Waffo retries the notification on a back-off schedule spread over the following day — see Webhook retry for the schedule.
  4. While investigating, use Order Inquiry to fetch the order state so your business flow is not blocked.

When should I call Order Inquiry instead of waiting?

  • As a reconciliation fallback when no webhook has arrived within your expected window.
  • When you receive an Exxxxx unknown error on a request — retry the inquiry until you get a final PAY_SUCCESS or ORDER_CLOSE.
  • In batch reconciliation jobs.
Order Inquiry and webhooks return the same order state; they never disagree on a final status.

The order creation failed. Will I still get a notification?

Yes. PAYMENT_NOTIFICATION is sent when a payment completes or fails — a failed order is notified exactly like a successful one, so do not build your flow on the synchronous create response alone. The notification is also richer than that response: So the failure reason for a rejected order is available in the notification and through Order Inquiry, not in the create response. Match the notification to your own record with paymentRequestId or merchantOrderId; both are echoed back. Acknowledge it the same way as any other webhook — see event type list.

How long can the customer still pay after I create the order?

orderExpiredAt is optional. If you omit it, the default expiry time from your merchant contract applies. What it controls is narrower than it looks:
  • It governs the window in which the customer can submit the order on the checkout page (must be UTC+0).
  • Once the order has been submitted to the payment method, the expiry is governed by that payment method, not by your value. A small number of payment methods accept a channel-side expiry time from the merchant — the Payin page in the Merchant Portal shows the expiry behavior per payment method.
  • Passing a time that has already elapsed is rejected at creation with A0034 (“the order expiry time provided by the merchant has expired”).
The practical consequence: your close time and the payment method’s close time are not the same clock, which is why a payment can still land after Waffo has closed the order — see the last section on this page for how to handle that case.

An offline or bank-transfer order has been pending for hours. Should I cancel it?

No. For over-the-counter (OTC) and virtual-account / bank-transfer payments the customer pays outside the checkout session, so a long pending period is normal rather than a symptom of failure — and the automatic close time differs per payment method. Do not infer failure from elapsed time, and do not release or void the purchase on your side while the order is still open: wait for the final notification, or poll Order Inquiry. Only PAY_SUCCESS and ORDER_CLOSE are final.

I sent the same request twice at the same time and got an error. Did either go through?

A concurrency rejection is protection, not a business failure. Creating the same order from two requests in parallel returns A0040 (“the order cannot be created concurrently”) instead of creating two orders. Handle it like this:
  1. Serialize calls per order — one in-flight request per order at a time, rather than parallel retries.
  2. Resolve the state, do not guess it. Call Order Inquiry to see what the order actually is before deciding anything.
  3. When you retry the original request, reuse the same paymentRequestId — it is the idempotency key. See the error code quick reference for A0011 and for E-class unknown-status handling.

The user says they paid, but the order shows failed or timed out

Occasionally a payment completes on the payment method’s side after the Waffo order has already been closed as timed out (“failed first, succeeded later”). How to handle it:
  • Never fulfil based on a user’s payment screenshot. Fulfil only when a webhook or Order Inquiry reports PAY_SUCCESS.
  • Report the case to Waffo support with the order IDs. Waffo reconciles the order with the payment method side; the payment is then either confirmed to you or the debited amount is returned to the user.
  • The user may be asked for additional details to locate the transaction, so collect what they can share — payment time, amount, and the payment method’s transaction reference.