The order lifecycle can be summarized as follows:

1

Order Creation or Update

Your application determines recipient info for the buyer. It can be an email wallet or wallet address. Create or update an order with this info to proceed to next step.

2

API Response

The API response returned from create/update order call(s) will include a payment.preparation object that your application uses to render the Stripe payment element.

3

Render Stripe Payment Element

Use the stripePublishableKey and stripeClientSecret returned in the API response to render the credit card checkout form.

4

User Completes Payment

The buyer completes checkout via credit card.
5

Poll for Status

Your application will poll the GET order status and update the UI as the order progresses to the next phase.

During the initial quote phase of the order the payment status will be requires-quote.

Once the quote phase is completed, the order enters the payment phase and will have the status awaiting-payment, which indicates that the order is ready to be paid.

See below the full list of possible statuses:

Payment StatusExplanation
requires-quotestill in the quote phase
awaiting-paymentready to submit payment
completedorder is in the delivery or order completion phase

Render the Stripe Payment Element

When the order is ready to accept payment, the API response will include an order.payment.preparation object, which contains two important properties to render the payment element. These properties are named: stripePublishableKey and stripeClientSecret. You can use these with the Stripe Payment Element package to collect the user’s credit card payment.

import { Elements, PaymentElement } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";

function PaymentElementWrapper({
    stripePublishableKey,
    stripeClientSecret,
}: {
    stripePublishableKey: string;
    stripeClientSecret: string;
}) {
    return (
        <ElementsProviderWrapper stripePublishableKey={stripePublishableKey} stripeClientSecret={stripeClientSecret}>
            <PaymentElement className="w-full" />
            <SubmitButton />
        </ElementsProviderWrapper>
    );
}

function ElementsProviderWrapper({
    stripePublishableKey,
    stripeClientSecret,
    children,
}: {
    stripePublishableKey: string;
    stripeClientSecret: string;
    children: React.ReactNode;
}) {
    const stripePromise = loadStripe(stripePublishableKey);
    return (
        <Elements
            stripe={stripePromise}
            options={{ clientSecret: stripeClientSecret }}
            key={`${stripeClientSecret}-${stripePublishableKey}`}
        >
            {children}
        </Elements>
    );
}

// Will be completed in the next step
function SubmitButton() {
    return null;
}

The key property on the Elements component is important to re-render the payment element if either the stripeClientSecret or stripePublishableKey change, as these fields can't be 'live' updated

Handle Payment Confirmation

The Stripe Payment Element does not handle submitting the payment, so you will also need to create your own submit button.

First, at the top of the file, update the import for @stripe/react-stripe-js to be:

import { Elements, PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";

Then, update the SubmitButton component with the following code:

function SubmitButton() {
    const stripe = useStripe();
    const elements = useElements();

    async function handleSubmit() {
        if (!stripe || !elements) {
            console.error("[handleSubmit] 'stripe' or 'elements' has not loaded");
            return;
        }

        const { error } = await stripe.confirmPayment({
            elements,
            confirmParams: {
                return_url: "http://localhost:3000/success", // Some payment methods require a redirect after payment - in those cases you must provide a return_url, which should resume the order process - which would be the delivery phase
            },
            redirect: "if_required", // Can be 'if-required', or 'always' if you prefer to always redirect to the next page
        });

        if (error) {
            console.error("[handleSubmit] Failed to confirm payment", error);
            // Optionally: display the message to the user via the UI
            return;
        }

        console.log("[handleSubmit] Successfully confirmed payment");
    }

    return (
        <button onClick={handleSubmit} className="w-full px-4 py-3 bg-blue-500 text-white font-semibold">
            Submit
        </button>
    );
}

At this point, your app should look similar to the one displayed below (fill in the 2 input fields with the data you recieved in order.payment.preparation in order to see the live Payment Element).

Poll for Status Updates

After making the payment via whichever payment method, you'll need to poll the Get Order API to check on the delivery status and present this info to your user.

Refer to the complete get order API reference here.

GET https://staging.crossmint.com/api/2022-06-09/orders/<orderId>