WEB3DEV

Cover image for Como criar uma experiência perfeita de Checkout de NFT
Isabela Curado Nehme
Isabela Curado Nehme

Posted on

Como criar uma experiência perfeita de Checkout de NFT

3 de fevereiro de 2023

Este guia mostrará a você como criar uma experiência perfeita de checkout de NFT para seus usuários usando o Magic e a Stripe.

Vamos passar pelo seguinte:

  1. Criação de um contrato de edição para o NFT.
  2. Criação de uma conta na Stripe para aceitar pagamentos.
  3. Criação de uma aplicativo Next.js de pilha completa (full-stack).

Vamos começar!

Implantação de um contrato ERC1155

Usarei um contrato de edição para os NFTs, mas você também pode usar o ERC721 para criar contratos exclusivos!

Para implantar o contrato, vá para a página “Contracts” (contratos) no seu painel da Thirdweb e clique em “Deploy new contract” (implantar novo contrato):

Você será direcionado para a nossa página Explore (explorar) - onde pode navegar por contratos inteligentes construídos pelos melhores protocolos na Web3 e implantá-los em apenas alguns cliques.

Observação: você também pode utilizar a CLI da Thirdweb para configurar um ambiente de contrato inteligente através da execução do comando abaixo a partir do seu terminal:

npx thirdweb create contract
Enter fullscreen mode Exit fullscreen mode

Isso o guiará por um fluxo de etapas fácil de seguir para que você crie seu contrato. Saiba mais sobre isso no nosso guia CLI.

Caso contrário, vamos voltar ao Explore:

Aqui, selecione o contrato inteligente de sua escolha. Para este guia, vamos utilizar o contrato Edition (de edição) para criar nossa coleção NFT. Clique em “Deploy Now” (implantar agora) para começar o fluxo de implantação:

Defina seu contrato inteligente com uma imagem, um nome, uma descrição etc. e configure qual endereço de carteira receberá os fundos das vendas primárias e secundárias:

Você pode selecionar qualquer rede que desejar; para este guia, estou escolhendo a Mumbai. Saiba mais sobre as diferentes redes que temos disponíveis abaixo:

https://blog.thirdweb.com/guides/which-network-should-you-use/

Uma vez que um contrato for implantado, vá para a guia NFT e clique em “mint” (cunhar). Uma gaveta será aberta onde você precisa adicionar os metadados do seu NFT.

Depois que você tiver inserido os metadados, clique em “Mint NFT” (cunhar NFT) e aprove a transação!

Criação de uma conta na Stripe

Vá até o painel da Stripe e crie uma nova conta:

Uma vez criada a conta, você verá as chaves de API:

Salve estas chaves porque vamos precisar delas mais tarde!

Criação de um aplicativo Magic.link

Vá para o painel do Magic.link e crie um novo aplicativo Magic Auth desta forma:

Criação do aplicativo Next.js de pilha completa

Antes de começarmos, você pode acessar o código fonte completo para este modelo no GitHub.

Usando a CLI da Thirdweb, crie um novo projeto Next.js e TypeScript com o React SDK pré-configurado para você utilizando o seguinte comando:

npx thirdweb create app --next --ts
Enter fullscreen mode Exit fullscreen mode

Por padrão, a rede é a principal (Mainnet); você precisará mudá-la para a rede na qual você implantou seus contratos inteligentes para dentro do arquivo _app.tsx.

// Esta é a Id da cadeia (chainId) na qual seu contrato trabalhará.
const activeChainId = ChainId.Mumbai;
Enter fullscreen mode Exit fullscreen mode

Agora, adicione as variáveis env em um novo arquivo .env.local assim:

NEXT_PUBLIC_MAGIC_LINK_API_KEY=<your-magic-link-api-key>
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=<your-stripe-publishable-key>
STRIPE_SECRET_KEY=<your-stripe-secret-key>
PRIVATE_KEY=<your-private-key>
NEXT_PUBLIC_DOMAIN=<your-domain>
Enter fullscreen mode Exit fullscreen mode

