Strong Customer Authentication (SCA) with Stripe

6 min read
by Robbie Averill
August 6, 2020

As technology evolves, so do the requirements around security. Payment processing is an area of infosec that has always been important, but becomes more so by the day as more and more people use the internet to shop.

Gone are the days of emailing a check to the business you just bought some software from online— let’s welcome the days of identifying yourself with your eyes, or your DNA! These enhancements in security are a result of changing digital environments over time and the need to react to changes in technological capabilities.

Paying for things online is now easier than ever. It can be done with a tap of the side button on your phone, a glance at your camera’s optical recognition, or re-using a saved credit card from a previous shopping experience.

What is SCA?

Enter Strong Customer Authentication (SCA) — like multi factor authentication but for payments. This new specification for handling online payments mandates that payment providers must require a second factor of authentication during processing.

A second factor is one of the following:

  • Something you (the customer) know (e.g. a password or PIN number)
  • Something you have (e.g. a security key or mobile phone)
  • Something you are (e.g. DNA, biometric scanning)

As of the 14th September, 2019, Strong Customer Authentication (SCA) became a requirement in the EU for European merchants to sell to European customers. While the implementation of SCA has been partially delayed, it is expected to be fully enforced by New Years Eve 2020.

How SCA impacts businesses

What does this mean for you, as an online merchant? It means that your existing eCommerce checkout flows need to be updated to handle this second factor of authentication when the customer purchases from you.

If you use Stripe with Commerce.js then you won’t need to do too much to make this happen!

Without SCA, a standard checkout flow with Stripe and Commerce.js would involve letting the user fill out the checkout form, enter their payment information, then tokenizing the card information using Stripe‘s JavaScript SDK, and sending the Stripe token to Chec via Commerce.js to complete the order.

A new standard with SCA

We’ve updated our checkout handling for Stripe payments to allow for the new Stripe Payment Intents API, which supports SCA payments.

Your updated flow would be that you use Stripe Elements to render a credit card form on your checkout, then when submitting your checkout, use Stripe’s SDK to register the card as a payment method. You can then pass the payment method ID in the payment.stripe.payment_method_id parameter to Commerce.js when you capture the order.

In the background, Commerce.js will create a Payment Intent using your provided payment method ID, and attempt to confirm it automatically. If Stripe identifies that the payment method requires further verification, Commerce.js will reply with a 402 “Payment Required” error which looks like this:

{
  "error": {
    "type": "requires_verification",
    "message": "The specified payment method requires further verification",
    "param": "pi_1HF6CXBKo6EzmBn8zXWeICof_secret_kRW4qg96roeelTqrUDj0dXfYc"
  },
  "status_code": 402
}

Notice the param field here? That’s a payment_intent_client_secret - you can use this to continue verifying the payment method using Stripe’s SDK. When Stripe is finished doing that it will provide you with a payment intent ID. You can use this as the payment.stripe.payment_intent_id parameter and re-submit your call to capture the order with Commerce.js. This time Commerce.js will detect the payment intent ID, load it and attempt to confirm it again. Assuming all goes well this time, the order will be confirmed as normal.

Easy huh? Let’s take a look at an example.

Firstly you’d set up Stripe Elements to render your payment form:

// Render your payment form
const elements = stripe.elements();
const card = elements.create('card');
card.mount('#payment-form');

Style the form as necessary using the Elements API.

Next, write your logic for what to do when the customer clicks your “Place order” button.

/**
 * Handle successful checkout capture!
 */
const handleOrderCaptured = () => {
  alert('Order placed!');
};

/**
 * Handle failures in checkout capture!
 */
const handleOrderCaptureFailed = (error) => {
  console.log(error);
};

/**
 * Capture the order! Note: we expect frontend validation has already been done.
 *
 * Steps: create payment method, capture order with Chec, handle any card actions
 *        with Stripe, then optionally capture the order again
 */
const captureOrder = (checkoutToken) => {
  const orderData = {
    // ... get your order data from your checkout form
    payment: { gateway: 'stripe' },
  };

  // Register the payment method with Stripe's SDK
  stripe.createPaymentMethod({ type: 'card', card: elements.card })
    .then(response => {
      // Handle any errors from Stripe
      if (response.error) {
        alert('Something went wrong!' + response.error);
        return;
      }

      // Continue to process the order
      commerce.checkout.capture(checkoutToken, {
        ...orderData,
        payment: {
          ...orderData.payment,
          payment_method_id: response.paymentMethod.id,
        },
      })
        .then(handleOrderCaptured)
        .catch(error => {
          // Check for further verification required
          if (error.data.error.type === 'required_verification') {
            stripe.handleCardAction(error.data.error.param)
              .then(result => {
                // Handle errors from Stripe's SDK verification of card
                if (result.error) {
                  handleOrderCaptureFailed(result.error);
                  return;
                }
                // Verification successful, re-submit Commerce.js checkout
                commerce.checkout.capture(checkoutToken, {
                  ...orderData,
                  payment: {
                    ...orderData.payment,
                    payment_intent_id: response.paymentIntent.id,
                  },
                })
                  .then(handleOrderCaptured)
                  .catch(handleOrderCaptureFailed);
          } else {
            // Failed to place order!
            handleOrderCaptureFailed(error);
          }
    });  
};

If the card used requires a second factor of authentication, Stripe’s JavaScript SDK will handle this for you by presenting the user with a popup or modal and asking them to confirm their identity.

When the process of validating the card eventually completes, you should re-submit your checkout capture call with Commerce.js using the payment intent ID this time (rather than the payment method ID you initially used).

Stripe SCA

We’ve rolled these changes out to our hosted Checkouts and Spaces already. When you add this to your own Commerce.js integration, head over to the Stripe docs for testing and you’ll find a list of test credit card numbers that trigger various types of second factor authentication for you to test with.

Interested to find out more? Read up on Stripe’s blog post about SCA payments, or dive into the official specs if you’re really keen.

You might also like