Pular para o conteúdo principal

Integração com Ethereum e BNB Smart Chain

Este guia explica como receber pagamentos cripto não custodiais no Ethereum e na BNB Smart Chain com os contratos inteligentes de pagamento da Volet.com e ethers v6.

Os clientes conectam as carteiras deles, aprovam as transações e pagam diretamente na sua carteira de recebimento por meio de um contrato inteligente. Os fundos não são creditados em uma conta Volet.com.

Antes de continuar, veja Integração com contratos inteligentes para uma visão geral do fluxo de pagamento.

Visão geral da integração​

  1. Conecte a carteira do cliente e confirme a rede blockchain escolhida.
  2. Configure os contratos de pagamento e a sua carteira de recebimento.
  3. Crie um pedido no backend antes de iniciar o pagamento.
  4. Chame previewPayment() para calcular o valor total do pagamento.
  5. Chame transfer() para iniciar o pagamento. Em USDT, verifique a permissão de gasto (allowance) do token e solicite a aprovação se necessário.
  6. Salve o hash da transação de pagamento (txid) no pedido.
  7. Verifique a transação no backend antes de marcar o pedido como pago.

Antes de começar​

Você vai precisar de:

  • Carteira do cliente: MetaMask ou outra carteira compatível com EIP-1193 para pagamentos no navegador.
  • Biblioteca: ethers versão 6 para interagir com carteiras e contratos inteligentes.
  • Carteira do lojista: um endereço de carteira no formato Ethereum que você controla para receber pagamentos.
  • Backend e banco de dados: para criar pedidos, armazenar os dados das transações e verificar os pagamentos.
  • Provedor RPC: para obter as transações da blockchain e verificar a execução delas.

Para conectar carteiras móveis, você também vai precisar de um Project ID da Reown/WalletConnect.

Redes e ativos suportados​

RedeChain IDAtivos suportados
Ethereum Mainnet1ETH, USDT (ERC-20)
BNB Smart Chain56BNB, USDT (BEP-20)

Cada ativo usa o contrato processador de pagamentos correspondente. Os exemplos de código abaixo contêm os endereços dos processadores e dos tokens da configuração da integração.

Os endereços dos contratos, os decimais dos tokens e os parâmetros das funções estão na Referência de contratos inteligentes.

Antes de usar os exemplos em produção, confirme com a Volet.com os endereços dos processadores implantados e a interface do contrato.

Etapa 1. Conecte a carteira do cliente​

Use ethers v6 para conectar a carteira de navegador do cliente.

<script src="https://cdn.jsdelivr.net/npm/ethers@6/dist/ethers.umd.min.js"></script>

<button id="connect-wallet">Connect wallet</button>

<script>
let provider;
let signer;

document.getElementById('connect-wallet').addEventListener('click', async () => {
if (!window.ethereum) {
alert('Install a compatible wallet, such as MetaMask.');
return;
}

provider = new ethers.BrowserProvider(window.ethereum);
await provider.send('eth_requestAccounts', []);
signer = await provider.getSigner();

document.getElementById('connect-wallet').textContent =
await signer.getAddress();
});
</script>

O cliente deve se conectar à rede escolhida na sua página de pagamento:

  • Ethereum Mainnet (chainId: 1)
  • BNB Smart Chain (chainId: 56)

Se a carteira estiver em outra rede, solicite a troca de rede ou peça ao cliente para trocá-la manualmente.

Sempre verifique o chain ID ativo logo antes de enviar um pagamento, porque o cliente pode trocar de rede na carteira.

Etapa 2. Configure os contratos de pagamento​

Adicione ao seu aplicativo a ABI do processador de pagamentos e as funções do token.

const PROCESSOR_ABI = [
'function previewPayment(uint256 amount, bool buyerPaysCommission) view returns (uint256 commission, uint256 totalPay)',
'function transfer(address seller, uint256 amount, bool buyerPaysCommission, address optionalCommissionWallet) payable',
];

const ERC20_ABI = [
'function balanceOf(address owner) view returns (uint256)',
'function allowance(address owner, address spender) view returns (uint256)',
'function approve(address spender, uint256 value) returns (bool)',
];

const PROCESSORS = {
native: '0x2fceE245109Cf3c535489a6F52fe11b3BBd644FF',
usdt: '0xe4083ED0A1101615b9092E8cD2164e2f42B4571E',
};

const USDT_BY_CHAIN = {
1: {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
decimals: 6,
},
56: {
address: '0x55d398326f99059fF775485246999027B3197955',
decimals: 18,
},
};

O processador de pagamentos oferece duas funções principais:

  • previewPayment(amount, buyerPaysCommission) calcula a taxa de processamento e o valor total que o cliente deve pagar.
  • transfer(seller, amount, buyerPaysCommission, optionalCommissionWallet) executa o pagamento e distribui os fundos de acordo com a configuração de taxas do contrato.

O parâmetro amount usa as menores unidades do ativo. Use ethers.parseEther() para ETH e BNB e ethers.parseUnits() com os decimais corretos do token para USDT.