IMPORTANTE: usar chaves privadas como uma variável env é vulnerável a ataques e não é a melhor prática. Estamos fazendo isso neste guia para abreviar, mas recomendamos fortemente o uso de um gerenciador secreto para armazenar sua chave privada.

IMPORTANTE: Chaves privadas.

Se você estiver utilizando variáveis de ambiente para armazenar sua chave privada (não recomendado) , nunca confirme sua chave privada no Git.

  • Crie um arquivo .gitignore.
  • Adicione .env ao seu arquivo .gitignore.

Se você enviar sua chave privada para o GitHub, sua carteira será drenada!

Adicionar o Magic Auth

No _app.tsx, adicione magicConnectors desta forma:

const magicLinkConnector = new MagicConnector({
  options: {
    apiKey: process.env.NEXT_PUBLIC_MAGIC_LINK_API_KEY as string,
    rpcUrls: {
      [ChainId.Mumbai]: "https://rpc-mumbai.maticvigil.com",
    },
  },
});

// Array dos conectores da carteira que você quer usar para o seu dApp.
const connectors = [magicLinkConnector];
Enter fullscreen mode Exit fullscreen mode

E passe para o ThirdwebProvider assim:

<ThirdwebProvider
      desiredChainId={activeChainId}
      walletConnectors={connectors}
      chainRpc={{
        [ChainId.Mumbai]: "https://rpc-mumbai.maticvigil.com",
      }}
    >
      <Component {...pageProps} />
    </ThirdwebProvider>
Enter fullscreen mode Exit fullscreen mode

A seguir, para adicionar o login com o e-mail, precisamos pegar o e-mail do usuário e conectar com o Magic usando isto:

<>
  <h2 style={{ fontSize: "1.3rem" }}>Login With Email</h2>
  <form
    onSubmit={(e) => {
      e.preventDefault();
      connectWithMagic({ email });
    }}
    style={{
      width: 500,
      maxWidth: "90vw",
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      flexDirection: "row",
      gap: 16,
    }}
  >
    <input
      type="email"
      placeholder="Your Email Address"
      style={{ width: "90%", marginBottom: 0 }}
      onChange={(e) => setEmail(e.target.value)}
    />
    <button>Login</button>
  </form>
</>
Enter fullscreen mode Exit fullscreen mode

Vamos precisar destes hooks (ganchos) também:

const connectWithMagic = useMagic();
const [email, setEmail] = useState<string>("");
const address = useAddress();
Enter fullscreen mode Exit fullscreen mode

Agora que o usuário pode entrar, mostraremos ao usuário You are signed in as: walletAddress (Você está logado como: walletAddress) se o usuário está lá assim:

<div>
  {address ? (
    <>
      <p>You are signed in as: {address}</p>
    </>
  ) : (
    <>
      <h2 style={{ fontSize: "1.3rem" }}>Login With Email</h2>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          connectWithMagic({ email });
        }}
        style={{
          width: 500,
          maxWidth: "90vw",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          flexDirection: "row",
          gap: 16,
        }}
      >
        <input
          type="email"
          placeholder="Your Email Address"
          style={{ width: "90%", marginBottom: 0 }}
          onChange={(e) => setEmail(e.target.value)}
        />
        <button>Login</button>
      </form>
    </>
  )}
</div>
Enter fullscreen mode Exit fullscreen mode

Também mostraremos ao usuário qual NFT ele está comprando quando estiver logado, portanto, precisaremos obter os detalhes do NFT do contrato desta forma:

const { contract } = useContract("YOUR_CONTRACT_ADDRESS", "edition");
const { data: nft } = useNFT(contract, 0);
Enter fullscreen mode Exit fullscreen mode

E, embaixo da linha you are signed in as (você está logado como), adicione o seguinte:

