Цей посібник показує, як створити масштабований бекенд для квитків на події на основі NFT на PHP, використовуючи Symfony Messenger для безпечної та надійної обробки затримок блокчейну.Цей посібник показує, як створити масштабований бекенд для квитків на події на основі NFT на PHP, використовуючи Symfony Messenger для безпечної та надійної обробки затримок блокчейну.

Створення децентралізованої системи продажу квитків на події Web3 з Symfony 7.4

2025/12/22 01:43

Перетин Web3 і традиційних веб-фреймворків — це те місце, де починається реальна корисність. Хоча хайп-цикли приходять і йдуть, корисність Невзаємозамінних токенів (NFT) для верифікації власності — особливо в квитках на заходи — залишається надійним випадком використання.

У цій статті ми побудуємо основу Децентралізованої системи продажу квитків на заходи за допомогою Symfony 7.4 і PHP 8.3. Ми вийдемо за межі базових туторіалів і реалізуємо архітектуру виробничого рівня, яка обробляє асинхронну природу блокчейн-транзакцій за допомогою компонента Symfony Messenger.

Архітектура

«Сеньйорський» підхід визнає, що PHP не є довготривалим процесом, як Node.js. Тому ми не прослуховуємо події блокчейну в реальному часі в контролері. Натомість ми використовуємо гібридний підхід:

  1. Пряма взаємодія (запис): ми використовуємо Symfony Messenger для передачі транзакцій «мінтингу» воркеру, запобігаючи тайм-аутам HTTP.
  2. RPC-опитування (читання): ми використовуємо заплановані команди для перевірки статусу он-чейн.
  3. Смартконтракт: ми припускаємо стандартний контракт ERC-721, розгорнутий у EVM-сумісному ланцюзі (Ethereum, Polygon, Base).

Передумови та стек

  • PHP: 8.3+
  • Symfony: 7.4 (LTS)
  • блокчейн-вузол: Infura, Alchemy або локальний вузол Hardhat.

Багато PHP Web3-бібліотек занедбані або погано типізовані. Хоча web3p/web3.php найвідоміший, строго покладатися на нього може бути ризиковано через прогалини в обслуговуванні.

Для цього посібника ми використаємо web3p/web3.php (версія ^0.3) для ABI-кодування, але скористаємося нативним HttpClient Symfony для фактичного JSON-RPC транспорту. Це дає нам повний контроль над тайм-аутами, повторними спробами та логуванням — критично для виробничих застосунків.

Налаштування проєкту

Спочатку давайте встановимо залежності. Нам потрібен Symfony runtime, HTTP-клієнт і Web3-бібліотека.

composer create-project symfony/skeleton:"7.4.*" decentralized-ticketing cd decentralized-ticketing composer require symfony/http-client symfony/messenger symfony/uid web3p/web3.php

Переконайтеся, що ваш composer.json відображає стабільність:

{ "require": { "php": ">=8.3", "symfony/http-client": "7.4.*", "symfony/messenger": "7.4.*", "symfony/uid": "7.4.*", "web3p/web3.php": "^0.3.0" } }

Блокчейн-сервіс

Нам потрібен надійний сервіс для спілкування з блокчейном. Ми створимо EthereumService, який обгортає JSON-RPC виклики.

