Introduction
This guide shows you how to configure Crossmint’s Embedded Checkout so it renders only the Apple Pay or Google Pay button, and everything else on the screen is yours: your amount selector, your token UI, your success states. Rendering just the wallet button gives users a smooth one-tap purchase experience inside your own UI. You’ll learn how to:- Restrict the embedded checkout to a single payment method
- Create the order server-side so the pay button is ready when the user sees it
- Hide the checkout’s built-in inputs and surface payment errors in your layout
- Detect success and update your own UI
The five required settings
The whole experience is this configuration. There is no single “Apple Pay only” switch: restricting the payment methods removes the tabs and forms, hiding the two inputs removes the rest, and the error rule gives payment failures a place to render.Prerequisites
Get API keys
- A client key (
ck_staging_...) with theorders.readscope, plususers.create,users.read,wallets.read,wallets.create, andwallets:balance.readif you also use Crossmint Auth and Wallets as the demo app does. Add every origin the app runs on (for examplehttp://localhost:3000and your public domain) to the key’s origins allowlist. - A server key (
sk_staging_...) with theorders.createandorders.readscopes.
Get a public HTTPS domain
localhost cannot be registered. For local development, tunnel your dev server with
ngrok (ngrok http 3000) and use the resulting domain.Register the domain for Apple Pay
- Download the verification file and serve it at
https://<your-domain>/.well-known/apple-developer-merchantid-domain-association. In Next.js, place it atpublic/.well-known/apple-developer-merchantid-domain-association. - Enter your domain and click Verify domain. The status turns to
verifiedimmediately once the file is reachable.
Have a recipient wallet
Integration
The example buys XMEME, the staging test token, on Solana. Production tokens will not work in the staging environment.Add environment variables
.env.local in your Next.js project root:Create the order server-side
app/checkout/actions.ts. The server key never reaches the browser, and the order carries the recipient and the receipt email, so the checkout never needs to ask for them:sk_staging_ keys pair with staging.crossmint.com and
sk_production_ keys with www.crossmint.com.Render only the Apple Pay button
app/checkout/page.tsx. Three details make the experience feel native:- Create the order as soon as the user picks an amount, not on a separate confirm button. Order creation takes a moment, so starting early means the Apple Pay button is ready by the time the user looks at it.
- Show a loader until the iframe reports content via the
ui:height.changedmessage, then reveal the button. - The Apple Pay button is the CTA. Do not add your own “Continue” button in front of it; one tap should open the payment sheet.
key={order.orderId}: changing the amount creates a fresh order, and keying the component by order id remounts the widget cleanly instead of navigating the iframe in place.Run your app
http://localhost:3000) to see your checkout!Test with Apple Pay
localhost) in Safari 17+ on macOS, or on an iPhone running iOS 17+, with a card added to Apple Wallet. Use a physical device rather than the iOS Simulator, which does not render Apple Pay. In staging the payment is processed in the PSP’s sandbox, so the card in your Wallet is never actually charged.To exercise the full flow from any browser during development, temporarily flip the allowed methods to the card form and pay with the staging test card 4242 4242 4242 4242 (any future expiry, any CVC):One-Tap on Mobile
The five settings above are order and URL parameters, so the same configuration renders inside a native app unchanged. The recommended integration paths are the Swift and Kotlin SDKs, which render and configure the checkout for you. If you prefer full control and do not want to add an SDK, create the order exactly as in step 2 and render the checkout in your own WebView following the Mobile WebView Integration guide:- Simpler wallet detection. The operating system determines the wallet, so replace the
ApplePaySessioncheck from the tip above with a platform check: enable Apple Pay on iOS and Google Pay on Android (Platform.OS === "ios"in React Native), keeping one wallet enabled per device. - No Apple Pay domain registration. The checkout page is served from
crossmint.com, which is already enabled for Apple Pay, so the domain steps in the prerequisites apply to the web integration only.
Troubleshooting
The payment sheet shows "Failed to validate merchant. Please try again."
The payment sheet shows "Failed to validate merchant. Please try again."
- You are on a registered domain. The page must be served from a domain that shows
verifiedunder Apple Pay Domains in the console —localhostalways fails validation. Use your ngrok or production domain instead. - The domain matches exactly. The registration is per host:
app.example.comandexample.comare different domains, and a new ngrok tunnel gets a new domain that needs registering again. - Everything belongs to one project. The order (server key), the widget’s
apiKey(client key), and the Apple Pay domain registration must all come from the same console project. If the order was created with a key from a different project, validation fails even with a verified domain.
The checkout iframe does not load at all
The checkout iframe does not load at all
- Add the exact origin the app runs on (scheme + host + port) to the client key’s origins allowlist in the console.
- Match the base URL to the key environment:
ck_staging_/sk_staging_keys pair withstaging.crossmint.com, production keys withwww.crossmint.com.
The console shows "The API key provided doesn't have the required scopes"
The console shows "The API key provided doesn't have the required scopes"
orders.read; if you also use Crossmint Auth
and Wallets like the demo app, add users.create, users.read, wallets.read, wallets.create, and
wallets:balance.read. Edit the key’s scopes in the console under Integrate → API Keys.A payment fails and nothing appears on screen
A payment fails and nothing appears on screen
GlobalMessage: { display: "visible" } in appearance.rules. In the pay-button-only layout this rule is
the only surface where payment errors (such as a payment blocked by risk checks) can render.Next steps
Take it to mobile
Apple Pay domain setup
Customize UI
Payment methods
FAQ
Is there a UI Crossmint provides?
Is there a UI Crossmint provides?
Do I need to complete KYC to purchase memecoins?
Do I need to complete KYC to purchase memecoins?
What happens if my transaction fails?
What happens if my transaction fails?
What are the transaction limits?
What are the transaction limits?
Is there a limit on how much of a single token I can sell in a day?
Is there a limit on how much of a single token I can sell in a day?
What do the order creation parameters mean?
What do the order creation parameters mean?
method: Set tocardfor credit card paymentscurrency: Set tousdfor US Dollar paymentsreceiptEmail: Optional. If the buyer pays with Apple Pay or Google Pay, Crossmint obtains the email automatically. Providing it improves fraud signal quality.
tokenLocator: Specifies the memecoin token address in the formatsolana:tokenAddressexecutionParameters:mode: Set to "exact-in" for memecoin purchases (specifies exact USD amount to spend)amount: Amount to purchase in USDmaxSlippageBps: Set to "500" for 5% slippage tolerance. If not provided, the default slippage will be applied from the available liquidity provider.
clientSecret and an order object with an orderId. Pass these to the Crossmint Embedded Checkout component to collect payment from the user.