<div>
  {nft?.metadata && (
    <ThirdwebNftMedia
      metadata={nft?.metadata}
      style={{ width: 200, height: 200 }}
    />
  )}
  <h2>{nft?.metadata?.name}</h2>
  <p>{nft?.metadata?.description}</p>
  <p>Price: 100$</p>
</div>
Enter fullscreen mode Exit fullscreen mode

Lidar com pagamentos usando a Stripe

Agora, precisamos configurar algumas APIs e lógica para receber os pagamentos. Então, primeiramente, instalaremos alguns pacotes:

npm i stripe micro @stripe/stripe-js @stripe/react-stripe-js # npm

yarn add stripe micro @stripe/stripe-js @stripe/react-stripe-js # yarn
Enter fullscreen mode Exit fullscreen mode

Agora, vamos criar uma API na pasta pages/api chamada stripe_intent.ts e adicionar o seguinte:

import { NextApiRequest, NextApiResponse } from "next";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: "2022-11-15",
});

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  const { address } = req.body;

  const amount = 10000;

  try {
    const payment_intent = await stripe.paymentIntents.create({
      amount: amount,
      currency: "usd",
      description: "Payment description",
      automatic_payment_methods: {
        enabled: true,
      },
      metadata: { address },
    });

    return res.status(200).json(payment_intent);
  } catch (err) {
    const errorMessage =
      err instanceof Error ? err.message : "Internal server error";
    return res.status(500).json({ statusCode: 500, message: errorMessage });
  }
};

export default handler;
Enter fullscreen mode Exit fullscreen mode

💡 Lembre-se de atualizar o valor e a moeda. O valor estará na subdivisão de um moeda, portanto, para USD, será em centavos. Para obter isso, basta multiplicar o valor em USD por 100.

Agora, faremos uma solicitação de API no front-end para obter e armazenar o clientSecret em um estado desta maneira:

const [clientSecret, setClientSecret] = useState("");

  useEffect(() => {
    if (address) {
      fetch("/api/stripe_intent", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          address,
        }),
      })
        .then((res) => res.json())
        .then((data) => {
          setClientSecret(data.client_secret);
        });
    }
  }, [address]);
Enter fullscreen mode Exit fullscreen mode

Como precisamos enviar o endereço como metadado, não enviaremos uma solicitação a menos que o endereço esteja lá.

Agora, também precisamos mostrar os elementos do cartão onde as pessoas irão inserir os detalhes de cartão. Para isso, vamos preparar nossas opções que a Stripe necessita desta forma:

const stripe = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY as string
);

const appearance: Appearance = {
  theme: "night",
  labels: "above",
};

const options: StripeElementsOptions = {
  clientSecret,
  appearance,
};
Enter fullscreen mode Exit fullscreen mode

Sinta-se livre para mexer na aparência baseada na sua necessidade!

Agora, logo antes do cartão NFT, exibirei o formulário dentro de um elemento empacotador da Stripe, desta maneira:

{clientSecret && (
    <Elements options={options} stripe={stripe}>
      <Form />
    </Elements>
  )}
Enter fullscreen mode Exit fullscreen mode

Precisamos importar tudo isso desta forma:

import { Elements } from "@stripe/react-stripe-js";
import {
  Appearance,
  loadStripe,
  StripeElementsOptions,
} from "@stripe/stripe-js";
Enter fullscreen mode Exit fullscreen mode

Você deve estar se perguntando de onde tiramos esse formulário; vamos criar isso agora!

Para fazer isso, crie uma nova pasta de componentes e um novo arquivo Form.tsx dentro dela. Uma vez que o arquivo for criado, adicione o seguinte:

import {
  PaymentElement,
  useElements,
  useStripe,
} from "@stripe/react-stripe-js";
import React, { useState } from "react";

