Overview

Stripe.js V3 libraries allow you to accept payments from shoppers through 25+ different payment methods with a single, embeddable UI component. Stripe powers this through their Payment Element, which requires both front-end and server-side work in order to accept a payment. This is different to the current Violet Checkout flow, which takes in credit card or Stripe token information in order to process a payment.

Violet checkout has been enhanced to enable you to use Stripe Payment Elements to create easier, more secure payment flows for your shoppers. This document walks you through how you can integrate with these changes. For more information, such as details on how Violet is powering this or to build multi-step confirmations, please refer to the Stripe Guides, available here.

Note: This document assumes that you have already set up Violet within your software ecosystem and integrated with our APIs. If this is not the case, please follow and read through Quick Start guide.

Creating the Order

The general process to Create a Cart remains the same, with the following changes:

Stripe requires both a Stripe Key and a Payment Intent Client Secret to create a Stripe PaymentElement. You will need to request them during the Apply Payment Method step. This step should only be done once you are ready to confirm the order and after all customer details, shipping, and billing information has been added.

JSON
{
  "stripe_key": "pk_test_UHg8oLvg4rrDCbvtqfwTE8qd",
  "payment_intent_client_secret": "pi_3MbFHUK29KDiBVld0N8b8EDd_secret_cb5AOMAxXyZeJfy4wylgnVd3C"
}

Fetching the Payment Intent Client Secret

To fetch the payment intent client secret, you need to call Apply Payment Method with the following payload:

JSON
{
  "intent_based_capture": true
}

This flag cannot be combined with any other flag that this request body takes. Since there is no Payment Method attached to the order (which is what we previously did at this step), the order can no longer be submitted until additional work has been done on the front-end.

Integrating with Stripe Payment Elements

The Stripe Payment Element is a single, embeddable UI component that accepts payment information from your shoppers and attaches it to the Violet order. Now that you have a Stripe Key and Payment Intent Client Secret, you’re ready to render the Stripe Payment Element form and collect payment details from your shopper.

Set up Stripe.js

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

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

Add and configure the Payment Element to your payment page

To use the Stripe Payment element, wrap your checkout page in a Stripe Elements provider. This is where you will also use the Stripe Key and Payment Intent Client Secret provided in the Order response.

TypeScript
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('{{STRIPE_KEY_FROM_ORDER_RESPONSE}}', {});

function App() {
  const options = {
    clientSecret: '{{PAYMENT_INTENT_CLIENT_SECRET_FROM_ORDER_RESPONSE}}',

    // Fully customizable with appearance API.
    appearance: {
      /*...*/
    },
  };

  return (
    <Elements stripe={stripePromise} options={options}>
      <CheckoutForm />
    </Elements>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

Add the Payment Element to your Checkout Form

In your checkout form, render the Stripe PaymentElement component:

TypeScript
import React from 'react';
import { PaymentElement } from '@stripe/react-stripe-js';

const CheckoutForm = () => {
  return (
    <form>
      <PaymentElement />
      <button>Submit</button>
    </form>
  );
};

export default CheckoutForm;

The Payment Element renders a dynamic form that allows your customer to pick a payment method. Depending on their payment method, the form automatically requests that the customer fills in all necessary payment details.

You can customize the Payment Element to match the design of your site by passing the appearance object into options when creating the Elements provider.

By default, the Payment Element only collects necessary billing address details. In cases where you need to collect full billing address, like for calculating tax for digital goods and services, you can use the Address Element in Billing mode.

Confirm the Payment

Add a listener to your Checkout form and then call Stripe.confirmPayment to attach the payment method your shopper provided to the payment intent sent by Violet and authorize the payment. Stripe will automatically request all the information that is needed for that payment method.

TypeScript
import React from 'react';
import {
  useStripe,
  useElements,
  PaymentElement,
} from '@stripe/react-stripe-js';

export default function CheckoutForm() {
  const stripe = useStripe();
  const elements = useElements();

  const handleSubmit = async (event) => {
    // We don't want to let default form submission happen here,
    // which would refresh the page.
    event.preventDefault();

    if (!stripe || !elements) {
      // Stripe.js has not yet loaded.
      // Make sure to disable form submission until Stripe.js has loaded.
      return;
    }

    const result = await stripe.confirmPayment({
      elements,
      confirmParams: {
        // Make sure to change this to your payment completion page
        return_url: 'http://localhost:3000',
      },
    });

    stripePaymentMethodHandler(result);
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button type="submit" disabled={!stripe}>
        Submit Payment
      </button>
    </form>
  );
}

If a successful result is returned from the call to Stripe, a payment method has successfully been attached to the payment intent that Violet provided.

Submit the order and render post-processing information to your Shopper

You can submit the order in the stripePaymentMethodHandler method called above. This is where you will call Violet Checkout submit. If any pricing modifications were made to the Order before this step, such as an updated Shipping method or another item being added to the cart, you will need to re-create the payment intent and show a new PaymentElement form:

TypeScript
const stripePaymentMethodHandler = async (result) => {
  if (result.error) {
    // Show error in payment form
  } else {
    // Otherwise submit order to Violet
    const res = await fetch('{{baseUrl}}/checkout/cart/:cart_id/submit', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        app_order_id: //orderId for this order in your system,
      })
    });

    const orderResponse = await res.json();

    handleServerResponse(orderResponse);
  }
}

Optional: If there are further actions required for your customer during the payment process, such as 3D secure authentication, Violet will respond with the following status on the order:

TypeScript
{
    "payment_status": "REQUIRES_ACTION",
}

If this is the case, you can use the same payment_intent_client_secret and the Stripe.handleNextAction method to request the additional information from the shopper.

TypeScript
const handleServerResponse = async (orderResponse) => {
  if (response.error) {
    // Show error from server on payment form
  } else if (orderResponse.payment_status.requires_action) {
    // Use Stripe.js to handle the required next action
    const { error: errorAction, paymentIntent } = await stripe.handleNextAction(
      {
        clientSecret: orderResponse.payment_intent_client_secret,
      }
    );

    if (errorAction) {
      // Show error from Stripe.js in payment form
    } else {
      // Actions handled, submit order to Violet once again
    }
  } else {
    // No actions needed, show success message
  }
};