Apple Pay plus Mada is the highest-converting checkout combination for Lebanese-founded brands selling to Saudi Arabia in 2026. This tutorial walks the full integration on a Next.js pages-router storefront with Tap as the acquirer, including the domain verification gotcha that breaks every first deploy.
By the end of this guide you will have a working Apple Pay and Mada checkout on a Next.js pages-router storefront, with Tap as the acquirer, the Apple Pay domain verified, the 3DS Mada challenge handled, and a refund path tested. This is the exact integration we ship for Lebanese-founded e-commerce brands selling into Saudi Arabia in 2026. The full path takes a senior engineer 4 to 6 days of focused work, and most of the failure modes are documented below so you do not waste two days debugging the same gotcha we did in 2024.
Key takeaways
- Apple Pay plus Mada is the highest-converting checkout combination for Saudi mobile traffic in 2026, lifting conversion 18 to 28 percent over Visa-only.
- The Apple Pay domain verification step breaks every first deploy. Get it right by hosting the association file before submitting to Apple.
- 3DS challenge on Mada is mandatory for new cards. Handle the redirect on the client and the webhook on the server, do not poll.
- Tap's sandbox covers 95 percent of test cases. Run the full sandbox suite before flipping production keys.
What is Apple Pay plus Mada and why does it convert so well for KSA in 2026?
Apple Pay plus Mada is the combination of Apple's tokenized wallet checkout and Saudi Arabia's national debit card network, presented together as the default mobile checkout method for Saudi shoppers. In 2026, 73 percent of Saudi e-commerce traffic comes from mobile, 62 percent of that mobile traffic is iOS, and Mada is the card on file for over 70 percent of Saudi consumers. When a storefront shows Apple Pay as the primary button with Mada as the underlying card method, the friction collapses from a six-field card form to a single Face ID confirmation, and conversion rates lift 18 to 28 percent versus a Visa-only checkout.
Why does Tap make this integration simpler than direct acquiring?
Tap (tap.company) sits between the storefront and the Saudi card networks as a partner merchant, which means a Lebanese-founded brand without a Saudi commercial registration can still process Mada and Apple Pay without setting up a Saudi entity. Tap also handles the Apple Pay merchant ID provisioning for you, which removes the most expensive step from a direct integration. The trade-off is 0.5 to 0.6 percent in fees versus a direct acquirer like Moyasar, which we covered in our HyperPay vs Moyasar vs Tap comparison. For a Lebanese brand under 50,000 USD per month in KSA revenue, Tap's simplicity wins.
The Apple Pay domain verification step is the single most-failed deploy moment in this integration. Get it right on day one and the rest is just code.
Step 1: How do you create the Tap merchant account?
Sign up at tap.company/sa, complete the partner-merchant onboarding (Lebanese passport, brand commercial registration if available, target market declaration), and request Mada plus Apple Pay activation in the dashboard. Tap's KYC for partner-merchant brands typically clears in 5 to 9 business days. Once the account is live, generate two API key pairs in the dashboard: sandbox (for development and testing) and production (for live charges). Store both pairs in environment variables. Do not commit them. The standard Lebanese mistake is to drop the production key into a .env.local that gets committed accidentally. Use the project's secret manager from day one.
Step 2: How do you verify the Apple Pay domain on Next.js?
This is the step that breaks every first deploy. Apple requires you to host a domain association file at exactly /.well-known/apple-developer-merchantid-domain-association before they will activate Apple Pay on your domain. On a Next.js static-export setup like a Voxire site, you do this by dropping the file into public/.well-known/apple-developer-merchantid-domain-association so it ships to the production CDN.
mkdir -p public/.well-known
# Paste the file content from Tap dashboard into:
nano public/.well-known/apple-developer-merchantid-domain-association
Deploy. Verify the file is reachable: curl https://yourdomain.com/.well-known/apple-developer-merchantid-domain-association should return the file content with a 200 status. Then submit the domain in the Tap dashboard. Apple takes 1 to 24 hours to verify. If you skip this step, the Apple Pay button will fail silently with "ApplePaySession is not available" in Safari console.
Step 3: How do you add the Apple Pay button on Next.js?
Create a checkout component that loads Tap's web SDK and renders the Apple Pay button only when ApplePaySession.canMakePayments() returns true. This gates the button so it only appears on Safari iOS where Apple Pay is supported.
import { useEffect, useRef } from "react";
import Script from "next/script";
export function ApplePayCheckout({ amount, currency = "SAR" }) {
const containerRef = useRef(null);
useEffect(() => {
if (typeof window === "undefined") return;
if (!window.ApplePaySession || !ApplePaySession.canMakePayments()) {
return; // Hide button on non-Safari
}
// Initialize Tap Apple Pay button
window.Tap.ApplePay.create({
container: containerRef.current,
amount,
currency,
onSuccess: async (token) => {
const res = await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token, amount, currency }),
});
const data = await res.json();
if (data.threeDsUrl) window.location.href = data.threeDsUrl;
else window.location.href = `/checkout/success?id=${data.chargeId}`;
},
onError: (err) => console.error("Apple Pay error:", err),
});
}, [amount, currency]);
return (
<>
<Script src="https://secure.gosell.io/js/sdk/tap.min.js" strategy="afterInteractive" />
<div ref={containerRef} id="apple-pay-button" />
</>
);
}
This pattern is what we ship in Next.js storefronts for Lebanese brands targeting GCC.
Step 4: How do you wire the server-side charge route?
Create a Next.js API route at pages/api/checkout.ts that takes the Apple Pay token from the client, calls Tap's create charge endpoint, and returns either a 3DS redirect URL or a success charge ID.
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") return res.status(405).end();
const { token, amount, currency } = req.body;
const tapRes = await fetch("https://api.tap.company/v2/charges", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TAP_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount,
currency,
source: { id: token.id },
customer: { first_name: token.cardholder_name || "Guest" },
redirect: { url: `${process.env.NEXT_PUBLIC_SITE_URL}/api/checkout/webhook` },
}),
});
const charge = await tapRes.json();
if (charge.status === "INITIATED" && charge.transaction?.url) {
return res.status(200).json({ threeDsUrl: charge.transaction.url });
}
if (charge.status === "CAPTURED") {
return res.status(200).json({ chargeId: charge.id });
}
return res.status(400).json({ error: charge.response?.message || "Charge failed" });
}
Keep the Tap secret key in a server-side environment variable. Never expose it to the client bundle.
Step 5: How do you handle the Mada 3DS challenge?
Mada requires 3DS authentication for new card-on-file transactions in 2026. When the create-charge response comes back with status INITIATED and a transaction.url, redirect the user to that URL. The user completes the bank-side OTP challenge, the bank redirects back to your webhook URL, and your /api/checkout/webhook route receives the final charge status. Do not poll the charge endpoint for status. Polling will rate-limit and miss the success window.
// pages/api/checkout/webhook.ts
export default async function webhook(req, res) {
const { tap_id } = req.query;
// Fetch the latest charge state
const charge = await fetch(`https://api.tap.company/v2/charges/${tap_id}`, {
headers: { Authorization: `Bearer ${process.env.TAP_SECRET_KEY}` },
}).then((r) => r.json());
if (charge.status === "CAPTURED") {
// Persist order, send confirmation email, etc.
return res.redirect(`/checkout/success?id=${charge.id}`);
}
return res.redirect(`/checkout/failure?reason=${charge.status}`);
}
Step 6: How do you test the full flow in Tap's sandbox?
Tap's sandbox supports five card scenarios you should test before flipping production keys. Use sandbox card 5123450000000008 (Mada, 3DS success). Use 4012001037141112 (Visa, 3DS challenge). Use 4012001036983698 (Visa, 3DS fail). Test a successful refund through the dashboard. Test a partial refund. Test a failed Apple Pay token (force-fail by using a sandbox device with no Apple Pay configured). For Apple Pay, sandbox testing requires a real Apple ID with sandbox Apple Pay configured, which is a 20-minute setup but mandatory. Voxire's SEO Lebanon team coordinates with the engineering team on these checks before launch.
Sources
- Tap Payments Web SDK Documentation
- Apple Pay on the Web Domain Verification
- Mada 3DS Implementation Guide
Ready to grow your business online?
Voxire ships production-grade Apple Pay and Mada integrations on Next.js storefronts for Lebanese brands every month. If you are stuck on domain verification, the 3DS redirect, or the webhook handler, we will ship it for you in 48 hours. Stuck on a step? We will do it for you in 48 hours.
Enjoying this article?
Enter your email and get a clean, formatted PDF of this article - free, no spam.
Voxire
E-commerce Development
High-converting Shopify and custom Next.js storefronts with Arabic support and regional payment gateways.
Learn more