const Form = () => {
  const [isLoading, setIsLoading] = useState(false);
  const stripe = useStripe();
  const elements = useElements();
  const URL = process.env.NEXT_PUBLIC_DOMAIN || "http://localhost:3000";

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    if (!stripe || !elements) {
      return console.error("not loaded");
    }

    setIsLoading(true);

    await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: URL,
      },
    });

    setIsLoading(false);
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button disabled={isLoading || !stripe || !elements}>
        <span>{isLoading ? "Loading..." : "Pay now"}</span>
      </button>
    </form>
  );
};

export default Form;
Enter fullscreen mode Exit fullscreen mode

Este código adicionará elementos de pagamento onde os usuários podem inserir seus detalhes e um botão de pagamento.

Enviar um NFT em um pagamento com sucesso

Podemos agora aceitar pagamentos, mas para onde enviamos o NFT? Para obter atualizações da Stripe, vamos usar um webhook. Para criar um webhook, crie um arquivo webhook.ts em pages/api e adicione o seguinte:

import Stripe from "stripe";
import { buffer } from "micro";
import { NextApiRequest, NextApiResponse } from "next";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, {
  apiVersion: "2022-11-15",
});

const webhookSecret = process.env.WEBHOOK_SECRET_KEY as string;

export const config = {
  api: {
    bodyParser: false,
  },
};

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  const buf = await buffer(req);
  const sig = req.headers["stripe-signature"];

  const sdk = ThirdwebSDK.fromPrivateKey(
    process.env.PRIVATE_KEY as string,
    "mumbai"
  );

  const nftCollection = await sdk.getContract(
    "YOUR_EDITION_ADDRESS",
    "edition"
  );

  let event;

  if (buf && sig) {
    try {
      event = stripe.webhooks.constructEvent(buf, sig, webhookSecret);
    } catch (err) {
      return res.status(400).send(`Webhook Error: ${(err as Error).message}`);
    }

    const data = JSON.parse(String(buf));

    if (event.type === "payment_intent.succeeded") {
      const paymentMethod = event.data.object as any;
      const address = paymentMethod.metadata.address;

      const tx = await nftCollection.erc1155.mintAdditionalSupplyTo(
        address,
        0,
        1
      );

      console.log(tx);

      console.log(
        `PaymentIntent was successfull for: ${data.data.object.amount}`
      );
    }
  }
  return res.json({ received: true });
};

export default handler;
Enter fullscreen mode Exit fullscreen mode

💡 Lembre-se de atualizar seu endereço de contrato.

Nós configuramos nosso webhook, mas precisamos obter as atualizações sobre ele também; para teste na hospedagem local (localhost), precisamos testar para instalar a Stripe CLI. Depois de ter instalado a Stripe CLI, execute o login na Stripe para conectar na sua conta. Agora, finalmente, execute este comando para ouvir aos webhooks em tempo real:

stripe listen --forward-to localhost:3000/api/webhook
Enter fullscreen mode Exit fullscreen mode

Uma vez que você executar este comando, ele mostrará uma chave secreta de assinatura:

Adicione isso ao ser arquivo .env.local desta forma:

WEBHOOK_SECRET_KEY=secret_key_here
Enter fullscreen mode Exit fullscreen mode

Se você experimentar o aplicativo agora com qualquer cartão de teste que a Stripe fornece, verá que o pagamento será processado no painel e o fornecimento de NFT aumentará de uma vez 🥳.

Mas nada acontece depois que o usuário concluir o pagamento. Então, para que eles saibam que o pagamento foi bem sucedido ou se teve algum erro, mostraremos uma mensagem no formulário como a seguir:

const [message, setMessage] = useState<null | string | undefined>(null);
const [isSuccess, setIsSuccess] = useState(false);
const stripe = useStripe();

