Quick Intro to the Payment Request API

The Payment Request API is a new JavaScript API that makes it easy to collect payment information that can then be sent to a payment processor such as Stripe. The aim is to improve payment UX by making it easy for users to save their card information with the browser. The API itself is developed openly through the W3C and with the participation of Google and Microsoft primarily.

The API is still quite new and support is limited to Chrome and Edge 15+ at this time. Support for Chrome desktop was added with Chrome 60, but you may still need to enable the Web Payments flag. The good thing is that it can easily be used as a progressive enhancement. Also note that the API only works for sites that are served over https.

Feature Testing

You’ll want to feature detect for the API’s availability:

if (window.PaymentRequest) {
  // Yep, we can go ahead! Our code goes here.
} else {
  // No support. Proceed the old school way
}

Payment Request Object

First, you’ll create a paymentRequest object and pass-in an array for accepted payment methods, an object with the payment details and a 3rd optional object for options:

const paymentMethods = [
  {
    supportedMethods: ['basic-card']
  }
];

const paymentDetails = {
  total: {
    label: 'What you pay',
    amount: {
      currency: 'USD',
      value: 80
    }
  }
};

const paymentRequest = new PaymentRequest(
  paymentMethods,
  paymentDetails
);

// ...

Notice the shape for paymentMethods and paymentDetails. With supportedMethods set to a value of basic-card, credit cards and debit cards will be accepted. You can also limit the supported payment networks. For example, with the following only Visa and MasterCard will be accepted:

const paymentMethods = [
  {
    supportedMethods: ['basic-card'],
    data: {
      supportedNetworks: ['visa', 'mastercard']
    }
  }
];

Payment Details

The following fields are required for the payment details object: total, label, amount, currency and value.

You can also add additional display items for the browser payment UI:

const paymentDetails = {
  total: {
    label: 'What you pay',
    amount: {
      currency: 'USD',
      value: 80
    }
  },
  displayItems: [
    {
      label: 'Promo code',
      amount: {
        currency: 'USD',
        value: -10
      }
    },
    {
      label: 'Taxes',
      amount: {
        currency: 'USD',
        value: 12
      }
    }
  ]
};

Space is often limited for the browser's payment UI, so you'll probably want to list out only top level fields like total price, taxes and shipping cost. Also note the the API doesn't perform any calculations, so your app is responsible for providing the pre-calculated amounts.


With the optional 3rd argument, you can request additional information from the user such as their name, email and phone number:

// ...

const options = {
  requestPayerName: true,
  requestPayerEmail: true
};

const paymentRequest = new PaymentRequest(
  paymentMethods,
  paymentDetails,
  options
);

Initiating the UI and Completing

We’re pretty much all set, expect nothing will happen until we call the show method on the payment request object. show() returns a promise, so it’s thenable:

// ...

const paymentRequest = new PaymentRequest(
  paymentMethods,
  paymentDetails,
  options
);

paymentRequest
  .show()
  .then(paymentResponse => {
    return paymentResponse.complete().then(() => {
      // call backend API to process payment with data from `paymentResponse`
    });
  })
  .catch(err => {
    // API error or user cancelled the payment
    console.log('Error:', err);
  });

Simple enough! With this, the user will be presented with the UI from the browser. Calling complete() on the paymentResponse that’s returned will close-out the browser payment UI and another promise is returned so that we can call our backend to send the information collected and process the payment.

The payment UI in Chrome 60

Online payments can be hard. As you can see though, the Payment Request API is quite easy to work with and makes the user information gathering part of the payment very simple.

With the above code, the browser UI will close immediately when the user confirms the payment, and then our app can take over and show a loading indicator while the payment is being processed by the backend. Alternatively, we can keep the browser UI and it’s native loading indicator with something like this instead:

// ...

const paymentRequest = new PaymentRequest(
  paymentMethods,
  paymentDetails,
  options
);

paymentRequest
  .show()
  .then(paymentResponse => {
    return verifyPaymentWithBackend(paymentResponse).then(success => {
      if (success) {
        console.log('💵 Payment is complete!');
        return paymentResponse.complete('success');
      } else {
        return paymentResponse.complete('failure');
      }
    });
  })
  .catch(err => {
    // API error or user cancelled the payment
    console.log('Error:', err);
  });

With this, the browser payment UI will show a processing screen until the promise resolves or rejects. Here verifyPaymentWithBackend would be a function that calls your backend API for payment processing with the content of the paymentResponse.

To help test it out, here’s a mock that returns a promise that resolves to true after 2.5 seconds:

function verifyPaymentWithBackend(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(true);
    }, 2500);
  });
}

Browser Support

Can I Use payment-request? Data on support for the payment-request feature across the major browsers from caniuse.com.

🔎 Here's an excellent resource to go more in-depth and learn the ins and outs of the Payment Request API.

✖ Clear

🕵 Search Results

🔎 Searching...