Your mada integration passed every sandbox test, then failed on real customers in three familiar ways: a saved-card charge the flow never supported, an order lost on the way back from the bank's authentication page, and a refund your store never heard about — or heard about twice. These are not random defects. They are three failure families with distinct mechanisms, and each one has a test that exposes it before go-live.
The short answer: the three failure families, and the test that exposes each one
Family one is tokenisation: your code assumes a tokenised mada card can be charged later the way a Visa token can, and discovers the supported transaction types only in production. Family two is the 3-D Secure redirect: the customer authenticates at the bank, returns — and your store has lost the order state, so a paid order sits in pending. Family three is refund webhooks: the event is missed, delivered twice, or accepted without verification, so refunds are doubled or never recorded. The exposing tests are equally specific: charge a real saved mada card, complete a real bank redirect on a phone, and refund a real order while watching your webhook log.
Why mada is not "just another card scheme" in your integration
Mada runs through your gateway, but the gateway's generic card documentation does not describe mada's limits. PayTabs' own mada documentation shows how specific the real rules are: mada payments are hosted by PayTabs — a redirect to its payment page, or the same page embedded in your checkout — in Saudi riyals only, and through mada cards you can perform Sale and Refund transactions, but no authorisation/capture or recurring transactions. A design copied from an international card flow is therefore broken for mada on that gateway before a line of your code is at fault. Read your gateway's mada page, not its card page. Our overview of web development in Saudi Arabia sets the wider build context.
Pitfall family 1 — Tokenisation: card-on-file and recurring charges that the flow does not support the way you assumed
Tokenisation itself is standard: the gateway replaces card details with a token your store can charge later. Moyasar's developer documentation, for example, describes its publishable key as the mechanism that lets a merchant tokenise card payments for later use in the backend. The pitfall is assuming the token inherits every behaviour of an international-scheme token.
On PayTabs, recurring transactions are not allowed using mada — so a subscription design that tokenises a mada card at signup and rebills it monthly fails at the first rebill, however clean the tokenisation step looked. Before designing card-on-file, list the exact operations your product needs — saved-card payment, rebill, pre-authorisation — and check each against your gateway's mada documentation, in writing. Where mada cannot carry the model, the product has to change: prompt for a fresh authenticated payment each cycle, or offer the recurring product only on schemes your gateway documents as supporting it.

Pitfall family 2 — 3-D Secure redirects: losing the order on the way back from the bank
Mada transactions require their authentication data. Checkout.com's support documentation states that its mada error 20030 occurs when the 3-D Secure and CVV information required for mada transactions is missing — integrations that treat 3DS as optional, or drop fields when a flow is embedded, manufacture this failure themselves.
The redirect is the fragile part. If the order state lives only in the browser session, an app switch on the phone or a slow bank page can strand a successfully authenticated payment as an abandoned order. Persist the order server-side before the redirect, keyed by the gateway's transaction reference; on return, fetch the transaction status from the gateway's API rather than trusting the return URL's parameters; and keep a webhook as the second, independent confirmation path. The browser's journey is evidence, never the record.

Pitfall family 3 — Refund webhooks: missed, duplicated and unverified refund events
A refund is a separate transaction against the original sale — PayTabs' developer documentation describes it as a follow-up transaction that requires the original transaction reference, which is why storing that reference at purchase time is non-negotiable. Your store usually learns the refund's final status from a webhook. Moyasar's webhook reference, for instance, lists distinct payment events including payment_paid and payment_refunded, alongside authorised and captured events — refund confirmation is its own event, not a flag on the original payment.
Three defects recur. Missed: your endpoint was down, the gateway's retries ended, and the refund exists at the gateway but not in your store — so reconcile refund status from the gateway for any refund older than your retry window. Duplicated: gateways retry deliveries, so an endpoint that processes each delivery as new can credit twice — deduplicate on the gateway's event or transaction identifier and make handlers idempotent. Unverified: an endpoint that trusts any POST to its URL will act on forged events — verify the secret or signature your gateway documents before changing any order state.