O parâmetro buyerPaysCommission define quem paga a taxa de processamento:

  • true: a taxa é somada ao pagamento do cliente.
  • false: a taxa é descontada do valor que o lojista recebe.

Use ethers.ZeroAddress em optionalCommissionWallet se não houver carteira de comissão de parceiro.

Sempre obtenha o valor total do pagamento de previewPayment(). Não calcule a taxa de processamento manualmente.

As definições completas dos parâmetros estão na Referência de contratos inteligentes.

Etapa 3. Receba ETH ou BNB​

Pagamentos em ETH e BNB nativos exigem uma única transação na carteira.

A mesma função funciona nas duas redes quando o cliente está conectado à blockchain correta.

async function payNative(signer, seller, humanAmount, buyerPaysCommission = true) {
if (!ethers.isAddress(seller)) {
throw new Error('Invalid merchant wallet');
}

const network = await signer.provider.getNetwork();

if (![1n, 56n].includes(network.chainId)) {
throw new Error('Unsupported network');
}

const processor = new ethers.Contract(
PROCESSORS.native,
PROCESSOR_ABI,
signer
);

const amount = ethers.parseEther(humanAmount);

const [, totalPay] = await processor.previewPayment(
amount,
buyerPaysCommission
);

const tx = await processor.transfer(
seller,
amount,
buyerPaysCommission,
ethers.ZeroAddress,
{ value: totalPay }
);

return tx.hash;
}

O value da transação deve ser igual ao totalPay retornado por previewPayment().

O cliente também precisa ter ETH ou BNB suficiente para cobrir a taxa de rede da blockchain.

A função retorna o hash da transação de pagamento (txid). Salve-o no pedido existente e verifique o pagamento antes de entregar o pedido.

Etapa 4. Receba USDT​

Pagamentos em USDT usam um contrato de token ERC-20 ou BEP-20 e podem exigir uma transação de aprovação adicional.

Antes de fazer um pagamento, seu aplicativo deve:

  1. Verificar o saldo de USDT do cliente com balanceOf().
  2. Verificar a permissão de gasto do processador com allowance().
  3. Se a permissão for insuficiente, solicitar a aprovação com approve().
  4. Chamar transfer() para executar o pagamento.
async function payUsdt(signer, seller, humanAmount, buyerPaysCommission = true) {
if (!ethers.isAddress(seller)) {
throw new Error('Invalid merchant wallet');
}

const chainId = Number((await signer.provider.getNetwork()).chainId);
const tokenConfig = USDT_BY_CHAIN[chainId];

if (!tokenConfig) {
throw new Error('Unsupported network');
}

const processor = new ethers.Contract(
PROCESSORS.usdt,
PROCESSOR_ABI,
signer
);

const token = new ethers.Contract(
tokenConfig.address,
ERC20_ABI,
signer
);

const amount = ethers.parseUnits(humanAmount, tokenConfig.decimals);

const [, totalPay] = await processor.previewPayment(
amount,
buyerPaysCommission
);

const buyer = await signer.getAddress();
const balance = await token.balanceOf(buyer);

if (balance < totalPay) {
throw new Error('Insufficient USDT');
}

const currentAllowance = await token.allowance(
buyer,
PROCESSORS.usdt
);

if (currentAllowance < totalPay) {
// Ethereum USDT may require resetting an existing allowance.
if (chainId === 1 && currentAllowance > 0n) {
const resetTx = await token.approve(PROCESSORS.usdt, 0n);
await resetTx.wait();
}

const approvalTx = await token.approve(
PROCESSORS.usdt,
totalPay
);

await approvalTx.wait();
}

const tx = await processor.transfer(
seller,
amount,
buyerPaysCommission,
ethers.ZeroAddress
);

return tx.hash;
}

Requisitos de aprovação de USDT​

Dependendo da permissão existente, um pagamento em USDT pode exigir:

Permissão existenteTransações necessárias
SuficienteSomente o pagamento
Zero ou insuficienteAprovação + pagamento
Permissão diferente de zero no USDT do EthereumZerar a permissão + aprovação + pagamento

O USDT do Ethereum pode rejeitar a alteração direta de uma permissão diferente de zero para outro valor diferente de zero. O exemplo trata isso zerando a permissão primeiro, quando necessário.

Cada aprovação deve ser confirmada antes de passar para a próxima transação.

Importante:

  • Uma transação approve() não é um pagamento.
  • O cliente precisa de ETH ou BNB suficiente para cobrir as taxas de rede, mesmo ao pagar em USDT.
  • Use valores inteiros de tokens calculados com ethers.parseUnits(). Não use aritmética de ponto flutuante para valores financeiros.
  • Acompanhe as transações de aprovação separadamente da transação de pagamento final.

Etapa 5. Salve a transação de pagamento​

Seu backend deve criar um pedido antes de o cliente enviar um pagamento.

Cada pedido deve conter os valores esperados de:

  • ID do pedido
  • Rede blockchain e chain ID
  • Ativo e valor
  • Carteira de recebimento do lojista (seller)
  • Contrato processador de pagamentos aprovado
  • Carteira do comprador, se a sua página de pagamento exigir
  • Configuração da taxa de processamento (buyerPaysCommission)

