Enterprise feature. Contact us for access.

Introduction

This guide will show you how to accept credit card payments using Crossmint’s Embedded Checkout and Headless Checkout API for memecoin sales. You’ll learn how to:

  • Set up credit card payments for Solana memecoin purchases in JavaScript
  • Implement a checkout UI using Embedded or Headless Checkout
  • Track order status and delivery

Important Notes

Supported Memecoins

Crossmint runs compliance checks on all tokens to ensure they do not qualify as securities or currencies under applicable regulations. Transactions for tokens that are determined to be too similar to securities or currencies will fail.

Token Format

Currently, memecoin checkout only supports Solana network. - Token Format: solana:${tokenMintHash} - Examples:

  • TRUMP: solana:6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN

Delivery to External Wallets Only

Memecoin checkout only delivers memecoins to EOAs (Externally Owned Accounts), not Crossmint supported delivery solutions, such as on-the-fly wallet creation (both Crossmint custodial wallets and smart wallet), delivery Twitter handle, etc.

Merchant of Record

Crossmint remains the merchant of record for all transactions. Your buyers will still receive delivery receipts and transaction confirmations from Crossmint.

Prerequisites

1

Get API Keys

Get your API keys from the Crossmint Console

Token Specification

Currently, memecoin checkout only supports Solana network.

  • Token Format: solana:${tokenMintHash} (tokenMintHash is commonly known as contract address, CA, or mint hash)
  • Examples:
    • TRUMP: solana:6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN

If you want to check if a memecoin is supported by Crossmint follow our supported tokens API

External Prerequisites

  • A Solana wallet address to deliver the purchased memecoins

Option 1 - Embedded Memecoin Checkout

The fastest way to start selling memecoins is to adapt our embedded checkout solution to use fungibles.

Important Parameters

Before implementing the checkout, note these key parameters:

  • maxSlippageBps: Optional slippage tolerance (default provided if not specified)
  • receiptEmail: Required for delivering payment receipts
  • executionParameters.mode: Set to “exact-in” for memecoin purchases (specifies exact USD amount to spend). Exact-out is for NFT’s, exact-in is for fungibles.

Integration Steps

This guide will start from scratch with an empty Next.js application. You'll install the required @crossmint/client-sdk-react-ui dependency and add the embedded checkout component. To get started:

Set up the Project

1

Create a new Next.js application

npx create-next-app@latest

If you see this message, type y and press Enter to proceed:

Need to install the following packages:
  create-next-app@latest
Ok to proceed? (y)
2

Name your app `crossmint-embedded-checkout-demo` and accept the default options

What is your project named? crossmint-embedded-checkout-demo
Would you like to use TypeScript? Yes
Would you like to use ESLint? Yes
Would you like to use Tailwind CSS? Yes
Would you like to use `src/` directory? No
Would you like to use App Router? (recommended) Yes
Would you like to customize the default import alias? No
3

Change into the directory created in previous steps

cd crossmint-embedded-checkout-demo
4

Install @crossmint/client-sdk-react-ui

npm i @crossmint/client-sdk-react-ui
5

Open the project in your preferred code editor

Memecoin Embedded Integration

Next, we will set up a project file with Crossmint’s embedded checkout to accept memecoin purchases.

1

Add environment variables

Create .env.local in your project root:

NEXT_PUBLIC_CLIENT_API_KEY="_YOUR_CLIENT_API_KEY_"    # From API Keys page
NEXT_PUBLIC_TOKEN_ADDRESS="6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN" # Instead of a collectionId, we will use a token address. In this case, $trumps contract address
NEXT_PUBLIC_RECIPIENT_WALLET_ADDRESS="YOUR_SOLANA_WALLET_ADDRESS" # Add desired recipient wallet
NEXT_PUBLIC_RECEIPT_EMAIL="YOUR_EMAIL" # Add desired recipient email
2

Create the checkout page

Create /src/app/page.tsx with:

"use client";

import { CrossmintProvider, CrossmintEmbeddedCheckout } from "@crossmint/client-sdk-react-ui";

export default function Home() {
    const clientApiKey = process.env.NEXT_PUBLIC_CLIENT_API_KEY as string;
    const tokenAddress = process.env.NEXT_PUBLIC_TOKEN_ADDRESS as string;
    const recipientWalletAddress = process.env.NEXT_PUBLIC_RECIPIENT_WALLET_ADDRESS as string;

    return (
        <div className="flex flex-col items-center justify-start h-screen p-6 bg-white">
            <CrossmintProvider apiKey={clientApiKey}>
                <div className="max-w-[450px] w-full">
                    <CrossmintEmbeddedCheckout
                        recipient={{
                            walletAddress: recipientWalletAddress, // Wallet address to receive the memecoins
                        }}
                        lineItems={{
                            tokenLocator: `solana:${tokenAddress}`, // Token address in format solana:tokenAddress (e.g., solana:6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN for TRUMP token)
                            executionParameters: {
                                mode: "exact-in", // The execution method for the order. It tells Crossmint to operate in buying fungibles mode
                                amount: "5", // Amount in USD
                                maxSlippageBps: "500" // Optional - default slippage will be applied if not specified
                            }
                        }}
                        payment={{
                            receiptEmail: process.env.NEXT_PUBLIC_RECEIPT_EMAIL as string, // Email address to receive the receipt
                            crypto: {
                                enabled: false, // Only fiat is supported for memecoin purchases
                            },
                            fiat: {
                                enabled: true,
                            },
                            defaultMethod: "fiat",
                        }}
                    />
                </div>
            </CrossmintProvider>
        </div>
    );
}
3

