Checkout with Stripe.js V3
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 how to build multi-step confirmations, please refer to Stripe’s Guides, available here.
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.
{
"stripe_key": "pk_test_UHg8oLvg4rrDCbvtqfwTE8qd",
"payment_intent_client_secret": "pi_3MbFHUK29KDiBVld0N8b8EDd_secret_cb5AOMAxXyZeJfy4wylgnVd3C"
}
The stripe_key
only changes at the environment level and is used to load the Stripe libraries. We recommend saving it as an environment variable in your client-side code so that you don’t need to re-render Stripe elements each time.
Fetching the Payment Intent Client Secret
To fetch the payment intent client secret, you need to call Apply Payment Method with the following payload:
{
"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 the below additional work has been done on the front-end.
The payment_intent_client_secret
does not need to change even if updates are made to the cart. You can use it for the life of your Cart. Violet will automatically ensure that it is up to date.
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.
You can find a fully integrated example of using stripe payment elements in our open source demo app on Github
Set up Stripe.js
Install the Stripe.js React libraries and the Stripe JS loader from the npm public registry:
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.
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:
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
When your shopper is ready to complete their cart and submit their order, you will need to confirm the payment. Note that you should only confirm a payment as the final step before calling /submit
on a cart to Violet since no other action can be taken on the cart that alters the cart total (like setting shipping methods or adding skus) after confirming 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.
The Stripe.confirmPayment
call does not charge the credit card. Instead, it only authorizes the amount that needs to be paid and ensures there are sufficient funds. Charges are only captured once an order has successfully been placed in the external merchant store.
If there are issues during submission, the shopper’s card is never charged.
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:
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:
{
"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.
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
}
};
Was this page helpful?