Depois que o cliente enviar o pagamento:

  1. Obtenha o hash da transação de pagamento (txid) retornado pela carteira.
  2. Envie o hash para o backend.
  3. Armazene-o com o ID do pedido e o chain ID.
  4. Inicie a verificação do pagamento no servidor.

Cada transação de pagamento deve estar associada a no máximo um pedido. Garanta isso com uma restrição única (chainId, txid) no banco de dados.

A função transfer() do contrato inteligente não aceita um ID de pedido. A relação entre o pedido e a transação de pagamento dele deve ser mantida pelo seu aplicativo.

Se um cliente tentar pagar de novo, mantenha as tentativas de pagamento separadas em vez de sobrescrever registros de transações que ainda podem estar pendentes.

Etapa 6. Verifique o pagamento​

A verificação do pagamento é obrigatória antes de concluir um pedido.

Um hash de transação retornado indica apenas que uma transação foi enviada. Ele não prova que o lojista recebeu o pagamento correto.

Seu backend deve, de forma independente:

  1. Obter a transação e o recibo de execução da blockchain.
  2. Confirmar a execução bem-sucedida e a finalidade exigida na blockchain.
  3. Verificar se a transação usou a rede e o processador de pagamentos esperados.
  4. Comparar o ativo, a carteira do lojista, o valor e a chamada ao contrato com o pedido.
  5. Confirmar que o lojista recebeu os fundos esperados.
  6. Garantir que a transação ainda não foi usada para outro pedido.
  7. Marcar o pedido como pago e entregá-lo uma única vez.

Um recibo de transação bem-sucedido não basta. Uma transação pode ser executada com sucesso sem pagar o lojista certo ou o valor certo.

Siga Verificação de pagamentos e acompanhamento de pedidos para a implementação no backend, o ciclo de vida do pedido, os requisitos de finalidade e o checklist de produção.

Opcional: suporte a carteiras móveis​

Para oferecer suporte a carteiras móveis via WalletConnect, integre @walletconnect/ethereum-provider e configure-o com um Project ID do Reown Cloud.

O exemplo HTML de EVM mostra a integração do WalletConnect, incluindo a seleção e a troca de rede.

Um Project ID do WalletConnect identifica o seu aplicativo. Ele não é uma chave privada. Restrinja os domínios ou origens permitidos do aplicativo quando houver essa opção.

Exemplo de integração​

O exemplo example-evm.html mostra:

  • A conexão da MetaMask e de carteiras compatíveis com WalletConnect.
  • A seleção de Ethereum ou BNB Smart Chain.
  • O cálculo dos valores de pagamento com previewPayment().
  • O envio de pagamentos em ETH, BNB e USDT.
  • A exibição dos hashes das transações e o acompanhamento das confirmações na blockchain.

Você pode abrir o exemplo no navegador ou baixar o arquivo HTML para estudar e adaptar o código:

  • Abrir o exemplo ao vivo — abre a demo interativa. Ela usa a mainnet do Ethereum e da BNB Smart Chain, então confirmar um pagamento transfere fundos reais.
  • Baixar example-evm.html — baixa o arquivo HTML para executar localmente ou adaptar ao seu aplicativo.

Extensões de carteira do navegador, como a MetaMask, podem não funcionar corretamente com páginas abertas diretamente do disco (file://). Sirva o arquivo baixado por um servidor web local. Por exemplo, execute npx serve na pasta do arquivo e abra a URL de localhost exibida.

Experimente o exemplo​

  1. Abra o exemplo ao vivo ou sirva o arquivo baixado localmente em um navegador com a MetaMask instalada.
  2. Selecione Ethereum Mainnet ou BNB Smart Chain e clique em Connect Wallet (MetaMask). Para carteiras móveis, informe o seu Project ID do WalletConnect e selecione WalletConnect (QR).
  3. Selecione o método de pagamento: ETH, BNB ou USDT.
  4. Informe a carteira de recebimento em Seller address.
  5. Informe o valor do pagamento e escolha quem paga a taxa de processamento.
  6. Clique em Preview para ver a taxa de processamento e o valor total do pagamento. Depois clique em Pay e confirme a transação na carteira. Pagamentos em USDT podem exigir antes uma aprovação separada.
  7. O pedido e o hash da transação aparecem na tabela Orders. A demo acompanha até 15 confirmações na blockchain.

Antes de pagar:

  • Use duas carteiras diferentes. O endereço do vendedor deve ser diferente da carteira pagadora. Caso contrário, o contrato rejeita o pagamento com SellerIsBuyer.
  • As transações usam fundos reais. O exemplo vem preenchido com 0.001 ETH ou BNB. Confira o valor, a taxa de processamento e a taxa de rede antes de confirmar.

Limitações da demo: este exemplo armazena o histórico de transações no localStorage do navegador e não é uma página de pagamento de produção. Acompanhar 15 confirmações não garante a finalidade do Ethereum nem substitui a verificação de pagamentos no backend. Implemente o processo completo de verificação de pagamentos antes de aceitar pedidos de clientes.