2.iii. Build Your Own Payment UI using Crossmint Embedded

Create a fully custom NFT purchase experience, natively integrated into your website without pop-ups or redirects. You can build your own checkout UI with custom branding and still let Crossmint handle payments, fraud, minting and even creating wallets for users who don't have one.

🚧

Enterprise-only feature

Embedding Crossmint into your own website in this modality is only enabled for customers in the Enterprise tier. Contact sales to request access.

1. Install the Crossmint Client SDK

# run this command from the root directory of your project
yarn add @crossmint/client-sdk-react-ui
# run this command from the root directory of your project
yarn add @crossmint/client-sdk-vue-ui
<!-- 
  If possible include in the <head> tag of your site. 
  You can also place right before the <crossmint-pay-button> code if necessary
-->
<script src="https://unpkg.com/@crossmint/client-sdk-vanill[email protected]/lib/index.global.js"></script>

2. Register your smart contract

Follow our contract registration guide to learn how to register your smart contract with Crossmint.

3. Integrate the embedded checkout component in your app

As opposed to the popup checkout you get with the CrossmintPayButton, the embedded checkout allows you to customize and keep the entire checkout flow within your site.


The embedded checkout component

// Add this import line at the top
import { CrossmintPaymentElement } from "@crossmint/client-sdk-react-ui";

function App() {
  return (
    <div>
      <CrossmintPaymentElement
        projectId="_YOUR_PROJECT_ID_"
        collectionId="_YOUR_COLLECTION_ID_"
        environment="_ENVIRONMENT_"
        recipient={{
          email: "_USERS_EMAIL_ADDRESS_",
          wallet: "_USERS_WALLET_ADDRESS_"
        }}
        currency="_CURRENCY_"  // USD only, more coming soon
        locale="_LANGUAGE_"  // en-US only, more coming soon
        uiConfig={{
          colors: {
            background: '#000814',
            backgroundSecondary: '#001D3D',
            backgroundTertiary: '#EEEEEE',
            textPrimary: '#FFFFFF',
            textSecondary: '#EEEEEE',
            accent: '#FFC300',
            danger: '#FFC300',
            textLink: '#FFC300'
          },
          fontSizeBase: '0.91rem',
          spacingUnit: '0.274rem',
          borderRadius: '4px',
          fontWeightPrimary: '400',
          fontWeightSecondary: '500'
        }}
        mintConfig={{
          type: "erc-721",
          amount: "_NUMBER_OF_NFTS_",
          totalPrice: "_PRICE_IN_NATIVE_TOKEN_",
          // your custom minting arguments...
        }}
        onEvent={onEvent}
      />
    </div>
  );
}

export default App;

// Add this import line at the top
import { CrossmintPaymentElement } from "@crossmint/client-sdk-react-ui";

function App() {
  return (
    <div>
      <CrossmintPaymentElement
        projectId="_YOUR_PROJECT_ID_"
        collectionId="_YOUR_COLLECTION_ID_"
        environment="_ENVIRONMENT_"
        recipient={{
          email: "_USERS_EMAIL_ADDRESS_",
          wallet: "_USERS_WALLET_ADDRESS_"
        }}
        currency="_CURRENCY_"  // USD only, more coming soon
        locale="_LANGUAGE_"  // en-US only, more coming soon
        uiConfig={{
          colors: {
            background: '#000814',
            backgroundSecondary: '#001D3D',
            backgroundTertiary: '#EEEEEE',
            textPrimary: '#FFFFFF',
            textSecondary: '#EEEEEE',
            accent: '#FFC300',
            danger: '#FFC300',
            textLink: '#FFC300'
          },
				  fontSizeBase: '0.91rem',
				  spacingUnit: '0.274rem',
				  borderRadius: '4px',
				  fontWeightPrimary: '400',
				  fontWeightSecondary: '500'
        }}
        onEvent={onEvent}
      />
    </div>
  );
}

export default App;
// Add this import line at the top
<script setup>
import { CrossmintPaymentElement } from "@crossmint/client-sdk-vue-ui";
</script>