Run your app

npm run dev

Visit http://localhost:3000 to see your checkout!

4

Test your app

Memecoin purchases are only supported in production, so pay with credit card in a small amount to test the flow.

Here’s how your embedded checkout will look after implementation:

🎉 Congratulations! You’ve successfully set up your embedded memecoin checkout. Check out the Next Steps section below to learn how to customize your integration.

Option 2 - Headless Memecoin Checkout

The headless checkout API allows complete control over your checkout experience, including:

  • Custom UI components and styling
  • Custom payment flow sequences
  • Integrated analytics and tracking
  • Custom error handling and retry logic
  • Branded confirmation pages

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.

Use the javascript code snippet below to create a starting point for your order. Alternatively, use the API playground to explore and create your own order.

const apiKey = "your-server-api-key"; // CHANGE THIS TO YOUR SERVER API KEY
const tokenId = "solana:6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN"; // trump token
const deliveryAddress = "your-solana-wallet-address"; // CHANGE THIS TO YOUR RECEIVING SOLANA WALLET ADDRESS
const receiptEmail = "your-email@example.com"; // CHANGE THIS TO YOUR EMAIL

const options = {
  method: 'POST',
  headers: {
    'X-API-KEY': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    lineItems: {
      tokenLocator: tokenId,
      executionParameters: {
        mode: "exact-in", // The execution method for the order. It also tells Crossmint to operate in buying fungibles mode
        amount: "1", // default currency USD
        maxSlippageBps: "500" // Optional, or else default autogenerated slippage will be applied
      }
    },
    payment: {
      method: "stripe-payment-element",
      receiptEmail: receiptEmail
    },
    recipient: {
      walletAddress: deliveryAddress
    }
  })
};

fetch('https://www.crossmint.com/api/2022-06-09/orders', options)
  .then(response => response.json())
  .then(response => console.log(JSON.stringify(response, null, 2)))
  .catch(err => console.error(err));

Note the following parameters in the request body:

  • maxSlippageBps: Optional, or else default autogenerated slippage will be applied.
  • receiptEmail: Required for credit card payments to deliver receipt
  • executionParameters.mode: The execution method for the order. Exact-out is for NFT’s, exact-in is for fungibles.

Render the Stripe Payment Element

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

The Stripe Payment Element will appear as shown below: a

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

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>

Example response from the Get Order API:

{
    "id": "order_xyz",
    "status": "completed",
    "phases": {
        "quote": { "status": "completed" },
        "payment": { "status": "completed" },
        "delivery": { "status": "completed", "details": "Memecoins delivered to specified wallet" }
    }
}

Understanding the Code

Quote Expiration

Price quotes are valid for 30 seconds. After expiration, you’ll need to request a new quote from the headless checkout API

Slippage

Crossmint applies the slippage specified in your API request via executionParameters.slippageBps. If not provided, Crossmint will use the default slippage configuration from Crossmint’s provider

Parameters Explained

The order creation request includes several important parameters:

Payment Object

  • method: Set to stripe-payment-element for credit card payments
  • currency: Set to usd for US Dollar payments
  • receiptEmail: Required for credit card payments to deliver receipt

Line Items Object

  • tokenLocator: Specifies the memecoin token address in the format solana:tokenAddress
  • callData:
    • amount: Amount to purchase in USD
    • slippageBps: Set to “500” for 5% slippage tolerance. If not provided, the default slippage will be applied from the available liquidity provider.

Order Lifecycle

The order goes through several phases: Learn more about order phases in the headless checkout guide

A summary of the phases is below:

  1. Quote Phase (30-second validity)

    • Initial price quote generated
    • Requires recipient information to proceed
  2. Payment Phase

    • Process credit card payment via Stripe
  3. Delivery Phase

    • Purchase memecoin with USDC
    • Apply specified slippage tolerance
    • Send transfer transactionsaction to recipient wallet
  4. Completion

    • Order marked as completed
    • Receipt email sent to recipient
    • Memecoins have been delivered to the recipient wallet

If the quote expires (after 30 seconds), you’ll need to create a new order to get updated pricing. You can choose the UX flow to handle this.

Next Steps

FAQ