Блог
10 мин чтения
|
13 янв. 2025 г.

Приём крипто-платежей в Laravel — руководство

Пошаговое руководство: как построить платёжный шлюз на Laravel, принимающий TRX и USDT (TRC20) через API Chaingateway.

Главы
C
Chaingateway Team
Эксперты по блокчейну

Этот туториал проведёт вас через создание платёжного шлюза на Laravel, поддерживающего платежи в Tron (TRC) и JST (TRC20). Он включает такие возможности, как генерация кошельков для платёжных сессий, обработку webhook для уведомлений о транзакциях и проверку статуса транзакции перед обработкой.

К концу этого туториала у вас будет рабочий платёжный шлюз, использующий API Chaingateway для взаимодействия с блокчейном.

Этот туториал описывает лишь базовый процесс работы реализации. С небольшими доработками это будет работать и для Bitcoin, Ethereum, Binance Smart Chain и Polygon.


Введение

Прежде чем перейти к реализации, разберёмся с инструментами, которые мы будем использовать:

Что такое Chaingateway?

Chaingateway — это сервис блокчейн-API, упрощающий взаимодействие с блокчейн-сетями вроде Tron. Он позволяет вам:

  • Генерировать адреса кошельков.
  • Отслеживать транзакции.
  • Выполнять транзакции программно.

Посетите официальную документацию для получения дополнительных сведений:

  1. Developer Portal: узнайте, как использовать возможности Chaingateway.
  2. Документация API: подробно изучите эндпоинты API.
  3. Создание API-ключа: сгенерируйте API-ключи, необходимые для аутентификации.

Шаги для создания API-ключа

Чтобы взаимодействовать с API Chaingateway, вам понадобится API-ключ. Следуйте этим шагам:

  1. Войдите в Chaingateway.
  2. Перейдите в User Settings > API Tokens.
  3. Нажмите Create Token, дайте своему токену имя (например, «Payment Gateway») и скопируйте его. Этот ключ вы будете использовать в своём приложении Laravel.

Предварительные требования

Прежде чем продолжить, убедитесь, что у вас есть:

  1. Установка Laravel: свежая установка проекта Laravel. При необходимости следуйте руководству по установке Laravel.
  2. Настройка базы данных: обновите файл .env с реквизитами доступа к вашей базе данных.
  3. Базовые знания Laravel: полезно быть знакомым с моделями, миграциями, контроллерами и маршрутами.

Возможности туториала

Этот туториал строит платёжный шлюз со следующими возможностями:

  1. Динамическая форма для запуска платёжных сессий:
    • Пользователи могут вводить сумму платежа.
    • Пользователи могут выбирать валюту (TRX или USDT).
  2. С каждой сессией связан сгенерированный адрес кошелька.
  3. Страница сессии, показывающая:
    • Адрес кошелька.
    • Статус сессии.
    • Сумму к отправке, полученную сумму и валюту.
  4. Webhook, обрабатывающий:
    • Проверку входящих транзакций.
    • Обновление статуса сессии.
    • Пересылку средств на холодный кошелёк при успешной оплате (опционально).

Шаг 1: настройка API Chaingateway

Сначала мы настроим Laravel для использования API Chaingateway.

Почему это важно?

Чтобы взаимодействовать с Chaingateway, вам нужно аутентифицировать каждый запрос с помощью API-ключа и указывать блокчейн-сеть, с которой вы работаете (например, testnet или mainnet). Этот шаг гарантирует, что ваше приложение сможет беспрепятственно взаимодействовать с Chaingateway.

Обновление конфигурации

Добавьте конфигурацию Chaingateway в 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'),
],

Затем откройте файл .env и добавьте следующее:

Chaingateway_API_URL=https://api.Chaingateway.io/api/v2
Chaingateway_API_KEY=your_api_key_here
Chaingateway_NETWORK=testnet
COLD_WALLET=your_cold_wallet_address

Объяснение

  • api_url: базовый URL для API Chaingateway.
  • api_key: ваш персональный API-ключ для аутентификации запросов.
  • network: укажите, используете ли вы testnet (для разработки) или mainnet (для продакшена).
  • cold_wallet: защищённый кошелёк, на который будут пересылаться средства после верификации.

Шаг 2: определение маршрутов

Маршруты определяют, как пользователи взаимодействуют с вашим приложением. Мы настроим маршруты для:

  1. Отображения страницы оплаты.
  2. Запуска новой платёжной сессии.
  3. Просмотра платёжной сессии.
  4. Обработки webhook.

Добавление маршрутов в 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']);

Объяснение

  • /payment: отображает страницу с кнопкой для запуска новой платёжной сессии.
  • /start-payment-session: создаёт новую сессию и генерирует адрес кошелька.
  • /payment-session/{id}: отображает адрес кошелька и статус сессии.
  • /webhook: получает от Chaingateway уведомления о входящих транзакциях.

