Payment Request Button

    Collect payment and address information from customers who use Apple Pay, Google Pay, Microsoft Pay, and the Payment Request API.

    Overview

    The Payment Request Button Element gives you a single integration for Apple Pay, Google Pay, Microsoft Pay, and the browser standard Payment Request API.

    Customers see a “Pay now” button or an Apple Pay button, depending on what their device and browser combination supports. If neither option is available, they don’t see the button. Supporting Apple Pay requires additional steps, but compatible devices automatically support browser-saved cards, Google Pay, and Microsoft Pay.

    Apple Pay with the Payment Request Button requires macOS 10.12.1+ or iOS 10.1+.

    Prerequisites

    Before you start, you need to:

    Verify your domain with Apple Pay

    To use Apple Pay, you need to register with Apple all of your web domains that will show an Apple Pay button. This includes both top-level domains (e.g., stripe.com) and subdomains (e.g., shop.stripe.com). You need to do this for domains you use in both production and testing. When testing locally, use a tool like ngrok to get an HTTPS domain.

    Important note: Apple’s documentation for Apple Pay on the Web describes their process of “merchant validation”, which Stripe handles for you behind the scenes. You do not need to create an Apple Merchant ID, CSR, etc., as described in their documentation, and should instead just follow these steps:

    1. Download this domain association file and host it at /.well-known/apple-developer-merchantid-domain-association on your site.

      For example, if you're registering https://example.com, make that file available at https://example.com/.well-known/apple-developer-merchantid-domain-association.

    2. Next, tell Stripe to register your domain with Apple. You can do this by either going to the Apple Pay tab in the Account Settings of your Dashboard, or by directly using the API with your live secret key as shown below.

      Note that we've redacted your live secret key here—head to your Dashboard and replace sk_live_•••••••••••••••••••••••• below with your live secret key. All domains, whether in production or testing, must be registered with your live secret key.

      curl https://api.stripe.com/v1/apple_pay/domains \ -u "sk_live_••••••••••••••••••••••••": \ -d domain_name="example.com"
      Stripe.api_key = "sk_live_••••••••••••••••••••••••" Stripe::ApplePayDomain.create({ domain_name: 'example.com', })
      stripe.api_key = "sk_live_••••••••••••••••••••••••" stripe.ApplePayDomain.create( domain_name='example.com', )
      \Stripe\Stripe::setApiKey("sk_live_••••••••••••••••••••••••"); \Stripe\ApplePayDomain::create([ 'domain_name' => 'example.com', ]);
      var stripe = require("stripe")("sk_live_••••••••••••••••••••••••"); const domain = await stripe.applePayDomains.create({ domain_name: 'example.com', });
      Stripe.apiKey = "sk_live_••••••••••••••••••••••••"; ApplePayDomainCreateParams params = ApplePayDomainCreateParams.builder() .setDomainName("example.com") .build(); ApplePayDomain domain = ApplePayDomain.create(params);
      stripe.Key = "sk_live_••••••••••••••••••••••••" params := &stripe.ApplePayDomainParams{ DomainName: stripe.String("example.com"), } domain, _ := applepaydomain.New(params)
      StripeConfiguration.ApiKey = "sk_live_••••••••••••••••••••••••"; var options = new ApplePayDomainCreateOptions { DomainName = "example.com", }; var service = new ApplePayDomainService(); var domain = service.Create(options);
    3. After registering your domains, you can make payments on your site using your live API keys.

    1 Set up Stripe Elements Client-side

    Elements is available as part of Stripe.js. Include this in your page and create a container that will be used for the paymentRequestButton Element:

    <script src="https://js.stripe.com/v3/"></script> <div id="payment-request-button"> <!-- A Stripe Element will be inserted here. --> </div>

    Your Stripe publishable API key is also required as it identifies your website to Stripe:

    var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
    const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');

    2 Create a paymentRequest instance Client-side

    Create an instance of stripe.paymentRequest with all required options.

    var paymentRequest = stripe.paymentRequest({ country: 'US', currency: 'usd', total: { label: 'Demo total', amount: 1099, }, requestPayerName: true, requestPayerEmail: true, });
    const paymentRequest = stripe.paymentRequest({ country: 'US', currency: 'usd', total: { label: 'Demo total', amount: 1099, }, requestPayerName: true, requestPayerEmail: true, });

    3 Create and mount the paymentRequestButton Client-side

    Create the paymentRequestButton Element and check to make sure that your customer has an active payment method using canMakePayment(). If they do, mount the Element to the container to display the Payment Request Button. If they do not, you cannot mount the Element, and we encourage you to show a traditional checkout form instead.

    var elements = stripe.elements(); var prButton = elements.create('paymentRequestButton', { paymentRequest: paymentRequest, }); // Check the availability of the Payment Request API first. paymentRequest.canMakePayment().then(function(result) { if (result) { prButton.mount('#payment-request-button'); } else { document.getElementById('payment-request-button').style.display = 'none'; } });
    const elements = stripe.elements(); const prButton = elements.create('paymentRequestButton', { paymentRequest, }); (async () => { // Check the availability of the Payment Request API first. const result = await paymentRequest.canMakePayment(); if (result) { prButton.mount('#payment-request-button'); } else { document.getElementById('payment-request-button').style.display = 'none'; } })();

    4 Create a PaymentIntent Server-side

    Stripe uses a PaymentIntent object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.

    Create a PaymentIntent on your server with an amount and currency. Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.

    # Verify your integration in this guide by including the metadata parameter curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=usd \ -d "metadata[integration_check]"=accept_a_payment
    # Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = Stripe::PaymentIntent.create({ amount: 1099, currency: 'usd', # Verify your integration in this guide by including this parameter metadata: {integration_check: 'accept_a_payment'}, })
    # Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = stripe.PaymentIntent.create( amount=1099, currency='usd', # Verify your integration in this guide by including this parameter metadata={'integration_check': 'accept_a_payment'}, )
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $intent = \Stripe\PaymentIntent::create([ 'amount' => 1099, 'currency' => 'usd', // Verify your integration in this guide by including this parameter 'metadata' => ['integration_check' => 'accept_a_payment'], ]);
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setCurrency("usd") .setAmount(1099L) // Verify your integration in this guide by including this parameter .putMetadata("integration_check", "accept_a_payment") .build(); PaymentIntent intent = PaymentIntent.create(params);
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'usd', // Verify your integration in this guide by including this parameter metadata: {integration_check: 'accept_a_payment'}, });
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyUSD)), } // Verify your integration in this guide by including this parameter params.AddMetadata("integration_check", "accept_a_payment") pi, _ := paymentintent.New(params)
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "usd", // Verify your integration in this guide by including this parameter Metadata = new Dictionary<string, string> { { "integration_check", "accept_a_payment" }, }, }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options);

    Included in the returned PaymentIntent is a client secret, which you’ll use to securely complete the payment process instead of passing the entire PaymentIntent object. Send the client secret back to the client to use in the next step.

    5 Complete the payment Client-side

    Listen to the paymentmethod event to receive a PaymentMethod object. Pass the PaymentMethod ID and the PaymentIntent’s client secret to stripe.confirmCardPayment to complete the payment.

    paymentRequest.on('paymentmethod', function(ev) { // Confirm the PaymentIntent without handling potential next actions (yet). stripe.confirmCardPayment( clientSecret, {payment_method: ev.paymentMethod.id}, {handleActions: false} ).then(function(confirmResult) { if (confirmResult.error) { // Report to the browser that the payment failed, prompting it to // re-show the payment interface, or show an error message and close // the payment interface. ev.complete('fail'); } else { // Report to the browser that the confirmation was successful, prompting // it to close the browser payment method collection interface. ev.complete('success'); // Let Stripe.js handle the rest of the payment flow. stripe.confirmCardPayment(clientSecret).then(function(result) { if (result.error) { // The payment failed -- ask your customer for a new payment method. } else { // The payment has succeeded. } }); } }); });
    paymentRequest.on('paymentmethod', async (ev) => { // Confirm the PaymentIntent without handling potential next actions (yet). const {error: confirmError} = await stripe.confirmCardPayment( clientSecret, {payment_method: ev.paymentMethod.id}, {handleActions: false} ); if (confirmError) { // Report to the browser that the payment failed, prompting it to // re-show the payment interface, or show an error message and close // the payment interface. ev.complete('fail'); } else { // Report to the browser that the confirmation was successful, prompting // it to close the browser payment method collection interface. ev.complete('success'); // Let Stripe.js handle the rest of the payment flow. const {error, paymentIntent} = await stripe.confirmCardPayment(clientSecret); if (error) { // The payment failed -- ask your customer for a new payment method. } else { // The payment has succeeded. } } });

    6 Test your integration

    To test your integration you must use HTTPS and a supported browser. If you are using the paymentRequestButton Element within an iframe, the iframe must have the allowPaymentRequest attribute set.

    In addition, each payment method and browser has specific requirements:

    Safari

    • Safari on Mac running macOS Sierra or later
    • An iPhone (not an iPad; Safari doesn't support them yet) with a card in its Wallet paired to your Mac with Handoff, or a Mac with TouchID. Instructions can be found on Apple's Support site.
    • Make sure you've verified your domain with Apple Pay.
    • When using an iframe, its origin must match the top-level origin. Two pages have the same origin if the protocol, host (full domain name), and port (if one is specified) are the same for both pages.

    Mobile Safari

    • Mobile Safari on iOS 10.1 or later
    • A card in your Wallet (go to Settings → Wallet & Apple Pay)
    • Make sure you've verified your domain with Apple Pay.
    • When using an iframe, its origin must match the top-level origin. Two pages have the same origin if the protocol, host (full domain name), and port (if one is specified) are the same for both pages.

    Chrome

    Chrome Mobile for Android

    Note that when using Google Pay, including Android Pay, with a test mode key, you might see an "Unrecognized app" warning. This is expected, and does not show up in live mode.

    Windows

    Collect shipping information

    To collect shipping information, begin by including requestShipping: true when creating the payment request.

    You may also provide an array of shippingOptions at this point, if your shipping options do not depend on the customer’s address.

    var paymentRequest = stripe.paymentRequest({ country: 'US', currency: 'usd', total: { label: 'Demo total', amount: 1099, }, requestShipping: true, // `shippingOptions` is optional at this point: shippingOptions: [ // The first shipping option in this list appears as the default // option in the browser payment interface. { id: 'free-shipping', label: 'Free shipping', detail: 'Arrives in 5 to 7 days', amount: 0, }, ], });
    const paymentRequest = stripe.paymentRequest({ country: 'US', currency: 'usd', total: { label: 'Demo total', amount: 1099, }, requestShipping: true, // `shippingOptions` is optional at this point: shippingOptions: [ // The first shipping option in this list appears as the default // option in the browser payment interface. { id: 'free-shipping', label: 'Free shipping', detail: 'Arrives in 5 to 7 days', amount: 0, }, ], });

    Next, listen to the shippingaddresschange event to detect when a customer selects a shipping address. Use the address to fetch valid shipping options from your server, update the total, or perform other business logic. The address data on the shippingaddresschange event may be anynomized by the browser to not reveal sensitive information that is not necessary for shipping cost calculation.

    Note that valid shippingOptions must be supplied at this point for the customer to proceed in the flow.

    paymentRequest.on('shippingaddresschange', function(ev) { if (ev.shippingAddress.country !== 'US') { ev.updateWith({status: 'invalid_shipping_address'}); } else { // Perform server-side request to fetch shipping options fetch('/calculateShipping', { data: JSON.stringify({ shippingAddress: ev.shippingAddress }) }).then(function(response) { return response.json(); }).then(function(result) { ev.updateWith({ status: 'success', shippingOptions: result.supportedShippingOptions, }); }); } });
    paymentRequest.on('shippingaddresschange', async (ev) => { if (ev.shippingAddress.country !== 'US') { ev.updateWith({status: 'invalid_shipping_address'}); } else { // Perform server-side request to fetch shipping options const response = await fetch('/calculateShipping', { data: JSON.stringify({ shippingAddress: ev.shippingAddress }) }); const result = await response.json(); ev.updateWith({ status: 'success', shippingOptions: result.supportedShippingOptions, }); } });

    Style the button

    Use the following parameters to customize the Element:

    elements.create('paymentRequestButton', { paymentRequest: paymentRequest, style: { paymentRequestButton: { type: 'default', // One of 'default', 'book', 'buy', or 'donate' // Defaults to 'default' theme: 'dark', // One of 'dark', 'light', or 'light-outline' // Defaults to 'dark' height: '64px' // Defaults to '40px'. The width is always '100%'. }, }, });
    elements.create('paymentRequestButton', { paymentRequest, style: { paymentRequestButton: { type: 'default', // One of 'default', 'book', 'buy', or 'donate' // Defaults to 'default' theme: 'dark', // One of 'dark', 'light', or 'light-outline' // Defaults to 'dark' height: '64px', // Defaults to '40px'. The width is always '100%'. }, }, });

    Using your own button

    If you wish to design your own button instead of using the paymentRequestButton Element, you may show your custom button based on the result of paymentRequest.canMakePayment(). Then, use paymentRequest.show() to display the browser interface when your button is clicked.

    When building your own button, follow Apple Pay’s Human Interface Guidelines.

    Use the Payment Request Button with Stripe Connect

    Connect platforms that either create direct charges or add the token to a Customer on the connected account must take some additional steps when using the Payment Request Button.

    1. On your frontend, before creating the PaymentRequest instance, set the stripeAccount option on the Stripe instance:

      var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx', { stripeAccount: 'CONNECTED_STRIPE_ACCOUNT_ID' });
      const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx', { stripeAccount: 'CONNECTED_STRIPE_ACCOUNT_ID' });
    2. Register all domains on which the Payment Request Button will be shown with Apple Pay. You can use the Stripe API for this, using your platform's secret key to authenticate the request, and setting the Stripe-Account header to your connected account's Stripe ID, as described in Making API calls for connected accounts.

      curl https://api.stripe.com/v1/apple_pay/domains \ -u {PLATFORM_SECRET_KEY}: \ -H "Stripe-Account: {{CONNECTED_STRIPE_ACCOUNT_ID}}" \ -d domain_name="example.com"
      Stripe.api_key = "sk_live_••••••••••••••••••••••••" Stripe::ApplePayDomain.create({ domain_name: 'example.com', },{ stripe_account: '{{CONNECTED_STRIPE_ACCOUNT_ID}}', })
      stripe.api_key = "sk_live_••••••••••••••••••••••••" stripe.ApplePayDomain.create( domain_name='example.com', stripe_account='{{CONNECTED_STRIPE_ACCOUNT_ID}}', )
      \Stripe\Stripe::setApiKey("sk_live_••••••••••••••••••••••••"); \Stripe\ApplePayDomain::create([ 'domain_name' => 'example.com', ],[ 'stripe_account' => '{{CONNECTED_STRIPE_ACCOUNT_ID}}', ]);
      var stripe = require("stripe")("sk_live_••••••••••••••••••••••••"); const domain = await stripe.applePayDomains.create({ domain_name: 'example.com', },{ stripe_account: '{{CONNECTED_STRIPE_ACCOUNT_ID}}', });
      Stripe.apiKey = "sk_live_••••••••••••••••••••••••"; ApplePayDomainCreateParams params = ApplePayDomainCreateParams.builder() .setDomainName("example.com") .build(); RequestOptions requestOptions = RequestOptions.builder() .setStripeAccount("{{CONNECTED_STRIPE_ACCOUNT_ID}}") .build(); ApplePayDomain domain = ApplePayDomain.create(params, requestOptions);
      stripe.Key = "sk_live_••••••••••••••••••••••••" params := &stripe.ApplePayDomainParams{ DomainName: stripe.String("example.com"), } params.SetStripeAccount("{{CONNECTED_STRIPE_ACCOUNT_ID}}") domain, _ := applepaydomain.New(params)
      StripeConfiguration.ApiKey = "sk_live_••••••••••••••••••••••••"; var options = new ApplePayDomainCreateOptions { DomainName = "example.com", }; var requestOptions = new RequestOptions(); requestOptions.StripeAccount = "{{CONNECTED_ACCOUNT_ID}}"; var service = new ApplePayDomainService(); var domain = service.Create(options, requestOptions);

    Overview

    The Payment Request Button Element gives you a single integration for Apple Pay, Google Pay, Microsoft Pay, and the browser standard Payment Request API.

    Customers see a “Pay now” button or an Apple Pay button, depending on what their device and browser combination supports. If neither option is available, they don’t see the button. Supporting Apple Pay requires additional steps, but compatible devices automatically support browser-saved cards, Google Pay, and Microsoft Pay.

    Apple Pay with the Payment Request Button requires macOS 10.12.1+ or iOS 10.1+.

    Prerequisites

    Before you start, you need to:

    Verify your domain with Apple Pay

    To use Apple Pay, you need to register with Apple all of your web domains that will show an Apple Pay button. This includes both top-level domains (e.g., stripe.com) and subdomains (e.g., shop.stripe.com). You need to do this for domains you use in both production and testing. When testing locally, use a tool like ngrok to get an HTTPS domain.

    Important note: Apple’s documentation for Apple Pay on the Web describes their process of “merchant validation”, which Stripe handles for you behind the scenes. You do not need to create an Apple Merchant ID, CSR, etc., as described in their documentation, and should instead just follow these steps:

    1. Download this domain association file and host it at /.well-known/apple-developer-merchantid-domain-association on your site.

      For example, if you're registering https://example.com, make that file available at https://example.com/.well-known/apple-developer-merchantid-domain-association.

    2. Next, tell Stripe to register your domain with Apple. You can do this by either going to the Apple Pay tab in the Account Settings of your Dashboard, or by directly using the API with your live secret key as shown below.

      Note that we've redacted your live secret key here—head to your Dashboard and replace sk_live_•••••••••••••••••••••••• below with your live secret key. All domains, whether in production or testing, must be registered with your live secret key.

      curl https://api.stripe.com/v1/apple_pay/domains \ -u "sk_live_••••••••••••••••••••••••": \ -d domain_name="example.com"
      Stripe.api_key = "sk_live_••••••••••••••••••••••••" Stripe::ApplePayDomain.create({ domain_name: 'example.com', })
      stripe.api_key = "sk_live_••••••••••••••••••••••••" stripe.ApplePayDomain.create( domain_name="example.com" )
      \Stripe\Stripe::setApiKey("sk_live_••••••••••••••••••••••••"); \Stripe\ApplePayDomain::create([ 'domain_name' => 'example.com' ]);
      var stripe = require("stripe")("sk_live_••••••••••••••••••••••••"); stripe.applePayDomains.create({ domain_name: 'example.com' });
      Stripe.apiKey = "sk_live_••••••••••••••••••••••••"; ApplePayDomainCreateParams params = ApplePayDomainCreateParams.builder() .setDomainName("example.com") .build(); ApplePayDomain domain = ApplePayDomain.create(params);
      stripe.Key = "sk_live_••••••••••••••••••••••••" params := &stripe.ApplePayDomainParams{ DomainName: stripe.String("example.com"), } domain, _ := applepaydomain.New(params)
      StripeConfiguration.ApiKey = "sk_live_••••••••••••••••••••••••"; var options = new ApplePayDomainCreateOptions { DomainName = "example.com", }; var service = new ApplePayDomainService(); var domain = service.Create(options);
    3. After registering your domains, you can make payments on your site using your live API keys.

    1 Set up Stripe Elements

    Install React Stripe.js and the Stripe.js loader from the npm public registry.

    npm install --save @stripe/react-stripe-js @stripe/stripe-js

    We also provide a UMD build for sites that are not using npm or modules.

    Include the Stripe.js script which exports a global Stripe function and the UMD build of React Stripe.js which exports a global ReactStripe object. Always load the Stripe.js script directly from js.stripe.com to remain PCI compliant. Do not include the script in a bundle or host a copy of it yourself.

    <!-- Stripe.js --> <script src="https://js.stripe.com/v3/"></script> <!-- React Stripe.js development build --> <script src="https://unpkg.com/@stripe/react-stripe-js@latest/dist/react-stripe.umd.js"></script> <!-- When you are ready to deploy your site to production, remove the above development script, and include the following production build. --> <script src="https://unpkg.com/@stripe/react-stripe-js@latest/dist/react-stripe.umd.min.js"></script>

    Add Stripe.js and Elements to your page

    To use Element components, wrap the root of your React app in an Elements provider. Call loadStripe with your publishable key and pass the returned Promise to the Elements provider.

    import React from 'react'; import ReactDOM from 'react-dom'; import {Elements} from '@stripe/react-stripe-js'; import {loadStripe} from '@stripe/stripe-js'; import CheckoutForm from './CheckoutForm'; // Make sure to call `loadStripe` outside of a component’s render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe("pk_test_TYooMQauvdEDq54NiTphI7jx"); function App() { return ( <Elements stripe={stripePromise}> <CheckoutForm /> </Elements> ); }; ReactDOM.render(<App />, document.getElementById('root'));

    2 Create a paymentRequest instance Client-side

    In your checkout form component, create an instance of stripe.paymentRequest with all required options.

    import React, {useState, useEffect} from 'react'; import {PaymentRequestButtonElement, useStripe} from '@stripe/react-stripe-js'; const CheckoutForm = () => { const stripe = useStripe(); const [paymentRequest, setPaymentRequest] = useState(null); useEffect(() => { if (stripe) { const pr = stripe.paymentRequest({ country: 'US', currency: 'usd', total: { label: 'Demo total', amount: 1099, }, requestPayerName: true, requestPayerEmail: true, }); } }, [stripe]); // Use a traditional checkout form. return 'Insert your form or button component here.'; }

    3 Render the Payment Request Button Element Client-side

    Check to make sure that your customer has an active payment method using canMakePayment. If they do, render the <PaymentRequestButtonElement>. If they do not, you cannot render the Element, and we encourage you to show a traditional checkout form instead.

    import React, {useState, useEffect} from 'react'; import {PaymentRequestButtonElement, useStripe} from '@stripe/react-stripe-js'; const CheckoutForm = () => { const stripe = useStripe(); const [paymentRequest, setPaymentRequest] = useState(null); useEffect(() => { if (stripe) { const pr = stripe.paymentRequest({ country: 'US', currency: 'usd', total: { label: 'Demo total', amount: 1099, }, requestPayerName: true, requestPayerEmail: true, }); // Check the availability of the Payment Request API. pr.canMakePayment().then(result => { if (result) { setPaymentRequest(pr); } }); } }, [stripe]); if (paymentRequest) { return <PaymentRequestButtonElement options={{paymentRequest}} /> } // Use a traditional checkout form. return 'Insert your form or button component here.'; }

    4 Create a PaymentIntent Server-side

    Stripe uses a PaymentIntent object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.

    Create a PaymentIntent on your server with an amount and currency. Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.

    # Verify your integration in this guide by including the metadata parameter curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=usd \ -d "metadata[integration_check]"=accept_a_payment
    # Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = Stripe::PaymentIntent.create({ amount: 1099, currency: 'usd', # Verify your integration in this guide by including this parameter metadata: {integration_check: 'accept_a_payment'}, })
    # Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = stripe.PaymentIntent.create( amount=1099, currency='usd', # Verify your integration in this guide by including this parameter metadata={'integration_check': 'accept_a_payment'}, )
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $intent = \Stripe\PaymentIntent::create([ 'amount' => 1099, 'currency' => 'usd', // Verify your integration in this guide by including this parameter 'metadata' => ['integration_check' => 'accept_a_payment'], ]);
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setCurrency("usd") .setAmount(1099L) // Verify your integration in this guide by including this parameter .putMetadata("integration_check", "accept_a_payment") .build(); PaymentIntent intent = PaymentIntent.create(params);
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'usd', // Verify your integration in this guide by including this parameter metadata: {integration_check: 'accept_a_payment'}, });
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyUSD)), } // Verify your integration in this guide by including this parameter params.AddMetadata("integration_check", "accept_a_payment") pi, _ := paymentintent.New(params)
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "usd", // Verify your integration in this guide by including this parameter Metadata = new Dictionary<string, string> { { "integration_check", "accept_a_payment" }, }, }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options);

    Included in the returned PaymentIntent is a client secret, which you’ll use to securely complete the payment process instead of passing the entire PaymentIntent object. Send the client secret back to the client to use in the next step.

    5 Complete the payment Client-side

    Listen to the paymentmethod event to receive a PaymentMethod object. Pass the PaymentMethod ID and the PaymentIntent’s client secret to stripe.confirmCardPayment to complete the payment.

    paymentRequest.on('paymentmethod', async (ev) => { // Confirm the PaymentIntent without handling potential next actions (yet). const {error: confirmError} = await stripe.confirmCardPayment( clientSecret, {payment_method: ev.paymentMethod.id}, {handleActions: false} ); if (confirmError) { // Report to the browser that the payment failed, prompting it to // re-show the payment interface, or show an error message and close // the payment interface. ev.complete('fail'); } else { // Report to the browser that the confirmation was successful, prompting // it to close the browser payment method collection interface. ev.complete('success'); // Let Stripe.js handle the rest of the payment flow. const {error, paymentIntent} = await stripe.confirmCardPayment(clientSecret); if (error) { // The payment failed -- ask your customer for a new payment method. } else { // The payment has succeeded. } } });

    6 Test your integration

    To test your integration you must use HTTPS and a supported browser. If you are using the paymentRequestButton Element within an iframe, the iframe must have the allowPaymentRequest attribute set.

    In addition, each payment method and browser has specific requirements:

    Safari

    • Safari on Mac running macOS Sierra or later
    • An iPhone (not an iPad; Safari doesn't support them yet) with a card in its Wallet paired to your Mac with Handoff, or a Mac with TouchID. Instructions can be found on Apple's Support site.
    • Make sure you've verified your domain with Apple Pay.
    • When using an iframe, its origin must match the top-level origin. Two pages have the same origin if the protocol, host (full domain name), and port (if one is specified) are the same for both pages.

    Mobile Safari

    • Mobile Safari on iOS 10.1 or later
    • A card in your Wallet (go to Settings → Wallet & Apple Pay)
    • Make sure you've verified your domain with Apple Pay.
    • When using an iframe, its origin must match the top-level origin. Two pages have the same origin if the protocol, host (full domain name), and port (if one is specified) are the same for both pages.

    Chrome

    Chrome Mobile for Android

    Note that when using Google Pay, including Android Pay, with a test mode key, you might see an "Unrecognized app" warning. This is expected, and does not show up in live mode.

    Windows

    Collect shipping information

    To collect shipping information, begin by including requestShipping: true when creating the payment request.

    You may also provide an array of shippingOptions at this point, if your shipping options do not depend on the customer’s address.

    var paymentRequest = stripe.paymentRequest({ country: 'US', currency: 'usd', total: { label: 'Demo total', amount: 1099, }, requestShipping: true, // `shippingOptions` is optional at this point: shippingOptions: [ // The first shipping option in this list appears as the default // option in the browser payment interface. { id: 'free-shipping', label: 'Free shipping', detail: 'Arrives in 5 to 7 days', amount: 0, }, ], });
    const paymentRequest = stripe.paymentRequest({ country: 'US', currency: 'usd', total: { label: 'Demo total', amount: 1099, }, requestShipping: true, // `shippingOptions` is optional at this point: shippingOptions: [ // The first shipping option in this list appears as the default // option in the browser payment interface. { id: 'free-shipping', label: 'Free shipping', detail: 'Arrives in 5 to 7 days', amount: 0, }, ], });

    Next, listen to the shippingaddresschange event to detect when a customer selects a shipping address. Use the address to fetch valid shipping options from your server, update the total, or perform other business logic. The address data on the shippingaddresschange event may be anynomized by the browser to not reveal sensitive information that is not necessary for shipping cost calculation.

    Note that valid shippingOptions must be supplied at this point for the customer to proceed in the flow.

    paymentRequest.on('shippingaddresschange', function(ev) { if (ev.shippingAddress.country !== 'US') { ev.updateWith({status: 'invalid_shipping_address'}); } else { // Perform server-side request to fetch shipping options fetch('/calculateShipping', { data: JSON.stringify({ shippingAddress: ev.shippingAddress }) }).then(function(response) { return response.json(); }).then(function(result) { ev.updateWith({ status: 'success', shippingOptions: result.supportedShippingOptions, }); }); } });
    paymentRequest.on('shippingaddresschange', async (ev) => { if (ev.shippingAddress.country !== 'US') { ev.updateWith({status: 'invalid_shipping_address'}); } else { // Perform server-side request to fetch shipping options const response = await fetch('/calculateShipping', { data: JSON.stringify({ shippingAddress: ev.shippingAddress }) }); const result = await response.json(); ev.updateWith({ status: 'success', shippingOptions: result.supportedShippingOptions, }); } });

    Style the button

    Use the following parameters to customize the Element:

    const options = { paymentRequest, style: { paymentRequestButton: { type: 'default', // One of 'default', 'book', 'buy', or 'donate' // Defaults to 'default' theme: 'dark', // One of 'dark', 'light', or 'light-outline' // Defaults to 'dark' height: '64px', // Defaults to '40px'. The width is always '100%'. }, } } <PaymentRequestButtonElement options={options} />

    Using your own button

    If you wish to design your own button instead of using the paymentRequestButton Element, you may show your custom button based on the result of paymentRequest.canMakePayment(). Then, use paymentRequest.show() to display the browser interface when your button is clicked.

    When building your own button, follow Apple Pay’s Human Interface Guidelines.

    Use the Payment Request Button with Stripe Connect

    Connect platforms that either create direct charges or add the token to a Customer on the connected account must take some additional steps when using the Payment Request Button.

    1. On your frontend, before creating the PaymentRequest instance, set the stripeAccount option on the Stripe instance:

      var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx', { stripeAccount: 'CONNECTED_STRIPE_ACCOUNT_ID' });
      const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx', { stripeAccount: 'CONNECTED_STRIPE_ACCOUNT_ID' });
    2. Register all domains on which the Payment Request Button will be shown with Apple Pay. You can use the Stripe API for this, using your platform's secret key to authenticate the request, and setting the Stripe-Account header to your connected account's Stripe ID, as described in Making API calls for connected accounts.

      curl https://api.stripe.com/v1/apple_pay/domains \ -u {PLATFORM_SECRET_KEY}: \ -H "Stripe-Account: {{CONNECTED_STRIPE_ACCOUNT_ID}}" \ -d domain_name="example.com"

    See also

    Use of Apple Pay on the web is subject to the Apple Pay on the web terms of service.

    Was this page helpful?

    Feedback about this page?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    On this page