Building Crypto Payments into Telegram: A Technical Deep Dive into telegram-wallet-php

Building Crypto Payments into Telegram: A Technical Deep Dive into telegram-wallet-php

posted 4 min read

Telegram is no longer just a messaging app. With an audience exceeding 1 billion monthly active users by early 2025 and deep integration with the TON blockchain, the platform has become serious infrastructure for commerce and decentralized applications. Developers building bots and Telegram Mini Apps (TMAs) now have a genuine opportunity to monetize their products by accepting cryptocurrency payments directly within the chat interface.

The official Wallet Pay API makes this possible, allowing merchants to accept TON, USDT, BTC, and NOT from users without redirecting them to external sites. However, integrating a raw REST API is never trivial. A developer must implement authentication, construct request payloads, verify cryptographic webhook signatures, and map HTTP status codes to meaningful errors.

This is exactly the problem that telegram-wallet-php solves. Created by Igor Sazonov, this open-source PHP SDK wraps the Wallet Pay API v1.2.0 in a clean, modern, and strictly typed interface. The library works as a standalone solution in any PHP 8.1+ project and provides first-class integration for Laravel (versions 9 through 13).

This post is a technical walkthrough of the current implementation: architectural shape, webhook security, error handling, and production gotchas.

Architectural Shape and Typing

At code level, telegram-wallet-php follows a fairly direct layered structure:

  • Contracts (interfaces) for the main client
  • Data Transfer Objects (DTOs) for request payloads and API responses
  • PHP 8.1 Enums for statuses and event types
  • Typed exceptions mapped to HTTP status codes
  • Laravel integration (Service Provider, Facade, Middleware)
  • Webhook handling classes

In practical terms, the main entry point is the WalletPayClient class, which implements WalletPayClientInterface. Its constructor accepts a PSR-18 compatible HTTP client, which is critical for unit testing — you can inject a mock client to verify your application logic without making real HTTP requests.

The library heavily uses DTOs to represent both request payloads and API responses. This is a significant advantage over an array-based approach. Using named arguments (a PHP 8.0 feature) makes the intent of each parameter crystal clear, and your IDE will warn you if you pass the wrong type.

$request = new CreateOrderRequest(
    amount: new MoneyAmount('USD', '9.99'),
    description: 'Premium subscription',
    externalId: 'ORDER-123',
    timeoutSeconds: 3600,
    customerTelegramUserId: 123456789
);

Order statuses and webhook event types are represented as PHP 8.1 Enums, eliminating the risk of typos when comparing strings. The order lifecycle follows a clear state machine: from ACTIVE (awaiting payment) to PAID (successful payment), EXPIRED (timeout reached), or CANCELLED (aborted).

Webhook Security: The Critical Path

Webhooks are the mechanism by which Wallet Pay notifies your server about payment outcomes. Implementing this correctly is the most security-sensitive part of any payment integration.

When Wallet Pay sends a webhook, it includes two headers: WalletPay-Timestamp (a nanosecond timestamp) and WalletPay-Signature (a Base64-encoded HMAC-SHA256 signature). The signature is computed based on the HTTP method, URI path, timestamp, and request body.

The WebhookVerifier class handles all this cryptography automatically. The developer does not need to implement HMAC verification manually, which is a major security benefit, as manual HMAC implementations are frequently a source of vulnerabilities.

In Laravel, the package provides a ready-to-use walletpay.webhook Middleware. Once registered, the webhook routing becomes extremely concise: the Middleware verifies the signature, and the controller simply parses the events and updates the database state.

Route::post('/webhook/walletpay', function (Request $request, WebhookVerifier $verifier) {
    $events = $verifier->parseWebhookEvents($request->getContent());
    
    foreach ($events as $event) {
        if ($event->type === WebhookEventType::ORDER_PAID) {
            // Payment successful! Update status in DB
        }
    }
    
    return response('OK', 200);
})->middleware('walletpay.webhook');

Error Handling: Typed Exceptions

One of the most developer-friendly aspects of telegram-wallet-php is its exception hierarchy. Instead of catching a generic \Exception and trying to parse the error message, you can precisely handle each failure scenario.

The exception hierarchy maps directly to the HTTP status codes defined in the Wallet Pay API documentation. For example, InvalidRequestException corresponds to status 400, RateLimitException to 429 (allowing you to implement exponential backoff and retries), and InvalidWebhookSignatureException is thrown when webhook signature verification fails.

Important Production Gotchas

Based on the documentation and source code, there are several important considerations for production deployment:

First, the customerTelegramUserId field is mandatory, and only the specified Telegram user can pay for the order. This is a security feature that prevents payment link sharing, but it means you cannot create a generic "pay now" link — you must create a unique order for each user.

Second, the externalId field serves as an idempotency key. If you create an order with the same externalId twice (for example, due to a retry on a network timeout), the API will return the existing order rather than creating a duplicate. You should generate your externalId deterministically.

Third, the Wallet Pay signature verification includes the exact URI path of the webhook endpoint. The path must match exactly what you configured in your store settings, including the presence or absence of a trailing slash. A mismatch will cause all webhook verifications to fail.

The Bottom Line

The telegram-wallet-php library is a well-designed, production-ready SDK that significantly lowers the barrier to integrating cryptocurrency payments into PHP applications and Telegram bots. Leveraging modern PHP 8.1 features (Enums, readonly properties, named arguments, and typed DTOs) makes the code safer, more readable, and easier to maintain.

The first-class Laravel integration is a genuine competitive advantage. The combination of a Service Provider, Facade, and Middleware means that Laravel developers can add Telegram crypto payments to their applications with minimal boilerplate, following conventions they already know. The built-in webhook signature verification eliminates a common source of vulnerabilities in payment integrations.

For developers building products in the rapidly growing Telegram Mini App ecosystem, this library is an excellent foundation.

1 Comment

0 votes

More Posts

Seamless Dropbox Integration in PHP & Laravel: A Modern SDK Tutorial

tigusigalpa - Feb 13

⚙️ What I Learned Building a Crypto Monitor with PHP and Discord

henriquesOmbisa - Oct 21, 2025

Four Spaces Before <?php

Bruno Pinto 1 - Feb 10

Building OneRule: A Technical Deep Dive into an Offline Password Manager with Flutter, SQLCipher, and AES-GCM

Fatih İlhan - Apr 13

How I optimized a web application early in my career

bhaswanth - Feb 23
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

4 comments
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!