Introduction

In this quickstart you’ll learn how to accept credit card payments (including Apple and Google Pay) using Crossmint’s headless APIs to purchase an NFT on the polygon-amoy testnet

Prerequisites

From Crossmint

  1. Create a developer account in the Staging Console.
  2. Create a new collection or import yours into the console, and have your collectionId ready.
    • Make sure you follow the maximum prices for collections set in staging outlined here
  3. Create a server-side API key with the orders.create, orders.update, and orders.read scopes enabled. More info on creating API keys here.

This Quickstart assumes that you'll be using the API Playground or cURL requests to make the API calls. This approach requires the use of a server-side API key.

If you would rather follow the quickstart while building a client-side app that makes requests to Crossmint directly from the browser, you must use a client-side API key. See the Server or Client Guide for more information.

To integrate in production/mainnet, you'll also need to complete account and collection verification. More information in the production launch guide.

External Prerequisites

  1. A Stripe dev account and console setup
  2. A provisioned stripePublishableKey from Stripe Console

Integration Steps

This quickstart will focus on the API calls to Crossmint necessary to implement headless checkout in your project, as well as the usage of the Stripe Payment Element to collect the user’s payment.

This guide assumes you are using React, however Crossmint’s headless fiat checkout can be used in all environments supported by stripe such as Web, iOS, Android, and React Native.

1. Create an Order

The first step in the headless checkout process is to create an order. An order is an object datastructure, that represents an intent to purchase in Crossmint's systems. This guide will create a basic order, and then update it with required info step-by-step.

You can also create the entire order in one API call if the necessary information is available at the time of order creation. This can be used for custom "one-click-checkout" experiences, should you wish to make them.

POST https://staging.crossmint.com/api/2022-06-09/orders

Refer to the complete create order API reference here.

{
    "payment": {
        "method": "stripe-payment-element"
    },
    "lineItems": {
        "collectionLocator": "crossmint:<collectionId>", // Use the collectionId you created in the Prerequisites section
        "callData": {
            "totalPrice": "0.0001"
        }
    }
}

For the comprehensive list of supported currencies and chains, view the Supported Currencies page.

At this point you have successfully created an Order in the Crossmint system! The order is currently in the quote phase and is awaiting a recipient to be set. Setting a recipient does not mean that we are sending the NFT to them; it is merely used to verify that the intended recipient is a valid address, before beginning gathering payment info.

Optional - View the Order in the Console

Congratulations! You've made your first contact with Crossmint's systems. You can see your request show up in the developer console. Navigate to the Orders tab for your collection in the Staging Console and enter the orderId returned from your API response to find your incomplete order object and status.

Crossmint Headless NFT Checkout - view order

2. Update the Order with Recipient

Next, you will set who's the intended recipient of the order by specifying a wallet address or email. When a recipient is passed as an email, Crossmint will automatically create a custodial wallet associated with this email, that can be accessed by logging in to the (staging) Crossmint Wallet or from your website if you're using whitelabel wallets.

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

Remember to ensure you're using the right type of API key! See the Server or Client Guide for more info.

Refer to the complete edit order API reference here.

Legally, for card payments, an email must be specified in order to deliver a receipt to your user, either in recipient.email or in payment.receiptEmail.

If one is not specified, the API will not return the fields necessary to collect the user’s payment.

{
    "recipient": {
        "email": "test@example.com"
    }
}

3. Render the Stripe Payment Element

After a recipient and/or a receipt email has been specified, the API will return the fields necessary to render the Stripe Payment Element and collect the user’s payment. The two fields are stripePublishableKey and stripeClientSecret, which can be found within order.payment.preparation.

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

4. Submitting Payment

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).

6. 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>

Next Steps

You can now receive payments for anything on the blockchain, and display it in whatever UI you choose to build.

Headless checkout is gated in production. To get started, contact us.