<template>
  <CrossmintPaymentElement
    projectId="_YOUR_PROJECT_ID_"
    collectionId="_YOUR_COLLECTION_ID_"
    environment="_ENVIRONMENT_"
    :recipient="{ email: '_USERS_EMAIL_ADDRESS_', wallet: '_USERS_WALLET_ADDRESS_' }"
    currency="_CURRENCY_"  // USD only, more coming soon
    locale="_LANGUAGE_"  // en-US only, more coming soon
    :ui-config="{
      colors: {
        background: '#000814',
        backgroundSecondary: '#001D3D',
        backgroundTertiary: '#EEEEEE',
        textPrimary: '#FFFFFF',
        textSecondary: '#EEEEEE',
        accent: '#FFC300',
        danger: '#FFC300',
        textLink: '#FFC300'
      },
      fontSizeBase: '0.91rem',
      spacingUnit: '0.274rem',
      borderRadius: '4px',
      fontWeightPrimary: '400',
      fontWeightSecondary: '500'
    }"
    :mint-config="{
      type: 'erc-721',
      quantity: '_NUMBER_OF_NFTS_',
      totalPrice: '_PRICE_IN_NATIVE_TOKEN_',
      // your custom minting arguments...
    }"
    @event="onEvent"
  />
</template>
// Add this import line at the top
<script setup>
import { CrossmintPaymentElement } from "@crossmint/client-sdk-vue-ui";
</script>

<template>
  <CrossmintPaymentElement
    projectId="_YOUR_PROJECT_ID_"
    collectionId="_YOUR_COLLECTION_ID_"
    environment="_ENVIRONMENT_"
    :recipient="{ email: '_USERS_EMAIL_ADDRESS_', wallet: '_USERS_WALLET_ADDRESS_' }"
    currency="_CURRENCY_"
    locale="_LANGUAGE_"
    :ui-config="{
      colors: {
        background: '#000814',
        backgroundSecondary: '#001D3D',
        backgroundTertiary: '#EEEEEE',
        textPrimary: '#FFFFFF',
        textSecondary: '#EEEEEE',
        accent: '#FFC300',
        danger: '#FFC300',
        textLink: '#FFC300'
      },
      fontSizeBase: '0.91rem',
      spacingUnit: '0.274rem',
      borderRadius: '4px',
      fontWeightPrimary: '400',
      fontWeightSecondary: '500'
    }"
    @event="onEvent"
  />
</template>

Explaining the component's props

Component PropDescription
projectIdUnique identifier of your project. Selected in the top navigation of dev console.
collectionIdIdentifier of the specific collection. Can be found in left hand navigation.
environment (optional)The environment you want the SDK to target. By default it targets production.

Options: production, staging
recipientThe recipient's object has two fields, email and wallet (optional). If a wallet gets passed the NFT will be directly minted to the wallet. Otherwise, by default, we will create a new custodial wallet for the email and mint to it.
currency (optional)The currency being used for payment.

Options: USD (More options coming soon to the embedded checkout)
locale (optional)The language being used in the checkout.

Options: en-US (More options coming soon to the embedded checkout)
uiConfig (optional)Allows you to customize the checkout UI.
onEvent (optional)Triggers a callback function every time an event occurs during the mint.

UI Customization

You can fully customize the elements inside of the embedded checkout UI using the tags inside of the uiConfig. Here's a complete list of the tags and which elements they apply to.

📘

Note

Targeted elements will be highlighted in red.

background

Targets the background of the checkout element.

backgroundSecondary

Targets the background of the input fields.

backgroundTertiary

Targets the Pay button background.

textPrimary

Targets: input labels, input placeholders, Crossmint's TOS footer text.

textSecondary

Targets the Pay button text.

accent

Targets the input fields border when in an active state.

textLink

Targets the Crossmint's terms link.

danger

Targets the different errors that appear under the inputs.

fontSizeBase

Targets the font size of all text. The default value is: 0.91rem.

spacingUnit

Targets the height of all input fields. The default value is: 0.274rem.

borderRadius

Targets the border radius of all input fields.

fontWeightPrimary

Targets the font-weight of all the input placeholders.

fontWeightSecondary

Targets the font-weight of all input labels.


4. Displaying progress, success, and errors in your UI

The onEvent prop is used to listen to all the events that occur during the checkout process. However, the events that emit during the minting phase are passed through useCrossmintEvents Here is a complete table of all the event types:


Payment Events

Event nameEvent Description
quote:status.changedTriggered when the price is calculated or if the price changes.
payment:preparation.readyTriggered when Crossmint is ready to proceed.
payment:preparation.succeededTriggered when checkout is ready for payment.
payment:process.startedTriggered when the user has finished entering their card details and clicked Pay.
payment:process.succeededTriggered when payment has been successfully authorized (capturing only occurs after transaction:fulfillment.succeeded)
payment:process.rejectedTriggered if a user's card has been rejected.
payment:preparation.failedTriggered if a failure has occurred after checkout.
quote:status.invalidatedTriggered when a new quote is retrieved and invalidates the previous existing one.

