A comprehensive C/C++ library implementation of the X402 Payment Protocol for IoT/Embedded Devices, specifically optimized for the ESP32-C6. This library enables resource-constrained embedded devices to participate in blockchain-based payment flows using the X402 standard.
- Full X402 Protocol Support: Complete implementation of the X402 payment protocol specification
- Solana Integration: Native support for Solana blockchain transactions on devnet/mainnet
- SPL Token Transfers: Built-in support for SPL token transfers with Associated Token Accounts (ATA)
- Hardware Wallet Functionality: Secure key management and transaction signing on-device
- WiFi Connectivity: Automated WiFi connection management
- Display Support: LVGL-based UI for payment status and user interaction
- Low Resource Footprint: Optimized for embedded systems with limited RAM/Flash
- Modular Architecture: Clean separation of concerns with reusable components
- Cryptographic Security: Ed25519 signing using libsodium
- SPIFFS Configuration: File-based configuration management
This project is demonstrated using PayAI Echo Merchant, a test service that implements the X402 protocol for Solana Devnet.
The Echo Merchant:
- Returns payment offers in response to HTTP 402 requests
- Accepts SPL token payments on Solana Devnet
- Echoes back premium content upon successful payment
- Provides a complete end-to-end X402 payment flow for testing
Default Configuration:
{
"payai_url": "https://x402.payai.network/api/solana-devnet/paid-content",
"solana_rpc_url": "https://api.devnet.solana.com",
"token_mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"
}You can use the PayAI Echo Merchant for testing or configure your own X402-compatible merchant endpoint.
- Architecture
- Hardware Requirements
- Software Requirements
- Installation
- Configuration
- Usage
- API Reference
- Protocol Flow
- Project Structure
- Examples
- Troubleshooting
- Contributing
- License
The library follows a modular architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββ
β Application Layer β
β (app_main.cpp) β
ββββββββββββββββββ¬βββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββββββββββ
β X402 Payment Client β
β (Orchestrates payment flow) β
βββ¬βββββββββ¬βββββββββ¬βββββββββ¬βββββββββββββ
β β β β
βΌ βΌ βΌ βΌ
ββββββ ββββββ ββββββ ββββββ
βWiFiβ βHTTPβ βSol β βDispβ
βMgr β βClntβ βana β βlay β
ββββββ ββββββ ββββββ ββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββββββββββββββ
β Crypto Utilities β
β (Ed25519, Base58/64) β
ββββββββββββββββββββββββββ
- X402PaymentClient: Main orchestrator for the payment protocol
- SolanaClient: Handles Solana RPC communication and transaction building
- HttpClient: Manages HTTP/HTTPS requests with X-PAYMENT header support
- WiFiManager: Automated WiFi connection and reconnection
- CryptoUtils: Cryptographic primitives (Ed25519, Base58, Base64)
- ConfigManager: SPIFFS-based configuration loading
- DisplayManager: LVGL-based UI for status and user feedback
- MCU: ESP32-C6
- Display: 240x280 LCD with CST816S touch controller
- Storage: Minimum 4MB Flash
- RAM: Minimum 512KB
- ESP-IDF: v5.0 or higher (Installation Guide)
- CMake: v3.16 or higher
- Python: 3.8 or higher
The following dependencies are automatically managed by ESP-IDF Component Manager:
| Component | Version | Purpose |
|---|---|---|
libsodium |
^1.0.20 | Ed25519 cryptography |
lvgl |
^9.4.0 | Display graphics |
esp_lvgl_port |
^2.6.2 | LVGL ESP32 integration |
esp_lcd_touch_cst816s |
^1.0.6 | Touch controller driver |
esp_http_client |
(built-in) | HTTP/HTTPS client |
mbedtls |
(built-in) | TLS/SSL support |
cJSON |
(built-in) | JSON parsing |
git clone https://github.com/merttozer/x402-cpp
cd x402-cpp# Source ESP-IDF environment
. $HOME/esp/esp-idf/export.sh
# Or if using ESP-IDF v5.0+
get_idfDependencies are automatically installed via ESP-IDF Component Manager during the build process.
# Set target device (ESP32-C6)
idf.py set-target esp32c6
# Open configuration menu (optional)
idf.py menuconfigidf.py buildidf.py -p /dev/ttyUSB0 flash monitorReplace /dev/ttyUSB0 with your actual serial port.
Create a config.json file in the main/spiffs/ directory:
{
"wifi_ssid": "YourWiFiSSID",
"wifi_password": "YourWiFiPassword",
"payer_public_key": [/* 32 bytes as decimal array */],
"payer_private_key": [/* 32 bytes as decimal array */],
"payai_url": "https://x402.payai.network/api/solana-devnet/paid-content",
"solana_rpc_url": "https://api.devnet.solana.com",
"user_agent": "x402-esp32c6/1.0",
"token_mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
"token_decimals": 6
}| Parameter | Type | Description |
|---|---|---|
wifi_ssid |
string | WiFi network name |
wifi_password |
string | WiFi password |
payer_public_key |
array[32] | Ed25519 public key (byte array) |
payer_private_key |
array[32] | Ed25519 private key (byte array) |
payai_url |
string | X402 payment merchant endpoint |
solana_rpc_url |
string | Solana RPC endpoint URL |
user_agent |
string | HTTP User-Agent header |
token_mint |
string | SPL token mint address (Base58) |
token_decimals |
integer | Token decimal places (usually 6 or 9) |
To generate a new Solana keypair for testing:
# Using Solana CLI
solana-keygen new --outfile keypair.json
# Convert to byte array format for config.json
# (You'll need to write a small script to convert the JSON keypair)WARNING: Never commit your private keys to version control!
- Use environment variables or secure key storage for production
- Consider implementing secure element integration for production devices
- The current implementation stores keys in SPIFFS (encrypted flash recommended)
#include "x402_client.h"
#include "config_manager.h"
extern "C" void app_main(void) {
// Initialize SPIFFS
if (!ConfigManager::init()) {
return;
}
// Load configuration
X402Config config = {};
if (!ConfigManager::load("/spiffs/config.json", config)) {
return;
}
// Create client
X402PaymentClient client(config);
// Initialize environment (WiFi, crypto, display)
if (!client.init()) {
return;
}
// Run event loop (blocking)
client.runEventLoop();
}// Initialize client
X402PaymentClient client(config);
client.init();
// Execute a single payment
bool success = client.executePaymentFlow();
if (success) {
ESP_LOGI(TAG, "Payment completed successfully!");
} else {
ESP_LOGE(TAG, "Payment failed!");
}
// Return to idle screen
client.returnToIdleAfterDelay(3000);The library includes a task-based payment system for handling button presses:
// This is handled automatically in runEventLoop()
// But you can trigger it manually:
client.onPaymentButtonPressed();X402PaymentClient(const X402Config& config)Creates a new payment client with the provided configuration.
Initializes the payment client environment:
- Initializes display
- Initializes libsodium cryptography
- Initializes NVS flash storage
- Connects to WiFi
Returns: true on success, false on failure
Executes a complete X402 payment flow:
- Fetches payment offer (HTTP 402 response)
- Parses payment requirements
- Fetches Solana blockhash
- Builds SPL token transfer transaction
- Signs transaction with Ed25519
- Submits payment with X-PAYMENT header
- Returns premium content on success
Returns: true if payment successful, false otherwise
Starts the main event loop (blocking). Displays idle screen and handles button press events for initiating payments.
Handles payment button press events. Creates a new FreeRTOS task to execute the payment flow asynchronously.
Returns to idle screen after the specified delay.
Parameters:
delay_ms: Delay in milliseconds before returning to idle
SolanaClient(const std::string& rpcUrl)Fetches the most recent blockhash from Solana RPC endpoint.
Returns: true on success
Builds a complete Solana transaction with:
- Compute budget instructions
- SPL token transfer instruction
- Multiple signatures support
Derives the Associated Token Account (ATA) address for a given owner and mint.
HttpClient(const HttpClientConfig& config)Performs GET request expecting HTTP 402 Payment Required response.
Returns: true if 402 response with valid payment offer received
Submits payment by sending X-PAYMENT header with Base64-encoded payment data.
Returns: true if HTTP 200 received with premium content
Static utility class for cryptographic operations.
Converts Base58-encoded string to 32-byte array.
Encodes binary data to Base64 string.
Signs message using Ed25519 algorithm.
Parameters:
signature[64]: Output signature buffermessage: Message to signmessage_len: Message lengthsecret_key[32]: Ed25519 private keypublic_key[32]: Ed25519 public key
WiFiManager(const std::string& ssid, const std::string& password)Initiates WiFi connection. Blocks until connected or timeout.
Returns: true if connected successfully
Checks current WiFi connection status.
Returns: true if currently connected
The X402 payment protocol implementation follows this sequence:
βββββββββββ βββββββββββ βββββββββββ
β Client β β Merchantβ β Solana β
ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ
β β β
β 1. GET /resource β β
βββββββββββββββββββββββββββ>β β
β β β
β 2. 402 Payment Required β β
β (Payment Offer) β β
β<βββββββββββββββββββββββββββ€ β
β β β
β 3. getLatestBlockhash β β
βββββββββββββββββββββββββββββΌββββββββββββββββββββββββββ>β
β β β
β 4. Blockhash Response β β
β<βββββββββββββββββββββββββββΌββββββββββββββββββββββββββββ€
β β β
β 5. Build Transaction β β
β 6. Sign with Ed25519 β β
β β β
β 7. GET /resource β β
β X-PAYMENT: <tx> β β
βββββββββββββββββββββββββββ>β β
β β β
β β 8. Verify & Send TX β
β βββββββββββββββββββββββββββ>β
β β β
β β 9. TX Confirmation β
β β<βββββββββββββββββββββββββββ€
β β β
β 10. 200 OK β β
β (Premium Content) β β
β<βββββββββββββββββββββββββββ€ β
β β β
- Initial Request: Client requests protected resource
- Payment Offer: Merchant returns 402 with payment requirements in JSON
- Blockhash Fetch: Client fetches recent blockhash from Solana
- Transaction Build: Client constructs SPL token transfer transaction
- Signing: Client signs transaction with Ed25519 private key
- Payment Submission: Client sends signed transaction in X-PAYMENT header
- Verification: Merchant verifies and broadcasts transaction to Solana
- Confirmation: Solana confirms transaction on-chain
- Content Delivery: Merchant returns premium content (HTTP 200)
esp32-x402-client/
βββ components/
β βββ x402_protocol/
β βββ include/
β β βββ config_manager.h
β β βββ crypto_utils.h
β β βββ display_manager.h
β β βββ http_client.h
β β βββ solana_client.h
β β βββ wifi_manager.h
β β βββ x402_client.h
β βββ src/
β β βββ config_manager.cpp
β β βββ crypto_utils.cpp
β β βββ display_manager.cpp
β β βββ http_client.cpp
β β βββ solana_client.cpp
β β βββ wifi_manager.cpp
β β βββ x402_client.cpp
β βββ CMakeLists.txt
βββ main/
β βββ spiffs/
β β βββ config.json # Configuration file
β βββ app_main.cpp
β βββ idf_component.yml
β βββ CMakeLists.txt
βββ CMakeLists.txt
βββ partitions.csv
βββ sdkconfig
βββ README.md
| Component | Responsibility |
|---|---|
| config_manager | SPIFFS initialization and JSON config loading |
| crypto_utils | Cryptographic primitives (Ed25519, Base58, Base64) |
| display_manager | LVGL-based UI rendering and touch handling |
| http_client | HTTP/HTTPS requests with X402 support |
| solana_client | Solana RPC, transaction building, ATA derivation |
| wifi_manager | WiFi connection and event handling |
| x402_client | Main payment protocol orchestration |
// The default configuration uses PayAI Echo Merchant
config.payai_url = "https://x402.payai.network/api/solana-devnet/paid-content";
X402PaymentClient client(config);
client.init();
client.executePaymentFlow();// Use your own X402-compatible merchant
config.payai_url = "https://your-merchant.com/api/paid-endpoint";
X402PaymentClient client(config);
client.init();
client.executePaymentFlow();{
"solana_rpc_url": "https://api.mainnet-beta.solana.com",
"token_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"token_decimals": 6
}for (int i = 0; i < 5; i++) {
ESP_LOGI(TAG, "Payment attempt %d", i + 1);
if (client.executePaymentFlow()) {
ESP_LOGI(TAG, "Payment %d successful", i + 1);
}
vTaskDelay(pdMS_TO_TICKS(2000)); // Wait 2 seconds between payments
}Symptoms: β WiFi connection timeout
Solutions:
- Verify SSID and password in config.json
- Check WiFi signal strength
- Ensure 2.4GHz WiFi (ESP32-C6 doesn't support 5GHz)
- Try increasing timeout in
x402_client.cpp
const int max_retries = 20; // Increase from 10Symptoms: β libsodium initialization failed
Solutions:
- Ensure libsodium component is properly installed
- Check ESP-IDF version (v5.0+ required)
- Verify dependency in
idf_component.yml
Symptoms: β Payment submission failed
Solutions:
- Verify merchant URL is accessible
- Check RPC endpoint connectivity
- Ensure sufficient token balance in payer account
- Verify private key matches public key
- Check merchant logs for rejection reason
Symptoms: β Failed to build transaction
Solutions:
- Verify token mint address is correct
- Ensure Associated Token Accounts exist for both sender/receiver
- Check token decimals match the mint's decimals
- Verify blockhash is recent (< 150 blocks old)
Symptoms: Crashes, stack overflows, allocation failures
Solutions:
- Increase task stack size in
x402_client.cpp:
xTaskCreate(paymentTaskWrapper, "payment_task", 16384, ...); // Increase from 8192- Enable PSRAM if available
- Reduce HTTP buffer sizes if needed
Enable verbose logging for specific components:
esp_log_level_set("x402", ESP_LOG_DEBUG);
esp_log_level_set("SolanaClient", ESP_LOG_DEBUG);
esp_log_level_set("HttpClient", ESP_LOG_DEBUG);idf.py monitorUse filters to focus on specific components:
idf.py monitor | grep "x402\|SolanaClient"Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Run tests (if available)
- Commit with clear messages:
git commit -m 'Add amazing feature' - Push to your fork:
git push origin feature/amazing-feature - Open a Pull Request
- Follow ESP-IDF coding standards
- Use meaningful variable and function names
- Add comments for complex logic
- Keep functions focused and modular
- Use C++11/14 features appropriately
- Code compiles without warnings
- All features tested on actual hardware
- Documentation updated (README, code comments)
- No private keys or secrets committed
- CHANGELOG updated (if applicable)
- Add support for multiple token types
- Implement transaction retry logic
- Add detailed error codes
- Improve display UI/UX
- Add OTA update support
- Support for Ethereum/EVM chains
- Hardware secure element integration
- BLE payment initiation
- QR code scanning support
- Multi-signature support
- Lightning Network integration
- Cross-chain atomic swaps
- Payment streaming
- Recurring payment subscriptions
- Mobile app companion
This project is dual-licensed:
Free to use under GNU Affero General Public License v3.0 for open source projects. If you use this software in a network service or distribute it, you must share your source code modifications under AGPL-3.0.
Use AGPL-3.0 if:
- Your project is open source
- You will share all source code
- Personal/educational/research use
- Internal company use (no external distribution)
For commercial products, SaaS, IoT devices, or proprietary use without sharing source code, you must purchase a commercial license.
Use Commercial License if:
- Building commercial IoT products
- Want to keep modifications private
- Embedding in hardware devices for sale
- Running as a paid SaaS service
- Any proprietary/closed-source use
Purchase Commercial License: Contact mertt.ozer@hotmail.com
See LICENSE file for complete terms.
- X402 Protocol Specification - Protocol design
- PayAI Network - Echo Merchant test service for X402 protocol
- Espressif Systems - ESP-IDF framework
- LVGL - Graphics library
- libsodium - Cryptography library
- Solana - Blockchain infrastructure
- GitHub Issues: Report bugs or request features
- Discussions: Ask questions
- Email: mertt.ozer@hotmail.com
- Twitter: @mertt_ozer
| Operation | Typical Time | Notes |
|---|---|---|
| WiFi Connection | 2-5s | Depends on signal strength |
| Payment Offer Fetch | 200-500ms | Network dependent |
| Blockhash Fetch | 300-800ms | RPC endpoint speed |
| Transaction Build | 50-100ms | CPU intensive |
| Transaction Sign | 10-20ms | Ed25519 is fast |
| Payment Submit | 500-2000ms | Includes TX broadcast |
| Total Flow | 2-5 seconds | End-to-end payment |
| Resource | Usage | Notes |
|---|---|---|
| Flash | ~1.5MB | Including dependencies |
| RAM | ~200KB | Peak during transaction |
| SPIFFS | 4KB | For config.json |
| Stack (payment task) | 8KB | Configurable |