Recevoir des paiements crypto dans Laravel
Tutoriel pas à pas : construire une passerelle de paiement Laravel qui reçoit du TRX et de l'USDT (TRC20) via l'API Chaingateway.
Ce tutoriel vous guide dans la construction d’une passerelle de paiement en Laravel prenant en charge les paiements Tron (TRC) et JST (TRC20). Il inclut des fonctionnalités comme la génération de wallets pour les sessions de paiement, le traitement des webhooks pour les notifications de transaction, et la vérification du statut de la transaction avant traitement.
À la fin de ce tutoriel, vous disposerez d’une passerelle de paiement fonctionnelle qui utilise l’API de Chaingateway pour les interactions blockchain.
Ce tutoriel ne décrit que le processus de base du fonctionnement de l’implémentation. Cela fonctionne aussi pour Bitcoin, Ethereum, Binance Smart Chain et Polygon, avec quelques petites adaptations.
Introduction
Avant de plonger dans l’implémentation, comprenons les outils que nous allons utiliser :
Qu’est-ce que Chaingateway ?
Chaingateway est un service d’API blockchain qui simplifie l’interaction avec des réseaux blockchain comme Tron. Il vous permet de :
- Générer des adresses de wallet.
- Surveiller les transactions.
- Exécuter des transactions de façon programmatique.
Consultez la documentation officielle pour plus de détails :
- Developer Portal : découvrez comment utiliser les fonctionnalités de Chaingateway.
- Documentation de l’API : explorez les endpoints de l’API en détail.
- Création de clé API : générez les clés API nécessaires à l’authentification.
Étapes pour créer une clé API
Pour interagir avec l’API Chaingateway, vous aurez besoin d’une clé API. Suivez ces étapes :
- Connectez-vous à Chaingateway.
- Allez dans User Settings > API Tokens.
- Cliquez sur Create Token, donnez un nom à votre token (par ex. « Payment Gateway ») et copiez-le. Vous utiliserez cette clé dans votre application Laravel.
Prérequis
Avant de continuer, assurez-vous d’avoir :
- Une installation Laravel : un projet Laravel fraîchement configuré. Suivez le guide d’installation de Laravel si besoin.
- Une configuration de base de données : mettez à jour votre fichier
.envavec vos identifiants de base de données. - Des connaissances de base de Laravel : une familiarité avec les models, migrations, contrôleurs et routes est utile.
Fonctionnalités du tutoriel
Ce tutoriel construit une passerelle de paiement avec les fonctionnalités suivantes :
- Un formulaire dynamique pour démarrer des sessions de paiement :
- Les utilisateurs peuvent saisir le montant du paiement.
- Les utilisateurs peuvent choisir la devise (TRX ou USDT).
- Une adresse de wallet générée est associée à chaque session.
- Une page de session affichant :
- L’adresse du wallet.
- Le statut de la session.
- Le montant à envoyer, le montant reçu et la devise.
- Un webhook chargé de :
- Vérifier les transactions entrantes.
- Mettre à jour le statut de la session.
- Transférer les fonds vers un cold wallet en cas de paiement réussi (optionnel).
Étape 1 : Configurer l’API Chaingateway
Nous allons d’abord configurer Laravel pour utiliser l’API Chaingateway.
Pourquoi est-ce important ?
Pour interagir avec Chaingateway, vous devez authentifier chaque requête avec une clé API et préciser le réseau blockchain utilisé (par ex. testnet ou mainnet). Cette étape garantit que votre application peut communiquer sans accroc avec Chaingateway.
Mettre à jour la configuration
Ajoutez la configuration Chaingateway dans config/app.php :
'Chaingateway' => [ 'api_url' => env('Chaingateway_API_URL', 'https://app.chaingateway.io/api/v2'), 'api_key' => env('Chaingateway_API_KEY'), 'network' => env('Chaingateway_NETWORK', 'testnet'), // Use 'mainnet' for production 'cold_wallet' => env('COLD_WALLET'),],Ensuite, ouvrez votre fichier .env et ajoutez ce qui suit :
Chaingateway_API_URL=https://api.Chaingateway.io/api/v2Chaingateway_API_KEY=your_api_key_hereChaingateway_NETWORK=testnetCOLD_WALLET=your_cold_wallet_addressExplication
api_url: l’URL de base de l’API Chaingateway.api_key: votre clé API personnelle pour authentifier les requêtes.network: indiquez si vous utilisez le testnet (pour le développement) ou le mainnet (pour la production).cold_wallet: le wallet sécurisé vers lequel les fonds seront transférés après vérification.
Étape 2 : Définir les routes
Les routes définissent comment les utilisateurs interagissent avec votre application. Nous allons mettre en place des routes pour :
- Afficher la page de paiement.
- Démarrer une nouvelle session de paiement.
- Consulter une session de paiement.
- Gérer les webhooks.
Ajouter les routes dans routes/web.php
use App\Http\Controllers\PaymentController;
Route::get('/payment', [PaymentController::class, 'showPaymentPage']);Route::post('/start-payment-session', [PaymentController::class, 'startPaymentSession']);Route::get('/payment-session/{id}', [PaymentController::class, 'showPaymentSession'])->name('showPaymentSession');Route::post('/webhook', [PaymentController::class, 'handleWebhook']);Explication
/payment: affiche une page avec un bouton pour démarrer une nouvelle session de paiement./start-payment-session: crée une nouvelle session et génère une adresse de wallet./payment-session/{id}: affiche l’adresse du wallet et le statut de la session./webhook: reçoit les notifications de transactions entrantes de Chaingateway.
Pour désactiver la protection CSRF sur l’endpoint des webhooks, nous devons l’exclure dans bootstrap/app.php. Si vous utilisez une version plus ancienne de Laravel, consultez https://laravel.com/docs/11.x/csrf#csrf-excluding-uris pour voir comment cela fonctionne pour votre version
use Illuminate\Foundation\Application;use Illuminate\Foundation\Configuration\Exceptions;use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // Exclure la route webhook de la protection CSRF $middleware->validateCsrfTokens(except: [ 'webhook', ]); }) ->withExceptions(function (Exceptions $exceptions) { // })->create();Étape 3 : Créer les models et les migrations
Nous avons besoin de deux tables en base de données :
- Wallets : stocke les adresses de wallet et les clés privées.
- Payment Sessions : suit le statut de chaque session (par ex. Pending, Completed ou Failed).
Générer les models et les migrations
Exécutez les commandes suivantes :
php artisan make:model Wallet -mphp artisan make:model PaymentSession -mDéfinir les migrations
Migration Wallet
Dans database/migrations/<timestamp>_create_wallets_table.php :
Schema::create('wallets', function (Blueprint $table) { $table->id(); $table->string('address')->unique(); // Adresse du wallet $table->string('private_key'); // Clé privée pour les transactions $table->timestamps();});Migration PaymentSession
Dans database/migrations/<timestamp>_create_payment_sessions_table.php :
Schema::create('payment_sessions', function (Blueprint $table) { $table->id(); $table->string('status')->default('Pending'); // Pending, Completed ou Failed $table->foreignId('wallet_id')->constrained()->onDelete('cascade'); // Lien vers le Wallet $table->decimal('amount', 18, 8)->nullable(); // Montant envoyé pour cette session $table->string('currency')->default('TRX'); // Devise du montant $table->decimal('received_amount', 18, 8)->nullable(); // Montant envoyé pour cette session $table->string('webhook_id')->nullable(); $table->timestamps(); });Exécutez les migrations :
php artisan migrateNous devons aussi nous assurer que les champs sont fillable et que les relations sont correctement construites. Pour cela, nous allons adapter les models.
Model Wallet
dans app\Models\Wallet.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Wallet extends Model{ protected $fillable = ['address', 'private_key'];
public function paymentSessions() { return $this->hasMany(PaymentSession::class); }}PaymentSession
dans app\Models\PaymentSession.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PaymentSession extends Model{ protected $fillable = ['wallet_id', 'status', 'amount', 'currency', 'received_amount', 'webhook_id'];
public function wallet() { return $this->belongsTo(Wallet::class); }}Étape 4 : Implémenter le PaymentController
Le PaymentController gère toute la logique de notre application :
- La génération des wallets.
- La création et l’affichage des sessions de paiement.
- Le traitement des notifications de webhook.
Générez le contrôleur :
php artisan make:controller PaymentControllerAjouter la logique du contrôleur
Voici l’implémentation complète de PaymentController :
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;use Illuminate\Support\Facades\Http;use App\Models\PaymentSession;use App\Models\Transaction;use App\Models\Wallet;
class PaymentController extends Controller{ private $apiUrl; private $apiKey; private $network; private $coldWallet;
public function __construct() { $this->apiUrl = config('app.Chaingateway.api_url'); $this->apiKey = config('app.Chaingateway.api_key'); $this->network = config('app.Chaingateway.network'); $this->coldWallet = config('app.Chaingateway.cold_wallet'); }
public function showPaymentPage() { return view('payment'); }
/** * Démarrer une session de paiement * * Cette fonction crée une nouvelle adresse de wallet et un webhook dans Chaingateway. * */
public function startPaymentSession(Request $request) { $response = Http::withHeaders([ 'Authorization' => "Bearer {$this->apiKey}", 'Content-Type' => 'application/json', 'X-Network' => $this->network, ])->post("{$this->apiUrl}/tron/addresses");
if ($response->successful()) { $walletData = $response->json()['data'];
$wallet = Wallet::create([ 'address' => $walletData['address'], 'private_key' => $walletData['privateKey'], ]);
$wbhookUrl = route('handleWebhook'); $response = Http::withHeaders([ 'Authorization' => "Bearer {$this->apiKey}", 'Content-Type' => 'application/json', 'X-Network' => $this->network, ])->post("{$this->apiUrl}/tron/webhooks", [ 'to' => $wallet->address, 'url' => $wbhookUrl, ]); $webhookData = $response->json()['data'];
$paymentSession = PaymentSession::create([ 'wallet_id' => $wallet->id, 'webhook_id' => $webhookData['id'], 'status' => 'Pending', ]);
return redirect()->route('showPaymentSession', ['id' => $paymentSession->id]); }
return back()->withErrors(['error' => 'Failed to create payment session.']); }
public function showPaymentSession($id) { $paymentSession = PaymentSession::with('wallet')->findOrFail($id); return view('payment-session', compact('paymentSession')); }
public function handleWebhook(Request $request) { $transactionData = $request->all();
$wallet = Wallet::where('address', $transactionData['to'])->first(); if (!$wallet) { return response()->json(['error' => 'Wallet not found'], 404); }
$paymentSession = PaymentSession::where('wallet_id', $wallet->id)->first(); if (!$paymentSession) { return response()->json(['error' => 'Payment session not found'], 404); }
/** * Nous devons toujours vérifier le reçu de la transaction pour confirmer qu'elle a réellement réussi */ $receiptResponse = Http::withHeaders([ 'Authorization' => "Bearer {$this->apiKey}", 'Content-Type' => 'application/json', 'X-Network' => $this->network, ])->get("{$this->apiUrl}/tron/transactions/{$transactionData['txid']}/receipt/decoded");
if ($receiptResponse->successful() && $receiptResponse->json()['data']['status'] == 'SUCCESS') { $paymentSession->status = 'Completed'; $paymentSession->received_amount = $transactionData['amount'];
/** * vous devriez vérifier que le montant reçu correspond au montant demandé * si ce n'est pas le cas, vous devriez rembourser l'utilisateur ou agir autrement. * Les montants peuvent varier, par exemple à cause des frais de transaction. Prenez cela en compte avec une marge. * Vous devriez aussi vérifier s'il s'agit d'une transaction de token TRC20 et si le contrat correspond à celui attendu. * */ $amountDifference = abs($paymentSession->amount - $transactionData['amount']); $allowedDifference = $paymentSession->amount * 0.10; // 10% du montant de la session de paiement
if ($amountDifference >= $allowedDifference) { // Autoriser un écart allant jusqu'à 10%, mettre à jour l'adresse du contrat // Si le montant est sur- ou sous-payé de plus de 10% if ($transactionData['amount'] > $paymentSession->amount) { $paymentSession->status = 'overpaid'; } else { $paymentSession->status = 'underpaid'; } }
if($paymentSession->currency == 'JST' && $transactionData['contractaddress'] != 'TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3'){ $paymentSession->status = 'Wrong currency received'; }
/** * Faites ceci si vous voulez transférer vos fonds uniquement vers un cold wallet. * Vous pouvez aussi envoyer les fonds vers un autre wallet ou ne rien faire. * Pour les tokens TRC20, vous devez vous assurer d'avoir assez de TRX pour payer les frais de transaction. * La fonctionnalité Tron Paymaster de Chaingateway est deprecated. Utilisez TronFuel (https://tronfuel.dev) si vous ne voulez pas gérer les frais vous-même.
$endpoint = $transactionData['contractaddress'] ? "{$this->apiUrl}/tron/transactions/trc20" : "{$this->apiUrl}/tron/transactions";
Http::withHeaders([ 'Authorization' => "Bearer {$this->apiKey}", 'Content-Type' => 'application/json', 'X-Network' => $this->network, ])->post($endpoint, [ 'amount' => $transactionData['amount'], 'privatekey' => $wallet->private_key, 'to' => $this->coldWallet, 'from' => $transactionData['to'], 'contractaddress' => $transactionData['contractaddress'], ]); */ } else { $paymentSession->status = 'Failed'; }
/** * Supprimer le webhook dans Chaingateway * Ne faites cela que si vous ne réutiliserez pas l'adresse */ $response = Http::withHeaders([ 'Authorization' => "Bearer {$this->apiKey}", 'Content-Type' => 'application/json', 'X-Network' => $this->network, ])->delete("{$this->apiUrl}/tron/webhooks/{$paymentSession->webhook_id}");
$paymentSession->save(); return response()->json(['status' => 'success']); }}Que fait chaque méthode ?
showPaymentPage: affiche la page de paiement principale avec un formulaire pour démarrer une nouvelle session.startPaymentSession: génère une adresse de wallet, crée une nouvelle session de paiement et redirige l’utilisateur vers la page de session.showPaymentSession: affiche l’adresse du wallet et le statut de la session.handleWebhook: traite les notifications de Chaingateway, vérifie la réussite de la transaction, met à jour le statut de la session et transfère les fonds vers le cold wallet (optionnel).
Étape 5 : Créer les vues
Pour créer une nouvelle session de paiement, ce tutoriel utilise un formulaire basique où vous saisissez le montant et la devise. Cela devrait normalement être géré par votre processus de checkout.
Page de paiement
Dans resources/views/payment.blade.php :
<!DOCTYPE html><html> <head> <title>Start Payment Session</title> </head> <body> <h1>Start a New Payment Session</h1> <form action="/start-payment-session" method="POST"> @csrf <label for="amount">Amount:</label> <input type="number" step="0.01" name="amount" id="amount" required /> <br /> <label for="currency">Currency:</label> <select name="currency" id="currency" required> <option value="TRX">TRX</option> <option value="JST">JST (TRC20)</option> </select> <br /> <button type="submit">Start Payment Session</button> </form> </body></html>Sur la page de session de paiement, les utilisateurs peuvent vérifier le statut de leur paiement. C’est également un exemple très basique. Dans un scénario réel, vous utiliseriez un polling plus interactif ou des websockets pour actualiser le statut du paiement.
Page de session de paiement
Dans resources/views/payment-session.blade.php :
<!DOCTYPE html><html> <head> <title>Payment Session</title> </head> ```html <body> <h1>Payment Session</h1> <p> Send <strong >{{ '{{' }} $paymentSession->amount }} {{ '{{' }} $paymentSession->currency }}</strong > to the address below: </p> <p><strong>{{ '{{' }} $paymentSession->wallet->address }}</strong></p> <p>Status: <strong>{{ '{{' }} $paymentSession->status }}</strong></p> <p> Received: <strong >{{ '{{' }} $paymentSession->received_amount }} {{ '{{' }} $paymentSession->currency }}</strong > </p> </body></html>---
## **Étape 6 : Tester**
### **Démarrer le serveur**
Lancez le serveur de développement Laravel :
```bashphp artisan serveTester l’application
- Visitez
/paymentpour démarrer une nouvelle session de paiement. - Notez l’adresse du wallet et envoyez-y des fonds (si vous testez sur le testnet).
- Simulez une notification de webhook en envoyant une requête POST à
/webhook. - Vérifiez que le statut de la session se met à jour correctement.
Nous espérons que ce tutoriel vous montre à quel point il est facile d’implémenter notre API pour recevoir des paiements crypto. Pour toute question supplémentaire ou de l’aide pendant l’implémentation, nous sommes toujours là pour vous ! Vous pouvez contacter notre communauté très accueillante ou nous écrire un e-mail. Voici comment rester en contact : https://Chaingateway.io/support
Prêt à le construire vous-même ? Obtenir votre clé API — essai de 7 jours, sans carte bancaire — ou consultez API Tron pour la référence complète des endpoints.