Ricevere pagamenti crypto in Laravel — Guida
Guida passo passo: costruire un gateway di pagamento Laravel che riceve TRX e USDT (TRC20) tramite l'API di Chaingateway.
Questo tutorial vi guiderà nella costruzione di un gateway di pagamento in Laravel che supporta pagamenti Tron (TRC) e JST (TRC20). Include funzionalità come la generazione di wallet per le sessioni di pagamento, la gestione dei webhook per le notifiche di transazione e la verifica dello stato della transazione prima dell’elaborazione.
Al termine di questo tutorial, avrete un gateway di pagamento funzionante che utilizza l’API di Chaingateway per le interazioni con la blockchain.
Questo tutorial descrive solo il processo di base di funzionamento dell’implementazione. Funzionerà anche per Bitcoin, Ethereum, Binance Smart Chain e Polygon con alcuni piccoli adattamenti.
Introduzione
Prima di addentrarci nell’implementazione, cerchiamo di capire gli strumenti che useremo:
Cos’è Chaingateway?
Chaingateway è un servizio di API blockchain che semplifica l’interazione con reti blockchain come Tron. Vi permette di:
- Generare indirizzi wallet.
- Monitorare le transazioni.
- Eseguire transazioni in modo programmatico.
Visitate la documentazione ufficiale per maggiori dettagli:
- Developer Portal: scoprite come usare le funzionalità di Chaingateway.
- Documentazione API: esplorate gli endpoint dell’API nel dettaglio.
- Creazione della chiave API: generate le chiavi API necessarie per l’autenticazione.
Passaggi per creare una chiave API
Per interagire con l’API di Chaingateway, avrete bisogno di una chiave API. Seguite questi passaggi:
- Accedete a Chaingateway.
- Andate su User Settings > API Tokens.
- Cliccate su Create Token, date un nome al vostro token (ad es. “Payment Gateway”) e copiatelo. Userete questa chiave nella vostra applicazione Laravel.
Prerequisiti
Prima di procedere, assicuratevi di avere:
- Un’installazione Laravel: un progetto Laravel appena configurato. Seguite la guida all’installazione di Laravel se necessario.
- Configurazione del database: aggiornate il vostro file
.envcon le credenziali del database. - Conoscenza di base di Laravel: la familiarità con model, migration, controller e route è utile.
Funzionalità del tutorial
Questo tutorial costruisce un gateway di pagamento con le seguenti funzionalità:
- Un modulo dinamico per avviare sessioni di pagamento:
- Gli utenti possono inserire l’importo del pagamento.
- Gli utenti possono selezionare la valuta (TRX o USDT).
- Un indirizzo wallet generato è associato a ogni sessione.
- Una pagina di sessione che mostra:
- L’indirizzo del wallet.
- Lo stato della sessione.
- L’importo da inviare, l’importo ricevuto e la valuta.
- Un webhook che gestisce:
- La verifica delle transazioni in entrata.
- L’aggiornamento dello stato della sessione.
- L’inoltro dei fondi a un cold wallet in caso di pagamento riuscito (opzionale).
Passo 1: Configurare l’API Chaingateway
Per prima cosa configureremo Laravel per usare l’API Chaingateway.
Perché è importante?
Per interagire con Chaingateway, dovete autenticare ogni richiesta usando una chiave API e specificare la rete blockchain con cui state lavorando (ad es. testnet o mainnet). Questo passaggio garantisce che la vostra applicazione possa comunicare con Chaingateway senza intoppi.
Aggiornare la configurazione
Aggiungete la configurazione di Chaingateway a 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'),],Poi, aprite il vostro file .env e aggiungete quanto segue:
Chaingateway_API_URL=https://api.Chaingateway.io/api/v2Chaingateway_API_KEY=your_api_key_hereChaingateway_NETWORK=testnetCOLD_WALLET=your_cold_wallet_addressSpiegazione
api_url: l’URL base per l’API Chaingateway.api_key: la vostra chiave API personale per autenticare le richieste.network: specificate se state usando la testnet (per lo sviluppo) o la mainnet (per la produzione).cold_wallet: il wallet sicuro verso cui i fondi verranno inoltrati dopo la verifica.
Passo 2: Definire le route
Le route definiscono come gli utenti interagiscono con la vostra applicazione. Configureremo le route per:
- Mostrare la pagina di pagamento.
- Avviare una nuova sessione di pagamento.
- Visualizzare una sessione di pagamento.
- Gestire i webhook.
Aggiungere le route a 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']);Spiegazione
/payment: mostra una pagina con un pulsante per avviare una nuova sessione di pagamento./start-payment-session: crea una nuova sessione e genera un indirizzo wallet./payment-session/{id}: mostra l’indirizzo del wallet e lo stato della sessione./webhook: riceve notifiche sulle transazioni in entrata da Chaingateway.
Per disabilitare la protezione CSRF sull’endpoint dei webhook, dobbiamo escluderlo in bootstrap/app.php. Se usate versioni più vecchie di Laravel, consultate https://laravel.com/docs/11.x/csrf#csrf-excluding-uris per vedere come funziona per la vostra versione
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) { // Escludere la route webhook dalla protezione CSRF $middleware->validateCsrfTokens(except: [ 'webhook', ]); }) ->withExceptions(function (Exceptions $exceptions) { // })->create();Passo 3: Creare model e migration
Ci servono due tabelle nel database:
- Wallets: memorizza indirizzi wallet e chiavi private.
- Payment Sessions: tiene traccia dello stato di ogni sessione (ad es. Pending, Completed o Failed).
Generare model e migration
Eseguite i seguenti comandi:
php artisan make:model Wallet -mphp artisan make:model PaymentSession -mDefinire le migration
Migration Wallet
In database/migrations/<timestamp>_create_wallets_table.php:
Schema::create('wallets', function (Blueprint $table) { $table->id(); $table->string('address')->unique(); // Indirizzo del wallet $table->string('private_key'); // Chiave privata per le transazioni $table->timestamps();});Migration PaymentSession
In database/migrations/<timestamp>_create_payment_sessions_table.php:
Schema::create('payment_sessions', function (Blueprint $table) { $table->id(); $table->string('status')->default('Pending'); // Pending, Completed o Failed $table->foreignId('wallet_id')->constrained()->onDelete('cascade'); // Collegamento al Wallet $table->decimal('amount', 18, 8)->nullable(); // Importo inviato a questa sessione $table->string('currency')->default('TRX'); // Valuta dell'importo $table->decimal('received_amount', 18, 8)->nullable(); // Importo inviato a questa sessione $table->string('webhook_id')->nullable(); $table->timestamps(); });Eseguite le migration:
php artisan migrateDovremmo anche assicurarci che i campi siano fillable e che le relazioni siano costruite correttamente. Per farlo, adatteremo i model.
Model Wallet
in 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
in 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); }}Passo 4: Implementare il PaymentController
Il PaymentController gestisce tutta la logica della nostra applicazione:
- La generazione dei wallet.
- La creazione e visualizzazione delle sessioni di pagamento.
- La gestione delle notifiche webhook.
Generate il controller:
php artisan make:controller PaymentControllerAggiungere la logica del controller
Ecco l’implementazione completa di 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'); }
/** * Avvia la sessione di pagamento * * Questa funzione crea un nuovo indirizzo wallet e un webhook in 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); }
/** * Dovremmo sempre verificare la receipt della transazione per confermare che sia realmente andata a buon fine */ $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'];
/** * dovreste verificare se l'importo ricevuto corrisponde all'importo richiesto * in caso contrario, dovreste rimborsare l'utente o fare qualcos'altro. * Gli importi possono variare, ad esempio a causa della commissione di transazione. Dovreste considerarlo aggiungendo un margine. * Dovreste anche verificare se la transazione è di un token TRC20 e se il contratto corrisponde a quello atteso. * */ $amountDifference = abs($paymentSession->amount - $transactionData['amount']); $allowedDifference = $paymentSession->amount * 0.10; // 10% dell'importo della sessione di pagamento
if ($amountDifference >= $allowedDifference) { // Consentire una differenza fino al 10%, aggiornare l'indirizzo del contratto // Se l'importo è sovrappagato o sottopagato di oltre il 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'; }
/** * Fatelo se volete spostare i vostri fondi solo verso un cold wallet. * Potete anche inviare i fondi a un altro wallet o non fare nulla. * Nel caso di token TRC20, dovete assicurarvi di avere abbastanza TRX per pagare la commissione di transazione. * La funzionalità Tron Paymaster di Chaingateway è deprecated. Usate TronFuel (https://tronfuel.dev) se non volete gestire le commissioni da soli.
$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'; }
/** * Elimina il webhook in Chaingateway * Fatelo solo se non riutilizzerete l'indirizzo */ $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']); }}Cosa fa ciascun metodo?
showPaymentPage: mostra la pagina di pagamento principale con un modulo per avviare una nuova sessione.startPaymentSession: genera un indirizzo wallet, crea una nuova sessione di pagamento e reindirizza l’utente alla pagina della sessione.showPaymentSession: mostra l’indirizzo del wallet e lo stato della sessione.handleWebhook: elabora le notifiche di Chaingateway, verifica il successo della transazione, aggiorna lo stato della sessione e inoltra i fondi al cold wallet (opzionale).
Passo 5: Creare le view
Per creare una nuova sessione di pagamento, questo tutorial usa un modulo di base in cui si digitano importo e valuta. Normalmente questo dovrebbe essere gestito dal vostro processo di checkout.
Pagina di pagamento
In 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>Nella pagina della sessione di pagamento, gli utenti possono controllare lo stato del proprio pagamento. Anche questo è un esempio molto basilare. In uno scenario reale, usereste un polling più interattivo o dei websocket per aggiornare lo stato del pagamento.
Pagina della sessione di pagamento
In 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>---
## **Passo 6: Test**
### **Avviare il server**
Eseguite il server di sviluppo Laravel:
```bashphp artisan serveTestare l’applicazione
- Visitate
/paymentper avviare una nuova sessione di pagamento. - Prendete nota dell’indirizzo del wallet e inviategli fondi (se state testando sulla testnet).
- Simulate una notifica webhook inviando una richiesta POST a
/webhook. - Verificate che lo stato della sessione si aggiorni correttamente.
Speriamo che questo tutorial vi mostri quanto sia facile implementare la nostra API per ricevere pagamenti crypto. Per qualsiasi ulteriore domanda o se avete bisogno di aiuto durante l’implementazione, siamo sempre qui per aiutarvi! Potete contattare la nostra community molto disponibile o scriverci un’email. Ecco come restare in contatto: https://Chaingateway.io/support
Pronto a costruirlo da solo? Ottieni la tua API key — prova di 7 giorni, senza carta — oppure consulta API Tron per il riferimento completo degli endpoint.