Digital wallets have become the default for mobile checkout. If your payment flow doesn't support Apple Pay and Google Pay, you're leaving conversion on the table—especially on iOS and Android respectively. In this guide, we'll walk through how to integrate both wallets into your Stripe payment flow, verify your domain correctly, and test on real devices so you ship with confidence.
Payment Element shows them automatically
The easiest path to supporting Apple Pay and Google Pay is Stripe's Payment Element. It detects the user's device and browser, then surfaces the appropriate wallet option automatically. You don't need separate conditionals or feature flags.
Here's a minimal example. First, create a payment intent on your backend:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
export async function POST(req: Request) {
const { amount, currency } = await req.json();
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency,
automatic_payment_methods: {
enabled: true,
},
});
return Response.json({
clientSecret: paymentIntent.client_secret,
});
}
On the frontend, render the Payment Element. Stripe automatically includes Apple Pay on iOS Safari and Google Pay on Android Chrome (or any browser where the user has configured a payment method):
import { loadStripe } from '@stripe/stripe-js';
import {
Elements,
PaymentElement,
useStripe,
useElements,
} from '@stripe/react-stripe-js';
import { useEffect, useState } from 'react';
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);
export function CheckoutForm() {
const stripe = useStripe();
const elements = useElements();
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!stripe || !elements) {
return;
}
setLoading(true);
const { error: submitError } = await elements.submit();
if (submitError) {
setError(submitError.message || 'Payment failed');
setLoading(false);
return;
}
const res = await fetch('/api/create-payment-intent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: 5000, currency: 'usd' }),
});
const { clientSecret } = await res.json();
const { error: confirmError } = await stripe.confirmPayment({
elements,
clientSecret,
confirmParams: {
return_url: `${window.location.origin}/success`,
},
});
if (confirmError) {
setError(confirmError.message || 'Confirmation failed');
}
setLoading(false);
};
return (
{error && <div>{error}</div>}
{loading ? 'Processing...' : 'Pay'}
);
}
export default function CheckoutPage() {
return (
);
}
The Payment Element handles browser detection and renders the wallet button if the user's device supports it. No extra code needed on your side. The button appears prominently—often as the first payment option—which drives higher adoption.
Verify your domain for Apple Pay
Apple Pay requires domain verification. Without it, Apple Pay simply won't appear in your checkout, even on a real iPhone. Stripe handles the certificate behind the scenes, but you must add a specific file to your domain's root.
Go to your Stripe Dashboard under Settings → Apple Pay. You'll see a section titled "Apple Pay domain registration." Stripe gives you a file name and content. For example:
File:
.well-known/apple-developer-merchantid-domain-associationContent: A long hex string provided by Stripe
Add this file to your domain's public root. If you're using Next.js, place it in the public folder under public/.well-known/apple-developer-merchantid-domain-association:
// Next.js folder structure
public/
.well-known/
apple-developer-merchantid-domain-association
If you're serving from a subdomain (e.g., checkout.example.com), register that specific subdomain with Stripe and deploy the file there. After uploading, Stripe automatically verifies. It usually takes a few minutes, but sometimes up to an hour. Once verified, a green checkmark appears in the Dashboard.
Common issue: forgetting to register each domain variant separately. If you run staging at staging.example.com and production at example.com, register both. Apple Pay won't work on unregistered domains.
Test on real devices
Simulator and emulator support for Apple Pay and Google Pay is limited. You need a real iPhone and Android device to properly validate the flow.
Testing Apple Pay on iPhone:
- Add a test card to your Apple Wallet. On a real device, open Wallet, tap the plus icon, and add a card manually using your Stripe test card number (e.g., 4242 4242 4242 4242).
- Set your domain correctly in your test environment (e.g.,
localhostwon't work; use a real domain or ngrok tunnel with proper TLS). - Navigate to your checkout on Safari from the iPhone.
- Tap the Payment Element. Apple Pay should appear as an option.
- Tap it, authenticate with Face ID or Touch ID, and complete the transaction.
Testing Google Pay on Android:
- On your Android device, open Google Pay and add a test card (same 4242 4242 4242 4242 number works).
- Open your checkout URL in Chrome.
- Tap the Payment Element. Google Pay appears if your device and browser support it.
- Tap it, authenticate if prompted, and complete the flow.
Pro tip: use ngrok or a service like Vercel's preview deployments to generate a real URL with TLS. Both Apple and Google require HTTPS; they won't activate their wallets over HTTP.
In your Stripe test mode, watch the Dashboard in real time. Each transaction appears instantly. Use Stripe's test card numbers and expiry dates (any future date works). For 3D Secure prompts, Stripe's test mode simulates the flow—just follow the on-screen instructions.
Great for mobile conversion
Mobile checkout abandonment is notoriously high. One-tap payment methods like Apple Pay and Google Pay reduce friction dramatically. Users don't type card details; they authenticate once and pay. The entire flow takes three seconds.
Real-world impact: teams we've worked with see 20–40% higher completion rates on mobile once they add wallet support. Apple Pay especially dominates on iOS—when it's available, it often becomes the preferred payment method.
From an engineering perspective, this also reduces your PCI compliance burden. Stripe handles the sensitive card data; your backend never sees it. You only manage the payment intent and the redirect after confirmation.
Strategically, you want wallet options above the fold in your checkout UI. The Payment Element respects this: it renders Apple Pay or Google Pay at the top by default, followed by card entry or other methods. This ordering alone drives higher adoption.
Common mistakes
- Forgetting HTTPS or proper domain setup: Apple Pay and Google Pay require a valid, trusted certificate. Localhost with self-signed certificates won't trigger wallet UI. Always test on a real domain with proper TLS.
- Not verifying your domain with Apple Pay: The most common blocker. Even if everything else is correct, if the
.well-knownfile isn't present or verified, Apple Pay silently doesn't appear. - Testing only on simulator: Emulators and simulators often skip wallet rendering. Always test on real hardware before shipping.
- Misconfiguring automatic_payment_methods: If you set
automatic_payment_methods: { enabled: false }, wallet buttons won't show. Keep it enabled or explicitly list payment method types. - Not handling the redirect correctly: Apple Pay and Google Pay often redirect after authentication. Make sure your success and error pages are reachable and properly handle the client secret in the URL.
- Ignoring regional differences: Apple Pay is available in many countries but not all. Google Pay likewise has regional restrictions. Check Stripe's documentation for supported regions before promising wallet support in all markets.
Key takeaways
- Payment Element automatically renders Apple Pay on iOS and Google Pay on Android—no conditional logic needed.
- Apple Pay requires domain verification via a
.well-knownfile; Google Pay does not, but both require HTTPS and a real domain. - Always test on real devices. Simulators and emulators don't reliably render wallet UI.
- One-tap checkout dramatically improves mobile conversion and reduces PCI scope. Ship it early in your mobile flow.
- Wallet adoption compounds: once users see the option, they prefer it over card entry. Prioritize visibility in your UI layout.






Comments
Loading comments…