//src/Service/Web3/EthereumService.php namespace App\Service\Web3; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Web3\Utils; class EthereumService { private const JSON_RPC_VERSION = '2.0'; public function __construct( private HttpClientInterface $client, #[Autowire(env: 'BLOCKCHAIN_RPC_URL')] private string $rpcUrl, #[Autowire(env: 'SMART_CONTRACT_ADDRESS')] private string $contractAddress, #[Autowire(env: 'WALLET_PRIVATE_KEY')] private string $privateKey ) {} /** * Reads the owner of a specific Ticket ID (ERC-721 ownerOf). */ public function getTicketOwner(int $tokenId): ?string { // Function signature for ownerOf(uint256) is 0x6352211e // We pad the tokenId to 64 chars (32 bytes) $data = '0x6352211e' . str_pad(Utils::toHex($tokenId, true), 64, '0', STR_PAD_LEFT); $response = $this->callRpc('eth_call', [ [ 'to' => $this->contractAddress, 'data' => $data ], 'latest' ]); if (empty($response['result']) || $response['result'] === '0x') { return null; } // Decode the address (last 40 chars of the 64-char result) return '0x' . substr($response['result'], -40); } /** * Sends a raw JSON-RPC request using Symfony HttpClient. * This offers better observability than standard libraries. */ private function callRpc(string $method, array $params): array { $response = $this->client->request('POST', $this->rpcUrl, [ 'json' => [ 'jsonrpc' => self::JSON_RPC_VERSION, 'method' => $method, 'params' => $params, 'id' => random_int(1, 9999) ] ]); $data = $response->toArray(); if (isset($data['error'])) { throw new \RuntimeException('RPC Error: ' . $data['error']['message']); } return $data; } }

Запустіть локальний тест доступу до getTicketOwner із відомим змінтеним ID. Якщо ви отримуєте 0x-адресу, ваше RPC-з'єднання працює.

Асинхронний мінтинг із Messenger

Блокчейн-транзакції повільні (від 15 секунд до хвилин). Ніколи не змушуйте користувача чекати на підтвердження блоку в запиті браузера. Ми будемо використовувати Symfony Messenger для обробки цього у фоновому режимі.

Повідомлення

//src/Message/MintTicketMessage.php: namespace App\Message; use Symfony\Component\Uid\Uuid; readonly class MintTicketMessage { public function __construct( public Uuid $ticketId, public string $userWalletAddress, public string $metadataUri ) {} }

Обробник

Ось тут відбувається магія. Ми використаємо допоміжний засіб бібліотеки web3p/web3.php для локального підпису транзакції.

Примітка: у високозахищеному середовищі ви б використовували службу управління ключами (KMS) або окремий підписний анклав. Для цієї статті ми підписуємо локально.

//src/MessageHandler/MintTicketHandler.php namespace App\MessageHandler; use App\Message\MintTicketMessage; use App\Service\Web3\EthereumService; use Psr\Log\LoggerInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Web3\Contract; use Web3\Providers\HttpProvider; use Web3\RequestManagers\HttpRequestManager; use Web3p\EthereumTx\Transaction; #[AsMessageHandler] class MintTicketHandler { public function __construct( private EthereumService $ethereumService, // Our custom service private LoggerInterface $logger, #[Autowire(env: 'BLOCKCHAIN_RPC_URL')] private string $rpcUrl, #[Autowire(env: 'WALLET_PRIVATE_KEY')] private string $privateKey, #[Autowire(env: 'SMART_CONTRACT_ADDRESS')] private string $contractAddress ) {} public function __invoke(MintTicketMessage $message): void { $this->logger->info("Starting mint process for Ticket {$message->ticketId}"); // 1. Prepare Transaction Data (mintTo function) // detailed implementation of raw transaction signing usually goes here. // For brevity, we simulate the logic flow: try { // Logic to get current nonce and gas price via EthereumService // $nonce = ... // $gasPrice = ... // Sign transaction offline to prevent key exposure over network // $tx = new Transaction([...]); // $signedTx = '0x' . $tx->sign($this->privateKey); // Broadcast // $txHash = $this->ethereumService->sendRawTransaction($signedTx); // In a real app, you would save $txHash to the database entity here $this->logger->info("Mint transaction broadcast successfully."); } catch (\Throwable $e) { $this->logger->error("Minting failed: " . $e->getMessage()); // Symfony Messenger will automatically retry based on config throw $e; } } }

Контролер

Контролер залишається тонким. Він приймає запит, валідує вхідні дані, створює сутність квитка «очікує» у вашій базі даних (опущено для стислості) і відправляє повідомлення.