useEffect(() => {
  if (!stripe) {
    return;
  }

  const clientSecret = new URLSearchParams(window.location.search).get(
    "payment_intent_client_secret"
  );

  if (!clientSecret) {
    return;
  }

  stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent }) => {
    switch (paymentIntent?.status) {
      case "succeeded":
        setIsSuccess(true);
        setMessage("Your payment was successfull!");
        break;
      case "processing":
        setMessage("Your payment is processing.");
        break;
      case "requires_payment_method":
        setMessage("Your payment was not successful, please try again.");
        break;
      default:
        setMessage("Something went wrong.");
        break;
    }
  });
}, [stripe]);
Enter fullscreen mode Exit fullscreen mode
return (
  <>
    {message ? (
      <>
        {isSuccess && (
          <a
            href="https://testnets.opensea.io/assets/mumbai/YOUR_EDITION_ADDRESS/0"
            target="_blank"
            rel="noreferrer"
          >
            Check out your NFT
          </a>
        )}
        <h1>{message}</h1>
      </>
    ) : (
      <form onSubmit={handleSubmit}>
        <PaymentElement />
        <button disabled={isLoading || !stripe || !elements}>
          <span>{isLoading ? "Loading..." : "Pay now"}</span>
        </button>
      </form>
    )}
  </>
);
Enter fullscreen mode Exit fullscreen mode

Agora, se o usuário retornar, haverá uma ID na URL, que podemos verificar usando a Stripe e mostrar uma mensagem baseada nisso. Também mostramos um botão para o NFT; aqui, você precisa atualizar o endereço. Se testarmos agora, tudo funciona perfeitamente! 🎉

Implantação

Para colocar este aplicativo em produção, você precisa fazer algumas etapas de implantação.

Stripe

Vá para o seu painel da Stripe e ative os pagamento. Você precisa fornecer alguns detalhes para ativar sua conta:

Após a conclusão, vá até a seção do desenvolvedor e clique na guia chaves de API. Você poderá ver suas chaves de API de produção. Armazene-as com segurança em algum lugar porque precisaremos delas quando implantamos nosso aplicativo.

Por fim, precisamos adicionar um ponto de extremidade no webhook para a Stripe. Vá até a seção webhooks em developers (desenvolvedores) e clique em “Add an endpoint” (adicionar um ponto de extremidade). Então, adicione seus detalhes:

  • A URL do ponto de extremidade seria https:///api/webhook.
  • Insira qualquer coisa como descrição, se você quiser.
  • Atualize a versão para a mais recente, pois estamos usando essa.
  • Selecione payment_intent.succeeded como um evento para ouvir.

Você pode usar qualquer serviço de hospedagem; usarei o Vercel. Portanto, crie um repositório do GitHub e envie o código que você escreveu. Vá para o Vercel e crie um novo projeto. Você precisará selecionar o repositório do GitHub que criou e clicar em “import” (importar). Agora, adicione todas as variáveis env atualizadas:

NEXT_PUBLIC_MAGIC_LINK_API_KEY=<your-magic-link-api-key>
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=<your-stripe-publishable-key>
STRIPE_SECRET_KEY=<your-stripe-secret-key>
WEBHOOK_SECRET_KEY=<your-stripe-webhook-secret-key>
PRIVATE_KEY=<your-private-key>
NEXT_PUBLIC_DOMAIN=<your-domain>
Enter fullscreen mode Exit fullscreen mode

Certifique-se de atualizar as chaves da Stripe para as que nós acabamos de receber!

Conclusão

Este guia nos ensinou como criar uma experiência perfeita de checkout de NFT para seus usuários ao permiti-los se inscrever pelo Magic.link e aceitar pagamentos com cartão de crédito usando a Stripe!

Você aprendeu bastante, agora, dê um tapinha nas suas costas e compartilhe seus aplicativos maravilhosos conosco! Se você quiser ver o código, confira o Repositório do GitHub.

Esse artigo foi escrito por Avneesh Agarwal e traduzido por Isabela Curado Nehme. Seu original pode ser lido aqui.

Latest comments (0)