Stripe

Overview

This guide will help you integrate the Stripe payment gateway into your Chec/Commerce.js storefront using Stripe Elements. Stripe Elements includes full PCI compliance, support for 3D secure payments (also known as strong customer authentication or SCA), and supports various payment methods including Apple Pay and Google Pay.

In this guide

  1. Overview of the payment flow between Commerce.js and Stripe.
  2. An example implementation:
  • Capturing an order with a card entered into a Stripe Elements field
  • Handling 3D secure payments (SCA), only when it's required

Payment flow

Chec/Commerce.js supports creating 3D Secure card payments with Stripe. You can read more in the Stripe documentation about why this is important. Because of this, the payment flow required in the checkout has a few stages. It is important that Commerce.js is involved in the payment capturing process so that you do not take payments without an order to go with it.

Step by step

  1. First, you need to add your credit card form to the page using Stripe Elements
  2. When the user presses a button to complete the payment/capture the order, you need to create a payment method with the Stripe SDK and provide the Elements "card".
  3. Once Stripe returns with the payment method, you need to tell Commerce.js to capture the order, and provide the Stripe payment method ID that Chec should charge.
  4. Chec/Commerce.js will either:
    • Complete the order if the card does not require additional authentication and provide the order details, or
    • Return with a 402 Payment Required response, indicating that you must continue with the 3D secure payment flow. In this case, the API provides the "client secret" for the payment intent that was created.
  5. If the 3D secure flow is required, you need to call the handleCardAction API provided by the Stripe SDK to allow the customer to interface with their bank for additional authentication
  6. Once Stripe indicates that the additional authentication has been successful, you need to update Chec/Commerce.js with the updated payment intent ID by calling capture again, but with the provided intent ID instead of the payment method ID.
  7. The Chec API will confirm the payment intent, charge the card, and complete the order.

Example implementation

This implementation assumes that you are familiar with getting a Stripe payment form integrated into your checkout form using Stripe Elements. For more information on how to do this, please use the docs provided by Stripe.

This implementation also assumes you are familiar with some modern JavaScript features including async/await, "shorthand property names", and object destructuring assignment.

Capturing an order with Stripe Elements

Once you are ready to capture an order (the customer has entered their card details and clicked to continue), you need to create a payment method using the "card element" provided by the Stripe SDK:

// Create a stripe SDK instance using your test key
const stripe = new Stripe('pk_test_stripeKey');
// Create a commerce.js instance using your sandbox key
const commerce = new Commerce('pk_test_commercejsKey');

// When mounting the Stripe elements form to your page, you should have a line like this which provides a card element
const card = elements.create('card')

// ... Integrate Elements onto your page, and other fields required for capturing a checkout with Commerce.js.

// Create a function that can be called when a "complete order" button is clicked
async function captureOrder() {
  // This process includes a few API calls, so now is a good time to show a loading indicator

  // Create a payment method using the card element on the page
  const paymentMethodResponse = await stripe.createPaymentMethod({ type: 'card', card });

  if (paymentMethodResponse.error) {
    // There was some issue with the information that the customer entered into the payment details form.
    alert(paymentMethodResponse.error.message);
    return;
  }

  try {
    // Use a checkout token ID generated that was generated earlier, and any order details that may have been collected
    // on this page. Note that Commerce.js checkout tokens may already have all the information saved against them to
    // capture an order, so this extra detail may be optional.
    const order = await commerce.checkout.capture(checkoutTokenId, {
      ...orderDetails,
      // Include Stripe payment method ID:
      payment: {
        gateway: 'stripe',
        stripe: {
          payment_method_id: paymentMethodResponse.paymentMethod.id,
        },
      },
    })

    // If we get here, the order has been successfully captured and the order detail is part of the `order` variable
    console.log(order);
    return;
  } catch (response) {
    // There was an issue with capturing the order with Commerce.js
    console.log(response);
    alert(response.message);
    return;
  } finally {
    // Any loading state can be removed here.
  }
}

This process should cover many use cases, but doesn't support cards that required additional authentication using 3D secure payments. The next section covers this.

Handling 3DS/SCA requirements

Handling 3D secure payments involves adding a few extra API calls, and an additional form shown to the customer. Luckily Stripe's SDK provides a handy toolkit for doing this. We have to add additional logic when Chec/Commerce.js indicates that the order was not captured because the card used requires further authentication:

// We pick up where we left off with the existing try..catch block:
try {
  const order = await commerce.checkout.capture(checkoutTokenId, {
    ...orderDetails,
    // Include Stripe payment mmethod ID:
    payment: {
      gateway: 'stripe',
      stripe: {
        payment_method_id: paymentMethodResponse.paymentMethod.id,
      },
    },
  })
  return;
} catch (response) {
  // We can check if the error is not related to additional payment steps being required
  if (response.statusCode !== 402 || response.data.error.type !== 'requires_verification') {
    // Handle the error as usual because it's not related to 3D secure payments
    console.log(response);
    return;
  }

  // Otherwise we need to continue with the 3DS process. We can use the Stripe SDK to show a modal to the customer.
  // Commerce.js provides us the "param" attribute that refers to a PaymentIntent that was created with Stripe by the
  // Chec API.
  const cardActionResult = await stripe.handleCardAction(response.data.error.param)

  if (cardActionResult.error) {
    // The customer failed to authenticate themselves with their bank and the transaction has been declined
    alert(cardActionResult.error.message);
    return;
  }

  // Now we can try to capture the order again, this time passing the payment intent ID:
  try {
    const order = await commerce.checkout.capture(checkoutTokenId, {
      payment: {
        gateway: 'stripe',
        stripe: {
          payment_intent_id: cardActionResult.paymentIntent.id,
        },
      },
    });

    // If we get here the order has been captured successfully and the order detail is available in the order variable
    console.log(order);
    return;
  } catch (response) {
    // Just like above, we get here if the order failed to capture with Commrece.js
    console.log(response);
    alert(response.message);
  }
}

For more information on 3D secure payment and SCA check out our blog post.