Intégration Ethereum et BNB Smart Chain
Ce guide explique comment accepter des paiements crypto non dépositaires sur Ethereum et BNB Smart Chain avec les smart contracts de paiement de Volet.com et ethers v6.
Vos clients connectent leur portefeuille, approuvent les transactions et paient directement sur votre portefeuille de réception via un smart contract. Les fonds ne sont pas crédités sur un compte Volet.com.
Avant de continuer, consultez Intégration des smart contracts pour une présentation du parcours de paiement.
L’intégration en bref
- Connectez le portefeuille du client et confirmez le réseau blockchain choisi.
- Configurez les contrats de paiement et votre portefeuille de réception.
- Créez une commande dans votre backend avant de lancer le paiement.
- Appelez
previewPayment()pour calculer le montant total du paiement. - Appelez
transfer()pour lancer le paiement. Pour l’USDT, vérifiez l’autorisation de dépense (allowance) du jeton et demandez une approbation si nécessaire. - Enregistrez le hash de la transaction de paiement (
txid) dans la commande. - Vérifiez la transaction sur votre backend avant de marquer la commande comme payée.
Avant de commencer
Vous aurez besoin :
- D’un portefeuille client : MetaMask ou un autre portefeuille compatible EIP-1193 pour les paiements dans le navigateur.
- D’une bibliothèque :
ethersversion 6 pour interagir avec les portefeuilles et les smart contracts. - D’un portefeuille marchand : une adresse de portefeuille au format Ethereum que vous contrôlez pour recevoir les paiements.
- D’un backend et d’une base de données : pour créer les commandes, stocker les détails des transactions et vérifier les paiements.
- D’un fournisseur RPC : pour récupérer les transactions blockchain et vérifier leur exécution.
Pour connecter des portefeuilles mobiles, il vous faudra aussi un Project ID Reown/WalletConnect.
Réseaux et actifs pris en charge
| Réseau | Chain ID | Actifs pris en charge |
|---|---|---|
| Ethereum Mainnet | 1 | ETH, USDT (ERC-20) |
| BNB Smart Chain | 56 | BNB, USDT (BEP-20) |
Chaque actif utilise le contrat processeur de paiement correspondant. Les exemples de code ci-dessous contiennent les adresses des processeurs et des jetons issues de la configuration de l’intégration.
Les adresses des contrats, les décimales des jetons et les paramètres des fonctions figurent dans la Référence des smart contracts.
Avant d’utiliser les exemples en production, confirmez auprès de Volet.com les adresses des processeurs déployés et l’interface du contrat.
Étape 1. Connectez le portefeuille du client
Utilisez ethers v6 pour connecter le portefeuille de navigateur du client.
<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>
Le client doit se connecter au réseau choisi sur votre page de paiement :
- Ethereum Mainnet (
chainId: 1) - BNB Smart Chain (
chainId: 56)
Si le portefeuille est sur un autre réseau, demandez un changement de réseau ou invitez le client à changer de réseau manuellement.
Vérifiez toujours le chain ID actif juste avant d’envoyer un paiement, car le client peut changer de réseau dans son portefeuille.
Étape 2. Configurez les contrats de paiement
Ajoutez à votre application l’ABI du processeur de paiement et les fonctions du jeton.
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,
},
};
Le processeur de paiement expose deux fonctions principales :
previewPayment(amount, buyerPaysCommission)calcule les frais de traitement et le montant total que le client doit payer.transfer(seller, amount, buyerPaysCommission, optionalCommissionWallet)exécute le paiement et répartit les fonds selon le paramétrage des frais du contrat.
Le paramètre amount est exprimé dans les plus petites unités de l’actif. Utilisez ethers.parseEther() pour ETH et BNB, et ethers.parseUnits() avec les bonnes décimales du jeton pour l’USDT.
Le paramètre buyerPaysCommission détermine qui prend en charge les frais de traitement :
true: les frais s’ajoutent au paiement du client.false: les frais sont déduits du montant reçu par le marchand.
Utilisez ethers.ZeroAddress pour optionalCommissionWallet s’il n’y a pas de portefeuille de commission partenaire.
Obtenez toujours le montant total du paiement via previewPayment(). Ne calculez pas les frais de traitement manuellement.
Les définitions complètes des paramètres figurent dans la Référence des smart contracts.
Étape 3. Acceptez l’ETH ou le BNB
Les paiements en ETH et BNB natifs nécessitent une seule transaction dans le portefeuille.
La même fonction fonctionne sur les deux réseaux lorsque le client est connecté à la bonne blockchain.
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;
}
La value de la transaction doit être égale au totalPay renvoyé par previewPayment().
Le client doit aussi disposer d’assez d’ETH ou de BNB pour couvrir les frais de réseau de la blockchain.
La fonction renvoie le hash de la transaction de paiement (txid). Enregistrez-le dans la commande existante et vérifiez le paiement avant d’exécuter la commande.
Étape 4. Acceptez l’USDT
Les paiements en USDT passent par un contrat de jeton ERC-20 ou BEP-20 et peuvent nécessiter une transaction d’approbation supplémentaire.
Avant d’effectuer un paiement, votre application doit :
- Vérifier le solde USDT du client avec
balanceOf(). - Vérifier l’autorisation de dépense du processeur avec
allowance(). - Si l’autorisation est insuffisante, demander une approbation avec
approve(). - Appeler
transfer()pour exécuter le paiement.
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;
}
Exigences d’approbation USDT
Selon l’autorisation existante, un paiement en USDT peut nécessiter :
| Autorisation existante | Transactions nécessaires |
|---|---|
| Suffisante | Paiement uniquement |
| Nulle ou insuffisante | Approbation + paiement |
| Autorisation non nulle existante sur l’USDT d’Ethereum | Remise à zéro de l’autorisation + approbation + paiement |
L’USDT d’Ethereum peut refuser de modifier directement une autorisation non nulle en une autre valeur non nulle. L’exemple gère ce cas en remettant d’abord l’autorisation à zéro si nécessaire.
Chaque approbation doit être confirmée avant de passer à la transaction suivante.
Important :
- Une transaction
approve()n’est pas un paiement. - Le client a besoin d’assez d’ETH ou de BNB pour couvrir les frais de réseau, même lorsqu’il paie en USDT.
- Utilisez des montants de jetons entiers calculés avec
ethers.parseUnits(). N’utilisez pas l’arithmétique à virgule flottante pour les montants financiers. - Suivez les transactions d’approbation séparément de la transaction de paiement finale.
Étape 5. Enregistrez la transaction de paiement
Votre backend doit créer une commande avant que le client envoie un paiement.
Chaque commande doit contenir les valeurs attendues suivantes :
- Identifiant de commande
- Réseau blockchain et chain ID
- Actif et montant
- Portefeuille de réception du marchand (
seller) - Contrat processeur de paiement approuvé
- Portefeuille de l’acheteur, si votre page de paiement l’exige
- Paramétrage des frais de traitement (
buyerPaysCommission)
Une fois le paiement envoyé par le client :
- Récupérez le hash de la transaction de paiement (
txid) renvoyé par le portefeuille. - Envoyez le hash à votre backend.
- Enregistrez-le avec l’identifiant de commande et le chain ID.
- Lancez la vérification du paiement côté serveur.
Chaque transaction de paiement doit être associée à une seule commande au maximum. Imposez-le avec une contrainte d’unicité (chainId, txid) dans votre base de données.
La fonction transfer() du smart contract n’accepte pas d’identifiant de commande. C’est à votre application de maintenir le lien entre la commande et sa transaction de paiement.
Si un client relance un paiement, conservez des tentatives de paiement distinctes au lieu d’écraser des enregistrements de transactions qui peuvent encore être en attente.
Étape 6. Vérifiez le paiement
La vérification du paiement est obligatoire avant de finaliser une commande.
Un hash de transaction renvoyé indique seulement qu’une transaction a été envoyée. Il ne prouve pas que le marchand a reçu le bon paiement.
Votre backend doit, de manière indépendante :
- Récupérer la transaction et le reçu d’exécution depuis la blockchain.
- Confirmer la réussite de l’exécution et la finalité requise sur la blockchain.
- Vérifier que la transaction a utilisé le réseau et le processeur de paiement attendus.
- Comparer l’actif, le portefeuille du marchand, le montant et l’appel au contrat avec la commande.
- Confirmer que le marchand a reçu les fonds attendus.
- S’assurer que la transaction n’a pas déjà été utilisée pour une autre commande.
- Marquer la commande comme payée et ne l’exécuter qu’une seule fois.
Un reçu de transaction réussi ne suffit pas. Une transaction peut s’exécuter correctement sans payer le bon marchand ni le bon montant.
Suivez Vérification des paiements et suivi des commandes pour l’implémentation côté backend, le cycle de vie des commandes, les exigences de finalité et la liste de contrôle de mise en production.
Facultatif : prendre en charge les portefeuilles mobiles
Pour prendre en charge les portefeuilles mobiles via WalletConnect, intégrez @walletconnect/ethereum-provider et configurez-le avec un Project ID issu de Reown Cloud.
L’exemple HTML EVM illustre l’intégration de WalletConnect, y compris la sélection et le changement de réseau.
Un Project ID WalletConnect identifie votre application. Ce n’est pas une clé privée. Restreignez les domaines ou origines autorisés de l’application lorsque c’est possible.
Exemple d’intégration
L’exemple example-evm.html illustre :
- La connexion de MetaMask et de portefeuilles compatibles WalletConnect.
- La sélection d’Ethereum ou de BNB Smart Chain.
- Le calcul des montants de paiement avec
previewPayment(). - L’envoi de paiements en ETH, BNB et USDT.
- L’affichage des hashs de transaction et le suivi des confirmations blockchain.
Vous pouvez ouvrir l’exemple dans votre navigateur ou télécharger le fichier HTML pour étudier et adapter le code :
- Ouvrir l’exemple en ligne — ouvre la démo interactive. Elle utilise le mainnet d’Ethereum et de BNB Smart Chain : confirmer un paiement transfère donc des fonds réels.
- Télécharger example-evm.html — télécharge le fichier HTML pour l’exécuter localement ou l’adapter à votre application.
Les extensions de portefeuille de navigateur comme MetaMask peuvent mal fonctionner avec les pages ouvertes directement depuis le disque (file://). Servez plutôt le fichier téléchargé depuis un serveur web local. Par exemple, lancez npx serve dans le dossier du fichier et ouvrez l’URL localhost qu’il affiche.
Essayer l’exemple
- Ouvrez l’exemple en ligne ou servez le fichier téléchargé localement, dans un navigateur où MetaMask est installé.
- Sélectionnez Ethereum Mainnet ou BNB Smart Chain et cliquez sur Connect Wallet (MetaMask). Pour un portefeuille mobile, saisissez votre Project ID WalletConnect et sélectionnez WalletConnect (QR).
- Sélectionnez le moyen de paiement : ETH, BNB ou USDT.
- Saisissez le portefeuille de réception dans Seller address.
- Saisissez le montant du paiement et choisissez qui paie les frais de traitement.
- Cliquez sur Preview pour afficher les frais de traitement et le montant total du paiement. Cliquez ensuite sur Pay et confirmez la transaction dans votre portefeuille. Les paiements en USDT peuvent d’abord nécessiter une approbation distincte.
- La commande et le hash de transaction s’affichent dans le tableau Orders. La démo suit jusqu’à 15 confirmations blockchain.
Avant de payer :
- Utilisez deux portefeuilles différents. L’adresse du vendeur doit être différente du portefeuille payeur. Sinon, le contrat refuse le paiement avec
SellerIsBuyer.- Les transactions engagent des fonds réels. L’exemple est prérempli avec
0.001ETH ou BNB. Vérifiez le montant, les frais de traitement et les frais de réseau avant de confirmer.
Limites de la démo : cet exemple stocke l’historique des transactions dans le
localStoragedu navigateur et n’est pas une page de paiement de production. Suivre 15 confirmations n’assure pas la finalité d’Ethereum et ne remplace pas la vérification des paiements côté backend. Mettez en œuvre le processus complet de vérification des paiements avant d’accepter des commandes clients.
Voir aussi
- Intégration des smart contracts — présentation du parcours de paiement non dépositaire.
- Intégration TRON — acceptez TRX et USDT TRC-20.
- Référence des smart contracts — adresses des processeurs, décimales des jetons et paramètres des contrats.
- Dépannage — erreurs courantes de portefeuilles et de transactions.