//src/Controller/TicketController.php: namespace App\Controller; use App\Message\MintTicketMessage; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Uid\Uuid; #[Route('/api/v1/tickets')] class TicketController extends AbstractController { #[Route('/mint', methods: ['POST'])] public function mint(Request $request, MessageBusInterface $bus): JsonResponse { $payload = $request->getPayload(); $walletAddress = $payload->get('wallet_address'); // 1. Basic Validation if (!$walletAddress || !str_starts_with($walletAddress, '0x')) { return $this->json(['error' => 'Invalid wallet address'], 400); } // 2. Generate Internal ID $ticketId = Uuid::v7(); // 3. Dispatch Message (Fire and Forget) $bus->dispatch(new MintTicketMessage( $ticketId, $walletAddress, 'https://api.myapp.com/metadata/' . $ticketId->toRfc4122() )); // 4. Respond immediately return $this->json([ 'status' => 'processing', 'ticket_id' => $ticketId->toRfc4122(), 'message' => 'Minting request queued. Check status later.' ], 202); } }

Конфігурація та посібник зі стилю

Дотримуючись стилю Symfony 7.4, ми використовуємо сувору типізацію та атрибути. Переконайтеся, що ваш messenger.yaml налаштований для асинхронного транспорту.

#config/packages/messenger.yaml: framework: messenger: transports: async: dsn: '%env(MESSENGER_TRANSPORT_DSN)%' retry_strategy: max_retries: 3 delay: 1000 multiplier: 2 routing: 'App\Message\MintTicketMessage': async

Верифікація

Щоб перевірити, що ця реалізація працює без розгортання в Mainnet:

Локальний вузол: запустіть локальний блокчейн за допомогою Hardhat або Anvil (Foundry).

npx hardhat node

Середовище: встановіть свій .env.local на localhost.

BLOCKCHAIN_RPC_URL="http://127.0.0.1:8545" WALLET_PRIVATE_KEY="<один із тестових ключів, наданих hardhat>" SMART_CONTRACT_ADDRESS="<адреса розгорнутого контракту>" MESSENGER_TRANSPORT_DSN="doctrine://default"

Споживання: запустіть воркера.

php bin/console messenger:consume async -vv

Запит:

curl -X POST https://localhost:8000/api/v1/tickets/mint \ -H "Content-Type: application/json" \ -d '{"wallet_address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"}'

Ви маєте побачити, як воркер обробляє повідомлення, і, якщо ви повністю реалізували логіку підпису сирих транзакцій, хеш транзакції з'явиться у вашій консолі Hardhat.

Висновок

Створення Web3-застосунків на PHP вимагає зміни мислення. Ви не просто будуєте CRUD-застосунок; ви створюєте оркестратор для децентралізованого стану.

Використовуючи Symfony 7.4, ми скористалися:

  • HttpClient для надійної, контрольованої RPC-комунікації.
  • Messenger для обробки асинхронної реальності блокчейнів.
  • PHP 8.3 Attributes для чистого, читабельного коду.

Ця архітектура масштабується. Незалежно від того, чи продаєте ви 10 квитків або 10 000, черга повідомлень діє як буфер, гарантуючи, що ваші nonce транзакцій не конфліктують, а ваш сервер не зависає.

Готові масштабувати свою Web3-інфраструктуру?

Інтеграція блокчейну вимагає точності. Якщо вам потрібна допомога з аудитом ваших взаємодій зі смартконтрактами або масштабуванням ваших споживачів повідомлень Symfony, зв'яжемося.

\

Ринкові можливості
Логотип 4
Курс 4 (4)
$0.02185
$0.02185$0.02185
+4.54%
USD
Графік ціни 4 (4) в реальному часі
Відмова від відповідальності: статті, опубліковані на цьому сайті, взяті з відкритих джерел і надаються виключно для інформаційних цілей. Вони не обов'язково відображають погляди MEXC. Всі права залишаються за авторами оригінальних статей. Якщо ви вважаєте, що будь-який контент порушує права третіх осіб, будь ласка, зверніться за адресою [email protected] для його видалення. MEXC не дає жодних гарантій щодо точності, повноти або своєчасності вмісту і не несе відповідальності за будь-які дії, вчинені на основі наданої інформації. Вміст не є фінансовою, юридичною або іншою професійною порадою і не повинен розглядатися як рекомендація або схвалення з боку MEXC.