📘

Note

The unitPrice inside payment:preparation.succeeded includes Crossmints fees.

Example payment event responses

{
  totalPrice: {
    amount: "30.00",
    currency: "usd"
  },
  lineItems: [
    {
      metadata: {
        description: "Crossmint Collection Description",
        imageUrl: "https://www.crossmint.com/assets/crossmint/logo.svg",
        title: "Crossmint Collection Name (set in dev console)"
      },
      price: {
        amount: "20.00",
        currency: "usd"
      },
      gasFee: {
        amount: "10.00",
        currency: "usd"
      },
      quantity: 3
    }
  ]
}

{}
{}
{}
{
  orderIdentifier: "0aa4229f-4a55-4d9c-b39e-6a92cd6dfecb"
}


{
  error: {
    message: "Insufficient funds in credit card",
    code: "payments:payment-rejected.insufficient-funds"
  }
}


{
  error: {
    message: "Item has been sold already",
    code: "payments:collection.sold-out"
  }
}


{
  wallet: "0x1234abcd..."
}
{}
{
  "type": "payment:process.succeeded",
  "payload": {
    "orderIdentifier": "ba094f55-8529-41bd-a001-d465687433a4"
  }
}

Payment code examples

// Add this import line at the top
import { CrossmintPaymentElement } from "@crossmint/client-sdk-react-ui";

function App() {
  function onEvent(event) {
    switch (event.type) {
      case "quote:status.changed":
        const { quote } = event.payload;
        console.log("quote", quote);
      case "payment:preparation.succeeded":
        const { totalQuote } = event.payload;
        console.log("totalQuote", totalQuote);
        break;
      case "payment:process.started":
        console.log("payment:process.started");
        break;
      case "payment:process.succeeded":
        const { orderIdentifier } = event.payload;
        console.log("orderIdentifier", orderIdentifier);
        break;
      default:
        break;
    }
  }
  return (
    <div>
      <CrossmintPaymentElement
        projectId="_YOUR_PROJECT_ID_"
        collectionId="_YOUR_COLLECTION_ID_"
        onEvent={onEvent}
      />
    </div>
  );
}

export default App;

<script setup>
import { CrossmintPaymentElement } from "@crossmint/client-sdk-vue-ui";

function onEvent(event) {
    switch (event.type) {
        case "payment:preparation.succeeded":
            // Triggered when checkout is ready for payment.
            break;
        case "payment:process.started":
            // Triggered when the user has finished entering their card details and clicked Pay.
            break;
        case "quote:status.changed":
            // Triggered when the price is calculated or if the price changes.
            break;
        case "payment:process.succeeded":
            // Triggered when payment has been successfully captured.
        case "payment:rejected":
            // Triggered if a user's card has been rejected.
        case "payment:preparation.failed":
            // Triggered if a failure has occurred after checkout.
    }
}

</script>

<template>
  <CrossmintPaymentElement 
    projectId="_YOUR_PROJECT_ID_"
    collectionId="_YOUR_COLLECTION_ID_"
    @event="onEvent" />
</template>

Minting Events

Event nameEvent Description
order:process.startedTriggered when payment has been accepted and minting begins.
order:process.finishedTriggered when all transactions have succeeded.
transaction:fulfillment.succeededTriggered when an NFT has been delivered successfully.
transaction:fulfillment.failedTriggered when capturing an NFT fails.

Example minting event responses

{}
{
  successfulTransactionIdentifiers: ["0aa4229f-4a55-4d9c-b39e-6a92cd6dfecb"],
  failedTransactionIdentifiers: [""],
  totalPrice: {
    amount: 20.66,
    currency: "USD"
  },
  verification: {
  required: true,
  url: ""
  }
}

// EVM
{
  "contractAddress": "",
  "price": {
    "amount": 1,
    "currency": "USD"
  },
  "tokenIds": ["50"],
  "transactionIdentifier": "511999eee-f7fb-4b9c-9b9a-1b9b9b9b9b9b",
  "txId": ""
}


//Solana
{
  "mintHash": "",
  "price": {
    "amount": 1,
    "currency": "USD"
  },
  "transactionIdentifier": "511999eee-f7fb-4b9c-9b9a-1b9b9b9b9b9b",
  "txId": "0xabcd1234...."
}



error: {
    code: "payments:transaction-error.generic",
    message: "An error occurred while processing the transaction. Any undelivered item will be refunded."
}

Minting code examples

import { useCrossmintEvents } from "@crossmint/client-sdk-react-ui";