Чтобы отключить CSRF-защиту на эндпоинте webhook, нам нужно исключить его в bootstrap/app.php. Если вы используете более старые версии Laravel, перейдите по адресу https://laravel.com/docs/11.x/csrf#csrf-excluding-uris, чтобы узнать, как это работает для вашей версии

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) {
// Исключить маршрут webhook из CSRF-защиты
$middleware->validateCsrfTokens(except: [
'webhook',
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();

Шаг 3: создание моделей и миграций

Нам нужны две таблицы в базе данных:

  1. Wallets: хранит адреса кошельков и приватные ключи.
  2. Payment Sessions: отслеживает статус каждой сессии (например, Pending, Completed или Failed).

Генерация моделей и миграций

Выполните следующие команды:

Terminal window
php artisan make:model Wallet -m
php artisan make:model PaymentSession -m

Определение миграций

Миграция Wallet

В database/migrations/<timestamp>_create_wallets_table.php:

Schema::create('wallets', function (Blueprint $table) {
$table->id();
$table->string('address')->unique(); // Адрес кошелька
$table->string('private_key'); // Приватный ключ для транзакций
$table->timestamps();
});

Миграция PaymentSession

В database/migrations/<timestamp>_create_payment_sessions_table.php:

Schema::create('payment_sessions', function (Blueprint $table) {
$table->id();
$table->string('status')->default('Pending'); // Pending, Completed или Failed
$table->foreignId('wallet_id')->constrained()->onDelete('cascade'); // Связь с Wallet
$table->decimal('amount', 18, 8)->nullable(); // Сумма, отправленная для этой сессии
$table->string('currency')->default('TRX'); // Валюта суммы
$table->decimal('received_amount', 18, 8)->nullable(); // Сумма, отправленная для этой сессии
$table->string('webhook_id')->nullable();
$table->timestamps();
});

Выполните миграции:

Terminal window
php artisan migrate

Также нам следует убедиться, что поля являются fillable, а связи выстроены корректно. Для этого мы доработаем модели.

Модель Wallet

в 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

в 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);
}
}

Шаг 4: реализация PaymentController

PaymentController обрабатывает всю логику нашего приложения:

  1. Генерацию кошельков.
  2. Создание и отображение платёжных сессий.
  3. Обработку уведомлений webhook.

Сгенерируйте контроллер:

Terminal window
php artisan make:controller PaymentController

Добавление логики контроллера

Вот полная реализация 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');
}
/**
* Запуск платёжной сессии
*
* Эта функция создаст новый адрес кошелька и webhook в 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);
}
/**
* Нам всегда следует проверять квитанцию транзакции, чтобы убедиться, что она действительно прошла успешно
*/
$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'];
/**
* вам следует проверить, совпадает ли полученная сумма с запрошенной суммой
* если нет, вам следует вернуть деньги пользователю или предпринять что-то ещё.
* Суммы могут отличаться, например, из-за комиссии за транзакцию. Учтите это, добавив допустимую погрешность.
* Вам также следует проверить, является ли транзакция транзакцией токена TRC20 и совпадает ли контракт с ожидаемым.
*
*/
$amountDifference = abs($paymentSession->amount - $transactionData['amount']);
$allowedDifference = $paymentSession->amount * 0.10; // 10% от суммы платёжной сессии
if ($amountDifference >= $allowedDifference) {
// Допустить расхождение до 10%, обновить адрес контракта
// Если сумма переплачена или недоплачена более чем на 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';
}
/**
* Сделайте это, если хотите перемещать средства только на холодный кошелёк.
* Вы также можете отправлять средства на другой кошелёк или не делать ничего.
* В случае токенов TRC20 вам нужно убедиться, что у вас достаточно TRX для оплаты комиссии за транзакцию.
* Функция Chaingateway Tron Paymaster является deprecated. Используйте TronFuel (https://tronfuel.dev), если не хотите заниматься комиссиями самостоятельно.
$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';
}
/**
* Удаление webhook в Chaingateway
* Делайте это только если больше не будете использовать этот адрес
*/
$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']);
}
}

Что делает каждый метод?

  • showPaymentPage: отображает главную страницу оплаты с формой для запуска новой сессии.
  • startPaymentSession: генерирует адрес кошелька, создаёт новую платёжную сессию и перенаправляет пользователя на страницу сессии.
  • showPaymentSession: отображает адрес кошелька и статус сессии.
  • handleWebhook: обрабатывает уведомления от Chaingateway, проверяет успешность транзакции, обновляет статус сессии и пересылает средства на холодный кошелёк (опционально).

Шаг 5: создание представлений

Для создания новой платёжной сессии в этом туториале используется простая форма, где вы вводите сумму и валюту. В реальности это обычно должен делать ваш процесс оформления заказа.

Страница оплаты

В 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>

На странице платёжной сессии пользователи могут проверять статус своего платежа. Это тоже очень простой пример. В реальном сценарии вы бы использовали более интерактивный polling или websockets для обновления статуса платежа.

Страница платёжной сессии

В 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>
---
## **Шаг 6: тестирование**
### **Запуск сервера**
Запустите сервер разработки Laravel:
```bash
php artisan serve

Тестирование приложения

  1. Перейдите на /payment, чтобы запустить новую платёжную сессию.
  2. Запишите адрес кошелька и отправьте на него средства (если тестируете в testnet).
  3. Смоделируйте уведомление webhook, отправив POST-запрос на /webhook.
  4. Убедитесь, что статус сессии корректно обновляется.

Мы надеемся, что этот туториал показывает, насколько легко реализовать наш API для приёма крипто-платежей. Если у вас возникнут дополнительные вопросы или понадобится помощь во время реализации, мы всегда готовы помочь! Вы можете обратиться в наше очень отзывчивое сообщество или написать нам письмо. Узнайте здесь, как оставаться на связи: https://Chaingateway.io/support

Готовы собрать это сами? Получите свой API-ключ — 7-дневный пробный период, без карты — или посмотрите API Tron для полного справочника по эндпоинтам.

C
Chaingateway Team
Эксперты по блокчейну

Команда Chaingateway стремится упростить интеграцию блокчейна для разработчиков по всему миру.