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:
- Criação de um contrato de edição para o NFT.
- Criação de uma conta na Stripe para aceitar pagamentos.
- 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
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
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;
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>
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];
E passe para o ThirdwebProvider assim:
<ThirdwebProvider
desiredChainId={activeChainId}
walletConnectors={connectors}
chainRpc={{
[ChainId.Mumbai]: "https://rpc-mumbai.maticvigil.com",
}}
>
<Component {...pageProps} />
</ThirdwebProvider>
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>
</>
Vamos precisar destes hooks (ganchos) também:
const connectWithMagic = useMagic();
const [email, setEmail] = useState<string>("");
const address = useAddress();
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>
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);
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>
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
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;
💡 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]);
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,
};
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>
)}
Precisamos importar tudo isso desta forma:
import { Elements } from "@stripe/react-stripe-js";
import {
Appearance,
loadStripe,
StripeElementsOptions,
} from "@stripe/stripe-js";
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;
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;
💡 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
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
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]);
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>
)}
</>
);
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>
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.
Top comments (0)