Apple Pay and Google Pay

Apple Pay and Google Pay settle as normal card charges: same USD balance, same callbacks, same status endpoint. They cannot go through the direct card API (/wallet/card/charge), because the card is tokenized on the customer device and no card number is exposed.

There are three ways to offer them.

OptionFront-end workDomain registration
A. Hosted checkout (redirect)NoneNot needed
B. Hosted checkout in an iframeAn <iframe>Ask support to allow your origin
C. Embedded on your page (Express Checkout Element)Stripe.js on your pageYour domain must be registered

All three start with the same call: POST /wallet/deposit/payment with method_pay=card. See Card payments (hosted checkout) for the full field list.

The wallet parameter

Send wallet to show a single wallet on the hosted checkout, with no card form and no other wallet button. Use it when the customer already chose the method on your page.

{
  "token": "YOUR_TOKEN",
  "secret": "YOUR_SECRET",
  "amount": 49.90,
  "method_pay": "card",
  "wallet": "apple_pay",
  "postback": "https://example.com/callbacks/card",
  "return_url": "https://example.com/orders/1043/thanks"
}
ValueEffect
omittedCard form plus any wallet the device supports.
apple_payOnly the Apple Pay button.
google_payOnly the Google Pay button.

Any other value returns 422 INVALID_WALLET.

The customer fields (debtor_name, email, phone, debtor_document_number) are optional for card. When you omit them, name and email come from the wallet. If you send them, they are kept as declared and not overwritten.

Option A: hosted checkout

Redirect the customer to checkout_url. Apple Pay and Google Pay appear automatically on supported devices. No domain setup on your side.

Option B: hosted checkout in an iframe

  1. Ask support to allow your site origin as a frame ancestor. Only https:// origins are accepted (for example https://shop.example.com).
  2. Load checkout_url in an iframe with allow="payment". Without that attribute the browser blocks the wallet APIs inside the frame.
<iframe
  src="CHECKOUT_URL"
  allow="payment"
  width="100%"
  height="720"
  style="border:0"></iframe>

Until your origin is allowed, browsers refuse to render the page inside your frame.

Option C: embedded on your own page

Use the client_secret, publishable_key and on_behalf_of from the create response to mount Stripe's Express Checkout Element on your HTTPS page.

<div id="express-checkout-element"></div>
<script src="https://js.stripe.com/v3/"></script>
<script>
  // Values from the POST /wallet/deposit/payment response:
  const publishableKey = "pk_test_YOUR_PUBLISHABLE_KEY";
  const clientSecret   = "pi_..._secret_...";
  const onBehalfOf     = "acct_...";

  // Do NOT pass { stripeAccount } here.
  const stripe = Stripe(publishableKey);

  // onBehalfOf is required and must equal on_behalf_of from the response.
  const elements = stripe.elements({ clientSecret, onBehalfOf });

  const ece = elements.create('expressCheckout');
  ece.mount('#express-checkout-element');

  ece.on('confirm', async () => {
    const { error: submitError } = await elements.submit();
    if (submitError) { console.error(submitError.message); return; }

    const { error } = await stripe.confirmPayment({
      elements,
      clientSecret,
      confirmParams: { return_url: 'https://example.com/checkout/complete' },
      redirect: 'if_required',
    });
    if (error) { console.error(error.message); return; }
    // Wait for the "paid" callback (or check the status endpoint) before fulfilling.
  });
</script>

Rules:

  • Initialize Stripe() with publishable_key only. Passing { stripeAccount } turns the charge into a different type and confirmation fails.
  • Pass onBehalfOf to stripe.elements(). It is required for these charges.
  • client_secret confirms only this payment. It can go to the browser over HTTPS, but never log it or put it in a URL.
  • publishable_key matches the environment of the credentials you used (sandbox or live). Do not mix a sandbox key with a live charge.

Register your domain

Apple Pay only renders on domains registered for our platform, and Google Pay needs HTTPS plus registration. Send support the exact domain that shows the buttons (for example checkout.example.com). Registration is per environment, so we register it for sandbox and live separately. You do not need to host any /.well-known/ file.

The buttons render only when all of these hold:

  1. The domain is registered and active.
  2. The page is served over HTTPS.
  3. The device, browser and region support the wallet, and the wallet has a card.

To show the buttons even before the wallet is set up on the device, create the element with paymentMethods: { applePay: 'always', googlePay: 'always' }.

Result

Same as any card charge:

  • paid callback with amount and currency, and the USD balance is credited.
  • If the customer closes the wallet sheet and never pays, nothing is charged. The session expires after expires_in_seconds and you receive a cancelled callback with failure_code: expired.
  • Refunds and disputes work as for any card charge.

Testing

Wallets work in sandbox, but you need a real device with a real card in the wallet and, for option C, a domain registered in the sandbox environment over HTTPS. The card is tokenized as a test token and no money moves.


Did this page help you?