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 Prop | Description |
---|---|
projectId | Unique identifier of your project. Selected in the top navigation of dev console. |
collectionId | Identifier 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 |
recipient | The 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 name | Event Description |
---|---|
quote:status.changed | Triggered when the price is calculated or if the price changes. |
payment:preparation.ready | Triggered when Crossmint is ready to proceed. |
payment:preparation.succeeded | Triggered when checkout is ready for payment. |
payment:process.started | Triggered when the user has finished entering their card details and clicked Pay. |
payment:process.succeeded | Triggered when payment has been successfully authorized (capturing only occurs after transaction:fulfillment.succeeded ) |
payment:process.rejected | Triggered if a user's card has been rejected. |
payment:preparation.failed | Triggered if a failure has occurred after checkout. |
quote:status.invalidated | Triggered when a new quote is retrieved and invalidates the previous existing one. |
Note
The
unitPrice
insidepayment: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 name | Event Description |
---|---|
order:process.started | Triggered when payment has been accepted and minting begins. |
order:process.finished | Triggered when all transactions have succeeded. |
transaction:fulfillment.succeeded | Triggered when an NFT has been delivered successfully. |
transaction:fulfillment.failed | Triggered 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 code | Error message | How to resolve the error |
---|---|---|
payments:mint-config.invalid | Error parsing parameter mintConfig. | The value of the mintConfig must be an object type. Ensure you are passing in a valid object. |
payments:payment-method.invalid | The param 'paymentMethod' must be a string | paymentMethod 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.invalid | The email {USERS_EMAIL} is not valid | The email value passed inside the recipient was found to be invalid. Ensure that a valid email address is being passed. |
payments:client-id.invalid | Invalid provided clientId | The ClientID provided in clientID does not exist. Ensure that you are using the correct ClientID and its associated environment. |
payments:minting-contract.missing | This API can only be used for clientIds with minting contract information setup | We were unable to find a registered contract on your Crossmint collection. Register a smart contract in the developer console. |
payments:collection.disabled | Owner 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.unavailable | Buying with Crossmint is currently disabled for this project. | Please contact Crossmint support. |
payments:collection.unverified | You must complete collection verification to enable credit card payments for this collection. | Ensure that you have completed collection verification. |
payments:project.unverified | This project is pending verification by Crossmint | Ensure you have completed creator KYC. |
payments:collection.sold-out | This one went quick! Unfortunately all the available items have been sold | Your collection has sold out. Ensure proper messaging is in the UI. |
payments:collection.not-live | This sale is not live yet | Your mint is not yet live. Ensure proper messaging is in the UI. |
payments:collection.sale-ended | This sale has ended on {time} UTC. | Your mint has ended. Ensure proper messaging is in the UI. |
payments:user-wallet.limit-reached | You 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-whitelisted | Your wallet is not eligible for this purchase | The 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
Updated 2 days ago