In this guide, you will add crypto payments to the embedded checkout, enabling users able to pay for NFTs with cryptocurrency. These payments work cross-chain: for example, users can pay for Optimism NFTs with mainnet ETH or Solana.

The first step to integrate is getting a signer reference for your user’s wallet. Check out the options below:

  • Ethers.js V5

  • Using an API Signer

Using a Local Signer

You can clone this nextjs repo to get up running immediately:

See below for a step by step walkthrough.

1. Create a new Next.js application

Check out the steps to setup a nextjs application here.

2. Add ethers to the project

This example is using nextjs with app router. Specify ethers version 5 when installing it.

pnpm i ethers@5.7.2

3. Edit the /app/page.tsx file

Replace the file contents with the code snippet below.

"use client";

import React from "react";
import Crossmint from "./components/Crossmint";
import useEthersSigner from "./hooks/useEthersSigner";

const Page: React.FC = () => {
  const { signer, accounts } = useEthersSigner();

  return (
    <div className="container mx-auto max-w-md bg-white p-4">
      <div className="flex flex-col">
        {signer && <Crossmint signer={signer} accounts={accounts} />}

export default Page;

4. Add a custom hook to initialize ethers

Create a new folder named hooks in the /app directory and add a file named useEthersSigner.ts. Then add the code in the block below.

"use client";

import { useState, useEffect, useRef } from "react";
import { ethers } from "ethers";

declare global {
  interface Window {
    ethereum: any;

const useEthersSigner = () => {
  const [accounts, setAccounts] = useState([]);
  const signerRef = useRef<ethers.providers.JsonRpcSigner>();

  useEffect(() => {
    const initializeEthers = async () => {
      if (typeof window.ethereum !== "undefined") {
        try {
          const accounts = await window.ethereum.request({
            method: "eth_requestAccounts",

          const provider = new ethers.providers.Web3Provider(window.ethereum);

          const signer = provider.getSigner();
          signerRef.current = signer;
        } catch (error) {
          console.error("User denied account access", error);
      } else {
        console.log("MetaMask is not installed!");

  }, []);

  return { signer: signerRef.current, accounts };

export default useEthersSigner;

5. Setup the CrossmintPayElement in a new file named Crossmint.tsx

The key details here are adding paymentMethod="ETH" and the signer property.

"use client";

import React from "react";
import { CrossmintPaymentElement } from "@crossmint/client-sdk-react-ui";
import { ethers } from "ethers";

type CrossmintProps = {
  signer: ethers.providers.JsonRpcSigner;
  accounts: string[];

const Crossmint: React.FC<CrossmintProps> = ({ signer, accounts }) => {
  const projectId = process.env.NEXT_PUBLIC_PROJECT_ID as string;
  const collectionId = process.env.NEXT_PUBLIC_COLLECTION_ID as string;
  const environment = process.env.NEXT_PUBLIC_ENVIRONMENT as string;

  return (
        address: accounts[0],
        signAndSendTransaction: async (transaction) => {
          const response = await signer.sendTransaction({
            type: transaction.type!,

          return response.hash;
        type: "erc-721",
        totalPrice: "0.001",
        _quantity: "1",
      onEvent={(event) => {
        if (event.type === "payment:process.succeeded") {
            "This is a basic example and does not logic to update UI upon payment completion. Check the main branch of this repository for a full example."

export default Crossmint;
The emailInputOptions attribute should be removed when setting up embedded checkout to use cross-chain payments.

That’s it! 🎉

Users can now start paying with other cryptocurrencies. You will receive the proceeds in the native currency of the contract, regardless of how the user paid.

Check out the repo:

The example above is based on the simple branch in this linked repo. The main branch includes a more complete example with logic to detect minting events and update the UI.