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 yourgo.modclean 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
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,
},
}),
)
}
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)
}
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))
}
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")
}
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:
- Idempotency: Wallet Pay may send the same webhook multiple times due to network retries. Always use the
EventIDfrom theWebhookEventto ensure you process each event only once. - External ID: Use the
ExternalIDfield when creating an order as your idempotency key. If you attempt to create an order with an existingExternalID, the API will return the existing order instead of creating a duplicate. - 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!
Top comments (0)