The order state machine that survives all three (authorised ≠ paid ≠ settled ≠ refunded)
Most integration pain traces back to one boolean: paid, true or false. Real payments move through states — created, authenticated, authorised, captured or sold, settled, refunded in part or in full — and gateways expose different subsets, as the PayTabs Sale-only model for mada shows. Model the states your gateway actually has, store the gateway reference beside each transition, and let only verified sources move an order: the gateway API response, or a verified webhook. A browser return can move an order to "checking", never to "paid".
This discipline also keeps the neighbouring jobs clean. The accounting view of these same movements — matching settlements to your books — is covered in our guide to reconciling mada and card settlements, and what a shopper-facing decline means on a live store is covered in our mada checkout decline troubleshooting guide. This article stays on the build layer between them.
Co-badged cards and scheme routing: the quiet fourth pitfall
Many mada cards are co-badged with an international scheme, so one physical card can travel down either route — and the route decides which rules apply: the transaction types available, the authentication flow, and how a refund behaves. Bugs appear when the integration assumes the route instead of recording it. Log the scheme the gateway actually processed for every transaction, and test with a co-badged card specifically, choosing each route your gateway exposes.
Sandbox lies: the production-only test protocol (real cards, real redirects, real refunds)
Sandbox cards never authenticate at a real bank, never hit a scheme limit, and never test your return URLs under real conditions. Before go-live, run a controlled production protocol with real, low-value transactions: pay with a mada card and complete the bank authentication on a phone, including an app switch mid-flow; abandon one authentication and confirm the order expires cleanly; save a card and attempt the later charge your design assumes; refund one order in full and one in part — PayTabs documents full and partial mada refunds from its dashboard — and watch each webhook arrive, be verified, and be deduplicated by replaying it. Record the gateway references for every step. Wider build work can be scoped through our custom web application development service.
Logging and evidence: what to capture so a failure is diagnosable in one look
For every payment attempt, log the order ID, the gateway transaction reference, the scheme actually processed, the transaction type, the 3DS outcome, and every webhook received with its verification result. With that record, support can separate the three families in one look. Without it, every failure becomes the same "payment failed" ticket. Refund timing limits are gateway-specific: Checkout.com's support documentation states that on its flow a mada refund attempted after a 30-day period fails with error 20030, with a direct bank transfer or a manual refund via the acquirer as its stated fallbacks — check your own gateway's documented window rather than assuming that figure travels.
Common mistakes
The recurring mistakes are assuming the generic card documentation describes mada, designing recurring billing on a token before checking the gateway's mada transaction types, keeping order state in the browser across the bank redirect, trusting return-URL parameters as proof of payment, building webhook handlers that are neither verified nor idempotent, and testing only in sandbox. Each maps to one family above — and each is cheaper to find in your test protocol than in a customer's complaint.
FAQ
Why does my mada integration pass sandbox but fail in production?
Sandbox cards skip real bank authentication, scheme transaction-type limits and real redirect behaviour. The failures that define mada integrations — an unsupported recurring charge, a lost 3DS return, a missed refund webhook — only appear with real cards, real banks and real refunds.
Can mada cards be used for recurring payments?
It depends on your gateway, and you must check its mada documentation rather than its general card pages. PayTabs' mada documentation states that recurring transactions are not allowed using mada on PayTabs, and that mada supports Sale and Refund transactions only.
Why did my order stay pending after the customer paid?
Usually because the store relied on the browser returning from the bank redirect, and that return was interrupted or its parameters were never confirmed. Fetch the transaction status from the gateway's API on return, and treat a verified webhook as the independent confirmation path.
What should I do if a refund webhook never arrives?
Do not assume the refund failed. Check the refund's status in the gateway — a refund is a follow-up transaction against the original sale — and reconcile any refund your store has no verified event for. Then fix the endpoint and its retry handling so the next one is captured.
How do I stop duplicate refunds?
Make webhook handling idempotent. Gateways retry deliveries, so deduplicate on the gateway's event or transaction identifier, verify the event before acting on it, and record the refund against the original transaction reference exactly once.
Get your Saudi payment integration audited and hardened (CTA)
If your store shows any of the three families, the fix is a build-layer audit, not another sandbox run. WebStackRank's ecommerce web development team audits the integration against your gateway's own mada documentation, rebuilds the order state machine and webhook handling, and proves it with a real-card production protocol. See our Saudi Arabia hub for how we work in the Kingdom, or our ecommerce website development in Dubai for UAE builds.