Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stripe Apple Pay — Elements reference

A minimal Stripe Elements checkout that renders Apple Pay in Chrome as well as Safari, plus a diagnostics panel that reports which requirements are met.

Express Checkout Element (wallet buttons) and Payment Element (card form) share one elements instance — the standard Elements shape. No Stripe-hosted Checkout.

Install

npm install
cp .env.example .env    # add your Stripe test keys

Run

Apple Pay requires public HTTPS, so localhost will not show the button. You need a tunnel and a registered domain:

npm start              # terminal 1
ngrok http 4242        # terminal 2

Then register the ngrok host as a payment method domain, either way:

npm run register-domain -- <your-host>.ngrok-free.app

…or in the Dashboard, on the Payment method domains screen:

https://dashboard.stripe.com/ACCOUNT_ID/test/settings/payment_method_domains?enabled=true

Swap ACCOUNT_ID for your acct_… id; drop /test/ for live mode. Click Add a new domain, paste the ngrok host (no https://, no trailing slash), and save.

Open the ngrok https URL. The diagnostics panel at the bottom of the page reports every requirement and what Stripe actually offered the browser.

npm run check-account     # is Apple Pay enabled on the account?

The ngrok hostname changes every restart, so re-run register-domain each session.

Requirements for Apple Pay to render

Four conditions must all hold. Each one fails silently — nothing thrown, nothing logged, the button simply isn't there.

1. Apple Pay enabled on the Stripe account

Cheapest to verify, so start here — one command:

$ npm run check-account

-- Default (pmc_…)  [DEFAULT] active=true
   apple_pay   : available=false  preference=off      <- not enabled
   google_pay  : available=false  preference=off
   link        : available=true   preference=on
   card        : available=true   preference=on

Enable in Dashboard → Settings → Payment methods. Once on, apple_pay reads available=true preference=on.

This is account-wide, so when it's off no browser shows the button — Safari included, even on hardware with a verified card in Wallet. That was the case while building this demo, which is why it's listed first.

2. paymentMethods.applePay set to 'always' on non-Safari desktop

From Stripe's supported-browsers table:

Apple Pay on non-Safari desktop browsers is only supported when paymentMethods.applePay is set to always.

Stripe's default renders Apple Pay only where it can confirm the customer has a card provisioned, which it can only do in Safari. On desktop Chrome, Edge, Firefox and Opera it renders nothing.

const expressCheckoutElement = elements.create('expressCheckout', {
  paymentMethods: {
    applePay: 'always',   // without this, Chrome shows no Apple Pay button
  },
});

A/B it live. The toggle at the top of the payment card switches this flag and reloads — the Apple Pay button appears and disappears with nothing else changing. Also settable via ?applePay=auto|always|never. Measured on Chrome 151 / macOS:

Mode Card in the Mac's Wallet Result
'auto' (Stripe default) present & verified applePay: false
'always' present & verified applePay: true

In Chrome the wallet card is irrelevant — under auto, Stripe declines to offer Apple Pay on non-Safari desktop regardless of what's provisioned. Per Stripe's testing docs, 'always' also "remove[s] the requirement for an active saved card", so a customer with an empty wallet still gets the button and completes via the iPhone handshake.

Tradeoff: 'always' shows the button to customers with no card saved. On desktop they complete by scanning a QR code with their iPhone (Apple opened this up in iOS 18 / Feb 2025) — a real flow, but longer than Safari tap-to-pay, and a dead end for anyone without an Apple device. Worth A/B testing rather than assuming it's a pure win.

3. The domain registered with Stripe

Every domain displaying Apple Pay must be registered, in both test and live mode, including subdomains — example.com, www.example.com and shop.example.com are three separate registrations. Registering in live mode also registers it in sandboxes.

Registration also gates Link, PayPal and Amazon Pay, so when a domain is unregistered those disappear alongside Apple Pay.

See Run for both registration routes. The diagnostics panel checks the current host against the account's registered domains and names the ones it found, so a typo or a missing subdomain is obvious.

4. No UI gate on window.ApplePaySession

ApplePaySession is Safari's native Apple Pay API. It does not exist in Chrome — Chrome reaches Apple Pay through the Payment Request API. This hides the button in Chrome 100% of the time:

if (window.ApplePaySession) { showApplePayButton(); }   // breaks Chrome

Use the Element's availablepaymentmethodschange event instead.

Debugging

Ask the Element what it decided, rather than inferring from the UI:

expressCheckoutElement.on('availablepaymentmethodschange', ({ paymentMethods }) => {
  // { applePay: {available: true}, googlePay: {available: false}, … }
  console.log(paymentMethods);
});

If applePay.available is false, something upstream of your rendering code is unmet — account config, domain, HTTPS, browser, or wallet state. If it's true and the button still isn't visible, it's CSS or a conditional in your own component.

Comparing Safari against Chrome on the same page also localises it quickly: a requirement that's account-wide or domain-wide takes both browsers down, while requirements 2 and 4 are specific to non-Safari browsers.

Watch the shape. paymentMethods is a truthy object even when every wallet in it is unavailable, so this is a bug that leaves an empty wallet row on the page:

if (paymentMethods) { showWalletRow(); }   // truthy even when all are false

Count the available ones instead — see availableWallets() in public/checkout.js.

Testing notes

  • You can't save Stripe test cards to Apple Pay. In test mode Stripe recognises the test key and returns a successful test token, so you use a real card from your wallet and it is never charged.
  • Test in both Safari and Chrome — they take different paths to Apple Pay, so one passing doesn't imply the other.
  • Apple Pay and Google Pay don't share the customer's email unless you set emailRequired: true on the Element.

Files

File Purpose
public/checkout.js The whole integration. The Chrome fix is commented in place.
public/index.html Payment page, always/auto toggle, diagnostics panel.
server.js /api/create-intent and a /api/domain-status check.
scripts/check-account.js npm run check-account — is Apple Pay enabled?
scripts/register-domain.js npm run register-domain — registers a domain for Apple Pay.

Sources

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages