DEV Community

Cover image for telegram-wallet-go: The Lightweight Go SDK for Accepting Crypto Payments in Telegram
Igor
Igor

Posted on

telegram-wallet-go: The Lightweight Go SDK for Accepting Crypto Payments in Telegram

Telegram has evolved far beyond a simple messaging application. With its massive user base and deep integration with the TON blockchain ecosystem, it has become a powerful platform for commerce, gaming, and decentralized applications. For developers building Telegram bots and Mini Apps, the ability to monetize by accepting cryptocurrency payments directly within the chat interface is a game-changer.

The official Telegram Wallet Pay API enables merchants to accept TON, USDT, BTC, and NOT from users seamlessly, without redirecting them outside of Telegram [1]. However, integrating a raw REST API involves handling authentication, constructing request payloads, verifying cryptographic webhook signatures, and mapping HTTP status codes to meaningful errors. Doing this from scratch in Go can be tedious and error-prone.

Enter telegram-wallet-go. Created by Igor Sazonov, this open-source Go SDK wraps the Wallet Pay API in a clean, idiomatic, and lightweight interface. It provides everything you need to start accepting crypto payments in your Go applications, with zero external dependencies for the core client and built-in middleware for popular web frameworks. In this article, we will explore the features, architecture, and usage of this excellent library.

Why Choose telegram-wallet-go?

When building backend services in Go, developers value simplicity, performance, and strong typing. The telegram-wallet-go library aligns perfectly with these principles. Its key design goals include:

  • Zero Core Dependencies: The core client relies entirely on the Go standard library (net/http, crypto/hmac, encoding/json, etc.), keeping your go.mod clean and reducing the risk of supply chain vulnerabilities.
  • Idiomatic Go Design: It uses the functional options pattern for configuration, context-aware requests (context.Context), and strong typing for all API requests and responses.
  • Framework Integrations: While the core is dependency-free, it provides optional middleware for net/http, Gin, and Echo to handle webhook signature verification effortlessly.
  • Security by Default: Webhook signature verification using HMAC-SHA256 is built-in, ensuring that you only process legitimate payment events from Wallet Pay.
  • Comprehensive Error Handling: It maps HTTP status codes to specific error types (e.g., AuthError, RateLimitError), making it easy to handle different failure scenarios programmatically.

Getting Started

Installation is as simple as running a go get command. The library requires Go 1.21 or higher.

go get github.com/tigusigalpa/telegram-wallet-go
Enter fullscreen mode Exit fullscreen mode

Initializing the Client

The library uses the functional options pattern, allowing you to configure the client cleanly. You only need your Store API Key, which you can obtain from the @WalletPay_Support_Bot on Telegram.

package main

import (
    "net/http"
    "time"

    "github.com/tigusigalpa/telegram-wallet-go"
)

func main() {
    // Basic initialization
    client := walletpay.NewClient("YOUR_STORE_API_KEY")

    // Advanced initialization with custom options
    customClient := walletpay.NewClient(
        "YOUR_STORE_API_KEY",
        walletpay.WithBaseURL("https://pay.wallet.tg"),
        walletpay.WithTimeout(60 * time.Second),
        walletpay.WithHTTPClient(&http.Client{
            Timeout: 90 * time.Second,
            Transport: &http.Transport{
                MaxIdleConns:        100,
                MaxIdleConnsPerHost: 10,
            },
        }),
    )
}
Enter fullscreen mode Exit fullscreen mode

Creating a Payment Order

Creating an order is straightforward thanks to the strongly typed CreateOrderRequest struct. This ensures you provide all required fields correctly.

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/tigusigalpa/telegram-wallet-go"
)

func main() {
    client := walletpay.NewClient("YOUR_STORE_API_KEY")

    order, err := client.CreateOrder(context.Background(), walletpay.CreateOrderRequest{
        Amount: walletpay.MoneyAmount{
            CurrencyCode: "USD",
            Amount:       "9.99",
        },
        Description:            "Premium subscription for 1 month",
        ExternalID:             "ORDER-12345",  // Your unique order ID for idempotency
        TimeoutSeconds:         3600,           // 1 hour to pay
        CustomerTelegramUserID: 123456789,      // Restrict payment to this user
        AutoConversionCurrency: "USDT",         // Automatically convert received funds to USDT
        ReturnURL:              "https://t.me/YourBot/YourApp",
        CustomData:             `{"user_id":42}`, // Metadata returned in webhooks
    })

    if err != nil {
        log.Fatal(err)
    }

    // The DirectPayLink is what you send to the user
    fmt.Println("Payment URL:", order.DirectPayLink)
}
Enter fullscreen mode Exit fullscreen mode

The DirectPayLink can be used as an Inline Button URL in a bot message or opened via Telegram.WebApp.openTelegramLink(url) in a Mini App [1].

Handling Webhooks Securely

When a payment succeeds or fails, Wallet Pay sends a webhook to your server. Verifying the HMAC-SHA256 signature of these webhooks is critical to prevent spoofing. telegram-wallet-go makes this incredibly easy by providing ready-to-use middleware.

Using Standard net/http

package main

import (
    "log"
    "net/http"

    "github.com/tigusigalpa/telegram-wallet-go"
    "github.com/tigusigalpa/telegram-wallet-go/middleware"
)

func main() {
    client := walletpay.NewClient("YOUR_STORE_API_KEY")

    http.HandleFunc("/webhook/walletpay", middleware.WalletPayWebhookHandler(
        client,
        func(w http.ResponseWriter, r *http.Request, events []walletpay.WebhookEvent) {
            for _, event := range events {
                if event.Type == walletpay.WebhookEventOrderPaid {
                    log.Printf("Order %d paid: %s", event.Payload.ID, event.Payload.ExternalID)
                    // Fulfill the order (e.g., grant premium access)
                }
            }
            // Always acknowledge receipt
            w.WriteHeader(http.StatusOK)
            w.Write([]byte("OK"))
        },
    ))

    log.Fatal(http.ListenAndServe(":8080", nil))
}
Enter fullscreen mode Exit fullscreen mode

Using Gin Framework

If you use Gin, you can leverage the specific middleware by building with the gin tag (go build -tags gin).

import (
    "github.com/gin-gonic/gin"
    "github.com/tigusigalpa/telegram-wallet-go"
    "github.com/tigusigalpa/telegram-wallet-go/middleware"
)

func main() {
    router := gin.Default()
    client := walletpay.NewClient("YOUR_API_KEY")

    router.POST("/webhook/walletpay", middleware.GinWebhookMiddleware(client), func(c *gin.Context) {
        // Retrieve parsed events from the context
        events, _ := c.Get("walletpay_events")

        for _, event := range events.([]walletpay.WebhookEvent) {
            if event.Type == walletpay.WebhookEventOrderPaid {
                // Handle successful payment
            }
        }

        c.String(200, "OK")
    })

    router.Run(":8080")
}
Enter fullscreen mode Exit fullscreen mode

Similar middleware is available for the Echo framework as well.

Best Practices for Production

When implementing Wallet Pay in production, keep these best practices in mind:

  1. Idempotency: Wallet Pay may send the same webhook multiple times due to network retries. Always use the EventID from the WebhookEvent to ensure you process each event only once.
  2. External ID: Use the ExternalID field when creating an order as your idempotency key. If you attempt to create an order with an existing ExternalID, the API will return the existing order instead of creating a duplicate.
  3. Acknowledge Quickly: Always return an HTTP 200 OK status as quickly as possible in your webhook handler. Perform heavy processing (like database updates or sending emails) asynchronously.

Conclusion

The telegram-wallet-go library is a stellar example of how an SDK should be written in Go. It is lightweight, secure, idiomatic, and provides exactly what developers need without unnecessary bloat. Whether you are building a simple storefront bot or a complex Telegram Mini App, this library will save you hours of boilerplate code and debugging.

You can check out the source code, contribute, or star the repository on GitHub: tigusigalpa/telegram-wallet-go.

Happy coding, and enjoy building the next generation of crypto-enabled Telegram applications!


References

[1] Wallet Pay API Documentation

Top comments (0)