function Minting(order) {
  const { listenToMintingEvents } = useCrossmintEvents({
    environment: "staging",
  }); // Specifying the environment is optional. It defaults to "production"

  listenToMintingEvents(
    { orderIdentifier: `${order.orderIdentifier}` },
    (event) => {
      switch (event.type) {
        case "order:process.started":
          break;
        case "order:process.finished":
          break;
        case "transaction:fulfillment.succeeded":
          break;
        case "transaction:fulfillment.failed":
          break;
        default:
          break;
      }
    }
  );

  return <div>Minting....</div>;
}

export default Minting;

<script setup>
import { useCrossmintEvents } from "@crossmint/client-sdk-vue-ui";

const { listenToMintingEvents } = useCrossmintEvents();

listenToMintingEvents({ orderIdentifier: route.query.orderIdentifier?.toString()! }, (event) => {
    switch (event.type) {
        case "order:process.finished":
            // All transactions have finished
            break;
        case "transaction:fulfillment.succeeded":
            // A transaction has finished
            break;
        case "transaction:fulfillment.failed":
            // A transaction has failed
            break;
        case "order:process.started":
            // Capturing NFTs process has started
            break;
    }
});
</script>

<template>
  <CrossmintPaymentElement 
    projectId="_YOUR_PROJECT_ID_"
    collectionId="_YOUR_COLLECTION_ID_"
    @event="onEvent"
  />
</template>

Error events

Whenever an error event is emitted it will be passed through theonEvent callback and the error message will be displayed on the checkout frontend.

Error codeError messageHow to resolve the error
payments:mint-config.invalidError parsing parameter mintConfig.The value of the mintConfig must be an object type. Ensure you are passing in a valid object.
payments:payment-method.invalidThe param 'paymentMethod' must be a stringpaymentMethod only accepts a string value. Ensure that you are passing in a string containing one of these possible values: "fiat", "ETH, and "SOL".
payments:email.invalidThe email {USERS_EMAIL} is not validThe email value passed inside the recipient was found to be invalid. Ensure that a valid email address is being passed.
payments:client-id.invalidInvalid provided clientIdThe ClientID provided in clientID does not exist. Ensure that you are using the correct ClientID and its associated environment.
payments:minting-contract.missingThis API can only be used for clientIds with minting contract information setupWe were unable to find a registered contract on your Crossmint collection. Register a smart contract in the developer console.
payments:collection.disabledOwner of the collection has payments disabled. If you are the owner, please enable them in Crossmint's developer console.Ensure that payments are enabled in the developer console.
payments:collection.unavailableBuying with Crossmint is currently disabled for this project.Please contact Crossmint support.
payments:collection.unverifiedYou must complete collection verification to enable credit card payments for this collection.Ensure that you have completed collection verification.
payments:project.unverifiedThis project is pending verification by CrossmintEnsure you have completed creator KYC.
payments:collection.sold-outThis one went quick! Unfortunately all the available items have been soldYour collection has sold out. Ensure proper messaging is in the UI.
payments:collection.not-liveThis sale is not live yetYour mint is not yet live. Ensure proper messaging is in the UI.
payments:collection.sale-endedThis sale has ended on {time} UTC.Your mint has ended. Ensure proper messaging is in the UI.
payments:user-wallet.limit-reachedYou have reached the maximum number of NFTs purchased for this stage of the sale.The user has minted the max amount of NFTs allowed per wallet. Ensure proper messaging is in the UI.
payments:user-wallet.not-whitelistedYour wallet is not eligible for this purchaseThe user is not eligible to mint. Ensure proper messaging is in the UI.

5. Post-integration

To ensure a seamless user experience after checkout, consider implementing the following features in your post-checkout UI.

Accessing NFTs

Provide clear instructions on viewing and managing NFTs on Crossmint after a successful purchase. Direct users to this FAQ page as a straightforward solution.

Direct link to purchased NFT

Incorporate a button on the post-checkout UI that directs users to a Crossmint login page. After logging in, they will be automatically redirected to view their purchased NFT in Crossmint.

You can implement this feature using a URL.

https://www.crossmint.com/user/collection/<CHAIN>:<CONTRACT_ADDRESS>:<TOKEN_ID>

<CHAIN>: Replace with the chain being used. Supported types: eth, poly, bsc.
<CONTRACT_ADDRESS>: Replace this with the collections smart contract address.
<TOKEN_ID>: Replace this with the id of the NFT that was minted.
https://www.crossmint.com/user/collection/sol:<MINT_ADDRESS>

<MINT_ADDRESS>: Replace with the mint address of the minted NFT