- Bitnovo Pay
Bitnovo Pay
Bitnovo Pay - MCP Integration
Table of Contents
- Introduction
- What is MCP?
- Quick Start (5 minutes)
- Main Features
- Prerequisites
- Installation
- Platform Configuration
- MCP Tools Reference
- Webhooks and Tunnels System
- Security
- Usage Examples
- Troubleshooting
Introduction
The Bitnovo Pay MCP server allows AI agents to interact with the Bitnovo Pay API to create and manage cryptocurrency payments autonomously. This integration facilitates crypto payment automation in applications using language models like ChatGPT, Claude, or Gemini.
{% hint style="success" %} Current version: v1.1.0 | Last update: September 30, 2025 {% endhint %}
What is MCP?
Model Context Protocol (MCP) is an open standard protocol that allows AI models to access external tools and services in a structured and secure manner.
Key MCP Concepts
| Concept | Description |
|---|---|
| MCP Server | Process that exposes capabilities (tools, resources) through the MCP protocol via stdio |
| MCP Tools | Functions that the AI model can invoke (e.g., create_payment_link) |
| MCP Resources | Data that the server can provide to the model (e.g., cryptocurrency catalog) |
| stdio Transport | Communication between MCP client and server using standard input/output |
The Bitnovo Pay MCP server implements this protocol to expose 8 MCP tools that enable complete crypto payment management from any compatible MCP client.
{% hint style="info" %} Official specification: modelcontextprotocol.io {% endhint %}
Quick Start (5 minutes)
Step 1: Get your credentials
- Create an account at Bitnovo Pay
- Obtain your Device ID from the Bitnovo dashboard
- (Optional) Generate Device Secret for webhooks
Step 2: Configure your MCP client
Add this configuration to your MCP client (example for Claude Desktop):
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": ["-y", "@bitnovopay/mcp-bitnovo-pay"],
"env": {
"BITNOVO_DEVICE_ID": "your_device_id_here",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com"
}
}
}
}
{% hint style="warning" %} Configuration file location:
- Claude Desktop (macOS):
~/Library/Application Support/Claude/claude_desktop_config.json - Claude Desktop (Windows):
%APPDATA%\Claude\claude_desktop_config.json - OpenAI ChatGPT:
~/.config/openai/mcp-config.json - Google Gemini:
~/.config/gemini/mcp-config.json{% endhint %}
Step 3: Restart your MCP client
Restart Claude Desktop, ChatGPT, or your MCP client to load the server.
Step 4: Test it!
Ask your AI assistant:
"Create a payment for 10 euros"
✅ You should receive a payment URL ready to share.
Main Features
-
8 MCP tools for complete payment management:
create_payment_onchain- Generate cryptocurrency addresses for direct paymentscreate_payment_link- Create web payment URLs with redirect managementget_payment_status- Query payment status with detailed informationlist_currencies_catalog- Get supported cryptocurrencies with filteringgenerate_payment_qr- Generate custom QR codes from existing paymentsget_webhook_events- Query webhook events received in real-timeget_webhook_url- Get the public webhook URL with configuration instructionsget_tunnel_status- Diagnose tunnel connection status
-
Automatic webhook system with 3 tunnel providers:
- 🔗 ngrok: Free persistent URL (1 static domain per account)
- 🌐 zrok: 100% free open-source with persistent URLs
- 🏢 manual: For servers with public IP (N8N, Opal, VPS)
-
Compatible with multiple LLMs:
- 🤖 OpenAI ChatGPT (GPT-5, GPT-4o, Responses API, Agents SDK)
- 🧠 Google Gemini (Gemini 2.5 Flash/Pro Sept 2025, CLI, FastMCP)
- 🔮 Claude (Claude Desktop, Claude Code)
-
High-quality QR codes (v1.1.0+):
- 📱 Default resolution of 512px (improved from 300px) for modern displays
- 🖨️ Support up to 2000px for professional printing
- ✨ Sharp edges with optimized interpolation algorithms
- 🎨 Custom Bitnovo Pay branding with smooth logo scaling
-
Security and privacy:
- Mandatory HTTPS communication
- HMAC signature validation for webhooks
- Replay attack prevention with nonce cache
- Sensitive data masked in logs
- No local storage (stateless operation)
Prerequisites
{% hint style="info" %} Before starting, make sure you have: {% endhint %}
| Requirement | Description | Needed for |
|---|---|---|
| ✅ Node.js 18+ | JavaScript runtime | Running MCP server |
| ✅ Bitnovo Pay Account | Registration at bitnovo.com/pay | Obtain credentials |
| ✅ Device ID | Device identifier from dashboard | All operations |
| ⚠️ Device Secret | HMAC secret (optional) | Webhooks with validation |
| ✅ MCP Client | Claude, ChatGPT, or Gemini | Interact with server |
| ⚠️ ngrok/zrok (optional) | Tunnel for webhooks | Local development with webhooks |
{% hint style="success" %} Quick start: You only need Node.js 18+ and Bitnovo credentials to begin. Webhooks are optional. {% endhint %}
Installation
There are two ways to install the Bitnovo Pay MCP server:
Option 1: Using npx (Recommended)
{% hint style="success" %} Recommended for most users - Simplest way and always up-to-date {% endhint %}
No prior installation required, just configure your MCP client (see "Platform Configuration" section below) using:
npx -y @bitnovopay/mcp-bitnovo-pay
✅ Advantages:
- Always get the latest published version
- No need to clone the repository
- No need to compile the code
- Automatic updates on each execution
- Perfect for end users
Option 2: Clone the repository (For development)
{% hint style="warning" %} For developers only - Requires TypeScript and Node.js knowledge {% endhint %}
If you need to modify the code or contribute to the project:
# Clone the repository
git clone https://github.com/bitnovo/mcp-bitnovo-pay.git
cd mcp-bitnovo-pay
# Install dependencies
npm install
# Build the project
npm run build
# Run in development mode
npm run dev
✅ Advantages:
- Full control of source code
- Allows modifications and local development
- Ideal for contributing to the project
Credentials Configuration
Get your credentials from the Bitnovo Pay dashboard:
- Device ID: Unique identifier for your merchant
- Device Secret: (Required for webhooks) For HMAC signature validation
- Base URL: Environment URL (development or production)
Platform Configuration
OpenAI ChatGPT
To integrate with ChatGPT (GPT-4o, GPT-5), create or edit your configuration file:
Location: ~/.config/openai/mcp-config.json
Option A: Using npx (Recommended)
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": ["@bitnovopay/mcp-bitnovo-pay"],
"env": {
"BITNOVO_DEVICE_ID": "your_device_id_here",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com",
"BITNOVO_DEVICE_SECRET": "your_device_secret_hex"
}
}
}
}
{% hint style="info" %} With webhooks enabled (local development with ngrok):
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": ["-y", "@bitnovopay/mcp-bitnovo-pay"],
"env": {
"BITNOVO_DEVICE_ID": "your_device_id_here",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com",
"BITNOVO_DEVICE_SECRET": "your_device_secret_hex",
"WEBHOOK_ENABLED": "true",
"TUNNEL_ENABLED": "true",
"TUNNEL_PROVIDER": "ngrok",
"NGROK_AUTHTOKEN": "your_ngrok_token",
"NGROK_DOMAIN": "bitnovo-dev.ngrok-free.app"
}
}
}
}
{% endhint %}
Supported since: March 2025 (GPT-5 since August 2025)
Google Gemini
To integrate with Gemini 2.5, configure the FastMCP file:
Location: ~/.config/gemini/mcp-config.json
Option A: Using npx (Recommended)
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": ["@bitnovopay/mcp-bitnovo-pay"],
"env": {
"BITNOVO_DEVICE_ID": "your_device_id_here",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com",
"BITNOVO_DEVICE_SECRET": "your_device_secret_hex"
}
}
}
}
Supported since: April 2025 (Sept 2025 Models)
Claude (Anthropic)
To integrate with Claude Desktop or Claude Code:
macOS Location: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows Location: %APPDATA%\Claude\claude_desktop_config.json
Basic configuration (without webhooks)
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": ["@bitnovopay/mcp-bitnovo-pay"],
"env": {
"BITNOVO_DEVICE_ID": "your_device_id_here",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com",
"BITNOVO_DEVICE_SECRET": "your_device_secret_hex"
}
}
}
}
Configuration with webhooks (local development with ngrok)
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": ["@bitnovopay/mcp-bitnovo-pay"],
"env": {
"BITNOVO_DEVICE_ID": "your_device_id_here",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com",
"BITNOVO_DEVICE_SECRET": "your_device_secret_hex",
"WEBHOOK_ENABLED": "true",
"TUNNEL_ENABLED": "true",
"TUNNEL_PROVIDER": "ngrok",
"NGROK_AUTHTOKEN": "your_ngrok_token",
"NGROK_DOMAIN": "bitnovo-dev.ngrok-free.app"
}
}
}
}
Configuration for N8N/Opal/Server
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": ["@bitnovopay/mcp-bitnovo-pay"],
"env": {
"BITNOVO_DEVICE_ID": "your_device_id_here",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com",
"BITNOVO_DEVICE_SECRET": "your_device_secret_hex",
"WEBHOOK_ENABLED": "true",
"TUNNEL_ENABLED": "false",
"WEBHOOK_PUBLIC_URL": "https://n8n.company.com"
}
}
}
}
Supported since: 2025
MCP Tools Reference
Payment Tools (5)
1. create_payment_onchain
Creates a payment with a specific cryptocurrency address for direct blockchain transactions.
Use when: User specifies a concrete cryptocurrency (Bitcoin, ETH, USDC, etc.)
Parameters:
{
"amount_eur": 50.0,
"input_currency": "BTC",
"fiat": "EUR",
"notes": "Coffee payment",
"include_qr": true
}
Response:
{
"identifier": "abc-123-def",
"payment_uri": "bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=0.001",
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"qr_address": "data:image/png;base64,...",
"qr_payment_uri": "data:image/png;base64,...",
"expected_output_amount": "0.001",
"currency_id": "BTC",
"fiat_amount": "50.00",
"status": "PE"
}
Usage examples:
- "Create a Bitcoin payment for 50 euros"
- "Generate ETH address for 100 euros"
- "I need a USDC QR for 25 euros"
2. create_payment_link
Creates a web payment URL where the customer can choose their preferred cryptocurrency.
Use when: Generic payment request without specific cryptocurrency mentioned (DEFAULT OPTION)
Parameters:
{
"amount_eur": 50.0,
"fiat": "EUR",
"url_ok": "https://mystore.com/success",
"url_ko": "https://mystore.com/cancelled",
"notes": "Order #1234",
"include_qr": true
}
Response:
{
"identifier": "abc-123-def",
"web_url": "https://payments.bitnovo.com/pay/abc-123-def",
"qr_web_url": "data:image/png;base64,...",
"fiat_amount": "50.00",
"fiat_currency": "EUR",
"status": "PE"
}
Usage examples:
- "Create a payment for 50 euros"
- "Generate QR to collect 100 euros"
- "Give me the payment link for 25 euros"
3. get_payment_status
Queries the current status of a payment with detailed information.
Parameters:
{
"identifier": "abc-123-def"
}
Response:
{
"identifier": "abc-123-def",
"status": "CO",
"status_description": "Completed",
"fiat_amount": "50.00",
"crypto_amount": "0.001",
"currency_id": "BTC",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:35:00Z"
}
Possible statuses:
NR(Not Ready): Pre-payment created, no crypto assignedPE(Pending): Waiting for customer paymentAC(Awaiting Completion): Crypto detected in mempoolCO(Completed): Payment confirmed on blockchainEX(Expired): Payment time limit exceededCA(Cancelled): Payment cancelledFA(Failed): Transaction failed to confirm
Usage examples:
- "What's the status of payment abc-123-def?"
- "Was the payment completed?"
- "Check payment status"
4. list_currencies_catalog
Gets the catalog of available cryptocurrencies with optional amount filtering.
Parameters:
{
"filter_by_amount": 25.0
}
Response:
{
"currencies": [
{
"symbol": "BTC",
"name": "Bitcoin",
"blockchain": "Bitcoin",
"min_amount": "0.0001",
"max_amount": "10.0",
"decimals": 8
},
{
"symbol": "ETH",
"name": "Ethereum",
"blockchain": "Ethereum",
"min_amount": "0.001",
"max_amount": "100.0",
"decimals": 18
}
]
}
Usage examples:
- "What cryptocurrencies are available?"
- "Which coins accept payments of 50 euros?"
- "Show crypto catalog"
5. generate_payment_qr
Generates custom QR codes from existing payments.
Parameters:
{
"identifier": "abc-123-def",
"qr_type": "both",
"size": 512,
"style": "branded",
"branding": true
}
qr_type options:
address: Only crypto address (customer enters amount manually)payment_uri: Address + amount included (recommended)both: Generates both types (recommended)gateway_url: QR of payment gateway URL
Response:
{
"qr_address": "data:image/png;base64,...",
"qr_payment_uri": "data:image/png;base64,...",
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"payment_uri": "bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=0.001"
}
Usage examples:
- "Generate larger QR for the payment"
- "Create QR without branding"
- "I need a 500px QR"
Webhook Tools (3)
6. get_webhook_events
Queries webhook events received in real-time from Bitnovo Pay API.
Available when: WEBHOOK_ENABLED=true
Parameters:
{
"identifier": "abc-123-def",
"limit": 50,
"validated_only": true
}
Response:
{
"events": [
{
"event_id": "abc-123:1234567890",
"identifier": "abc-123-def",
"status": "CO",
"received_at": "2025-09-30T10:30:00.000Z",
"validated": true,
"payload": {
"identifier": "abc-123-def",
"status": "CO",
"confirmed_amount": 0.0012,
"crypto_amount": 0.0012
}
}
],
"total_count": 1
}
Usage examples:
- "Has any webhook arrived for payment abc-123?"
- "Show all webhook events"
- "Was payment confirmation received?"
7. get_webhook_url
Gets the public webhook URL with configuration instructions for the Bitnovo dashboard.
Available when: WEBHOOK_ENABLED=true
Parameters:
{
"validate": true
}
Response:
{
"webhook_url": "https://bitnovo-dev.ngrok-free.app/webhook/bitnovo",
"provider": "ngrok",
"validated": true,
"instructions": "✅ ngrok tunnel active with persistent URL.\n\nConfiguration steps:\n1. Copy this URL: https://bitnovo-dev.ngrok-free.app/webhook/bitnovo\n2. Go to https://pay.bitnovo.com\n3. Navigate to: Settings → Merchant → Devices\n4. Select device\n5. Configure 'notification_url' with the URL\n\nNote: This URL is persistent and won't change between restarts."
}
Usage examples:
- "What's my webhook URL?"
- "Give me the URL to configure in Bitnovo"
- "How do I set up webhooks?"
8. get_tunnel_status
Diagnoses the tunnel connection status (ngrok, zrok, or manual).
Available when: WEBHOOK_ENABLED=true
Parameters: None
Response:
{
"enabled": true,
"provider": "ngrok",
"status": "connected",
"public_url": "https://bitnovo-dev.ngrok-free.app",
"connected_at": "2025-09-30T10:30:00.000Z",
"last_error": null,
"reconnect_attempts": 0,
"health_check_enabled": true,
"context_detected": {
"execution_context": "local",
"confidence": 0.7,
"suggested_provider": "ngrok",
"indicators": ["Local development environment detected"]
}
}
Connection statuses:
disconnected: Tunnel not startedconnecting: Tunnel initializingconnected: Tunnel active and healthyreconnecting: Connection lost, attempting to reconnecterror: Failed after maximum retries
Usage examples:
- "Is the webhook tunnel working?"
- "What's the connection status?"
- "Diagnose tunnel issues"
Webhooks and Tunnels System
Dual-Server Architecture
The MCP server can run two servers simultaneously:
┌─────────────────────────────────────────────────────────┐
│ MCP Bitnovo Pay Server │
│ │
│ ┌──────────────┐ ┌──────────────────┐ ┌────────────┐│
│ │ MCP Server │ │ Webhook Server │ │ Tunnel ││
│ │ (stdio) │ │ (HTTP :3000) │ │ Manager ││
│ └──────┬───────┘ └────────┬─────────┘ └──────┬─────┘│
│ │ │ │ │
│ │ Event Store │ Public URL │ │
│ │ (in-memory) │ (ngrok/zrok) │ │
│ └──────────┬────────┴──────────┬────────┘ │
└────────────────────┼───────────────────┼───────────────┘
│ │
┌────────┴────────┐ ┌───────┴────────┐
│ │ │ │
Claude Desktop Bitnovo API Tunnel Provider
(MCP Tools) (Webhooks) (ngrok/zrok/manual)
Tunnel Providers
Provider Comparison
| Provider | Cost | Persistent URL | Best For | Stability |
|---|---|---|---|---|
| ngrok | Free (1 static domain) | ✅ Yes | Local development | ~99% uptime |
| zrok | 100% Free | ✅ Yes (reserved shares) | Open-source preference | Medium-high |
| manual | Depends on hosting | ✅ Yes | Servers with public IP | Server-dependent |
1. ngrok (Recommended for Local Development)
Features:
- ✅ Free static domain (1 per account since 2023)
- ✅ Persistent URL (doesn't change on restarts)
- ✅ High reliability (~99% uptime)
- ✅ 24-hour timeout (auto-reconnection handles this)
Configuration:
TUNNEL_ENABLED=true
TUNNEL_PROVIDER=ngrok
NGROK_AUTHTOKEN=your_ngrok_token
NGROK_DOMAIN=bitnovo-dev.ngrok-free.app # Your free static domain
Setup:
2. zrok (Open-Source Alternative)
Features:
- ✅ 100% Free (no limits on reserved shares)
- ✅ Persistent URL with unique-name
- ✅ Open-source (built on OpenZiti)
- ✅ Medium-high stability (improving with each release)
Configuration:
TUNNEL_ENABLED=true
TUNNEL_PROVIDER=zrok
ZROK_TOKEN=your_zrok_token
ZROK_UNIQUE_NAME=bitnovo-webhooks # Your reserved share name
# Results in persistent URL:
# https://bitnovo-webhooks.share.zrok.io/webhook/bitnovo
Setup:
- Create account at myzrok.io
- Install zrok CLI:
brew install openziti/zrok/zrok - Enable account:
zrok enable YOUR_TOKEN - Reserve share:
zrok reserve public --unique-name bitnovo-webhooks 3000
3. manual (Servers with Public IP)
Best For:
- N8N instances
- Opal deployments
- VPS/cloud servers
- Docker with ingress
- Kubernetes with LoadBalancer
Environment Auto-detection:
The system automatically detects these environments:
| Environment | Detection Method |
|---|---|
| N8N | Variables N8N_HOST or N8N_PROTOCOL |
| Opal | Variables OPAL_WEBHOOK_URL or OPAL_HOST |
| Kubernetes | Variable KUBERNETES_SERVICE_HOST |
| Docker | File /.dockerenv or DOCKER_HOST |
| VPS/Server | Multiple indicators (systemd, PM2, etc.) |
Configuration:
WEBHOOK_ENABLED=true
TUNNEL_ENABLED=false # or TUNNEL_PROVIDER=manual
WEBHOOK_PUBLIC_URL=https://n8n.company.com # Server's public URL
Webhook Security
HMAC-SHA256 Validation
All webhooks are validated using:
signature = hex(hmac_sha256(device_secret, nonce + raw_body))
Validation process:
- Extract nonce and signature from headers
X-NONCEandX-SIGNATURE - Calculate expected signature using device_secret
- Compare using timing-safe comparison
- Reject if signatures don't match (401 Unauthorized)
Replay Attack Prevention
- Nonce cache: Stores used nonces for 5 minutes
- Duplicate detection: Rejects webhooks with already-used nonces
- Event deduplication: Same event received multiple times stored once
Why Public URLs Are Secure?
Question: "Won't malicious actors send fake webhooks to my public URL?"
Answer: No, because:
- HMAC validation ensures only Bitnovo (with your device_secret) can create valid signatures
- Without the device_secret, attackers can't generate valid signatures
- All requests without valid signatures are rejected (401 Unauthorized)
- Replay prevention with nonces stops reuse of captured valid requests
Security Model:
Attacker sends fake webhook → Missing/invalid signature → 401 Rejected ✅
Attacker replays captured webhook → Nonce already used → 401 Rejected ✅
Bitnovo sends webhook → Valid signature + fresh nonce → 200 Accepted ✅
Decision Tree for LLM Payment Tool Selection
CRITICAL RULE: Does user explicitly mention a cryptocurrency?
User mentions specific crypto (Bitcoin, BTC, Ethereum, ETH, USDC, etc.)?
→ Use create_payment_onchain with that specific input_currency
User does NOT mention specific crypto
→ Use create_payment_link (DEFAULT OPTION)
Payment Methods Comparison Table
| Feature | create_payment_onchain | create_payment_link |
|---|---|---|
| Returns | Crypto address + QR | Web URL |
| Customer chooses crypto? | No (fixed) | Yes (in gateway) |
| Best for | Crypto-specific payments | Generic payments (DEFAULT) |
| Sharing method | Show QR/address | Send link |
| Redirects | N/A | Yes (url_ok/url_ko) |
| Use when | User specifies crypto | NO crypto mentioned |
| Examples | "Bitcoin payment", "ETH address" | "Payment for 24 euros", "Generate QR" |
Usage Examples
Example 1: Generic payment (without specific crypto)
User request:
"I need to create a payment for 50 euros"
Tool to use: create_payment_link
Command:
{
"amount_eur": 50.0,
"notes": "Generic payment"
}
Result: Web URL where customer can choose any available cryptocurrency.
Example 2: Bitcoin specific payment
User request:
"Create a Bitcoin payment for 100 euros"
Tool to use: create_payment_onchain
Command:
{
"amount_eur": 100.0,
"input_currency": "BTC",
"notes": "Bitcoin payment"
}
Result: Specific Bitcoin address + QR with amount included.
Example 3: Verify payment with webhooks
User request:
"Was payment abc-123-def completed?"
Tool 1: get_webhook_events
Command:
{
"identifier": "abc-123-def",
"validated_only": true
}
Result: List of webhook events received showing real-time updated status.
Alternative - Tool 2: get_payment_status
Command:
{
"identifier": "abc-123-def"
}
Result: Current payment status queried from API.
Example 4: Industry use cases
🛒 E-commerce: Checkout payment
Scenario: Customer completes €150 purchase, needs cryptocurrency flexibility
Tool: create_payment_link
{
"amount_eur": 150.0,
"url_ok": "https://store.com/order/12345/confirmed",
"url_ko": "https://store.com/order/12345/cancelled",
"notes": "Order #12345 - Bluetooth Headphones"
}
Flow:
- Customer confirms order → AI generates payment link
- Customer chooses preferred cryptocurrency in gateway
- Makes payment with wallet
- Gateway redirects to
url_okafter confirmation - System processes order automatically
☕ Cafe/Restaurant: Bitcoin tips
Scenario: Customer wants to leave €5 tip in Bitcoin
Tool: create_payment_onchain
{
"amount_eur": 5.0,
"input_currency": "BTC",
"notes": "Tip - Table 7"
}
Flow:
- Waiter requests tip QR → AI generates BTC address
- Customer scans QR with Bitcoin wallet
- Payment confirms in minutes
- System notifies waiter via webhook
💼 Freelance: International invoice in stablecoins
Scenario: Freelancer charges $500 USD for project, prefers USDC
Tool: create_payment_onchain
{
"amount_eur": 500.0,
"input_currency": "USDC_ERC20",
"fiat": "USD",
"notes": "Invoice #2025-001 - Web development"
}
Flow:
- International client receives USDC address
- Transfers from exchange or wallet
- Blockchain confirmation
- Freelancer receives funds without intermediaries
🎮 Gaming: In-game purchases
Scenario: Player buys €25 skin, chooses cryptocurrency
Tool: create_payment_link
{
"amount_eur": 25.0,
"notes": "In-game purchase - Dragon Skin Legendary",
"include_qr": true
}
Flow:
- Player selects skin → AI generates QR + link
- Pays with their preferred crypto (BTC, ETH, etc.)
- System detects payment via webhook
- Automatic item unlock in account
🏨 Hotel: Reservation with deposit
Scenario: Room reservation with €200 deposit
Tool: create_payment_link
{
"amount_eur": 200.0,
"url_ok": "https://hotel.com/bookings/confirm/789",
"url_ko": "https://hotel.com/bookings/cancel/789",
"notes": "Reservation deposit - Presidential Suite Oct 15-20"
}
Flow:
- Customer books → AI generates payment link
- Customer pays deposit in preferred cryptocurrency
- Automatic booking confirmation
- Frictionless check-in
Example 5: Real-time payment monitoring
Scenario: Point of sale system monitoring payments
Complete flow:
// 1. Create payment
create_payment_link({
amount_eur: 50.0,
notes: "POS Sale #4567"
})
// → Identifier: "abc-123-def"
// 2. Monitor with webhooks (recommended)
get_webhook_events({
identifier: "abc-123-def",
validated_only: true
})
// → Receives events in real-time
// 3. Manually verify status (alternative)
get_payment_status({
identifier: "abc-123-def"
})
// → Status: "CO" (Completed)
// 4. Confirm payment completed
// → System updates inventory, prints receipt
Example 6: Configure webhooks
User request:
"How do I configure webhooks in Bitnovo?"
Tool: get_webhook_url
Command:
{
"validate": true
}
Result: Webhook URL + step-by-step instructions to configure in Bitnovo dashboard.
Technical Architecture
System Layers
┌─────────────────┐
│ MCP Tools │ ← 8 tools: 5 payment + 3 webhook
│ (src/tools/) │
├─────────────────┤
│ Services │ ← Business logic: PaymentService, CurrencyService
│ (src/services/) │
├─────────────────┤
│ API Client │ ← Bitnovo API integration with retry logic
│ (src/api/) │
├─────────────────┤
│ Webhook Server │ ← Express HTTP server + Event Store + Tunnel Manager
│ (src/webhook-*) │
├─────────────────┤
│ Utilities │ ← Logging, validation, error handling, crypto
│ (src/utils/) │
└─────────────────┘
Data Flow
Payments:
- Tool Request → Validation → Service Layer → API Client → Bitnovo API
- Response → Error Handling → Data Transformation → JSON Response
- Logging: All operations logged with sensitive data masking
Webhooks:
- Bitnovo sends webhook → Tunnel (ngrok/zrok) → Webhook Server (HTTP :3000)
- HMAC Validation → Nonce verification → Event Store storage
- MCP Query → get_webhook_events → Event Store data
Environment Variables
Required Variables
BITNOVO_DEVICE_ID=your_device_id_here # Required
BITNOVO_BASE_URL=https://pos.bitnovo.com # Required
Webhook Variables (Optional)
# Enable webhooks
WEBHOOK_ENABLED=true
WEBHOOK_PORT=3000
WEBHOOK_HOST=0.0.0.0
WEBHOOK_PATH=/webhook/bitnovo
# Security
BITNOVO_DEVICE_SECRET=your_device_secret_hex # Required for webhooks
# Event store
WEBHOOK_MAX_EVENTS=1000
WEBHOOK_EVENT_TTL_MS=3600000 # 1 hour
Tunnel Variables (Optional)
# Tunnel configuration
TUNNEL_ENABLED=true
TUNNEL_PROVIDER=ngrok # Options: ngrok, zrok, manual
# ngrok specific
NGROK_AUTHTOKEN=your_ngrok_token
NGROK_DOMAIN=bitnovo-dev.ngrok-free.app # Optional: free static domain
# zrok specific
ZROK_TOKEN=your_zrok_token
ZROK_UNIQUE_NAME=bitnovo-webhooks # Your reserved share name
# manual provider
WEBHOOK_PUBLIC_URL=https://n8n.company.com # For manual provider
# Health monitoring and reconnection
TUNNEL_HEALTH_CHECK_INTERVAL=60000 # 60 seconds (default)
TUNNEL_RECONNECT_MAX_RETRIES=10 # Maximum retry attempts
TUNNEL_RECONNECT_BACKOFF_MS=5000 # Initial backoff delay
🔒 Security
Security is a fundamental priority of the Bitnovo Pay MCP server. We implement multiple layers of protection to ensure secure transactions.
{% hint style="danger" %} IMPORTANT: Never expose your credentials in public repositories, logs, or shared configurations. {% endhint %}
🛡️ Implemented Security Principles
1. Secure Communication
- ✅ Mandatory HTTPS: All API calls use HTTPS exclusively
- ✅ No HTTP: HTTP requests are automatically rejected
- ✅ TLS 1.2+: Encrypted communication with modern protocols
2. HMAC-SHA256 Webhook Validation
Webhooks are protected with HMAC cryptographic signatures:
signature = hex(hmac_sha256(device_secret, nonce + raw_body))
Validation process:
- Bitnovo sends webhook with headers
X-NONCEandX-SIGNATURE - Server calculates expected signature using
BITNOVO_DEVICE_SECRET - Timing-safe comparison between signatures
- Immediate rejection if they don't match (401 Unauthorized)
{% hint style="info" %}
Do I need webhooks? Only if you require real-time notifications. For manual status queries, use get_payment_status.
{% endhint %}
3. Replay Attack Prevention
- ✅ Nonce cache: Stores used nonces for 5 minutes
- ✅ Duplicate detection: Rejects webhooks with already-used nonces
- ✅ Event deduplication: Same event received multiple times stored once
Example of rejected attack:
Attacker replays valid webhook → Nonce already used → 401 Rejected ✅
4. Data Privacy
- ✅ Masked logs: Device IDs, secrets, and crypto addresses partially hidden
- ✅ No exchange rates: Rates not exposed to prevent inaccuracies
- ✅ Stateless design: No local persistence, real-time API queries
- ✅ Minimal data: Only necessary data requested for operation
Example of masked log:
[INFO] Payment created for device: 12345678-****-****-****-********90ab
[INFO] Webhook validated with signature: a3f2c1...******
5. Resilience and Availability
- ✅ Timeouts: 5 seconds maximum per API operation
- ✅ Smart retries: Maximum 2 retries with exponential backoff
- ✅ Tunnel auto-reconnection: Exponential backoff up to 10 retries
- ✅ Health monitoring: Tunnel connection verification every 60 seconds
🔐 Secure Credentials Configuration
{% hint style="warning" %} Security best practices:
- ✅ Use environment variables, never hardcode secrets
- ✅ Rotate
BITNOVO_DEVICE_SECRETregularly - ✅ Use production URLs (
https://pos.bitnovo.com) in production environments - ✅ Limit access to MCP configuration file (permissions 600)
- ❌ NEVER share
BITNOVO_DEVICE_SECRETin logs, repos, or messages {% endhint %}
Recommended permissions for configuration file:
# Claude Desktop config (macOS)
chmod 600 ~/Library/Application\ Support/Claude/claude_desktop_config.json
# OpenAI ChatGPT config
chmod 600 ~/.config/openai/mcp-config.json
🌐 Public Tunnel Security
Common question: "Isn't it insecure to expose a public URL for webhooks?"
Answer: No, thanks to HMAC validation:
| Scenario | Result |
|---|---|
| Attacker sends fake webhook | ❌ Invalid signature → 401 Rejected |
| Attacker replays captured webhook | ❌ Nonce already used → 401 Rejected |
| Bitnovo sends legitimate webhook | ✅ Valid signature + fresh nonce → 200 Accepted |
Conclusion: Only Bitnovo (with your BITNOVO_DEVICE_SECRET) can generate valid webhooks.
📋 Security Checklist
Before using in production, verify:
-
BITNOVO_DEVICE_SECRETconfigured and rotated regularly - Configuration file permissions restricted (600)
- Using production
BITNOVO_BASE_URL(https://pos.bitnovo.com) - Logs don't expose sensitive data
- Tunnels with persistent URLs (ngrok domain or zrok unique-name)
- Webhooks validated with HMAC enabled
- Configuration backups stored securely
🔧 Troubleshooting
Common Problems and Solutions
{% hint style="info" %} Tip: Check MCP server logs for detailed diagnostics. Logs include masked security information. {% endhint %}
❌ Error: "MCP server not found" or "Server failed to start"
Cause: MCP server cannot initialize.
Solutions:
- Verify Node.js 18+ is installed:
node --version - Try running manually:
npx -y @bitnovopay/mcp-bitnovo-pay - Check environment variables are correctly configured
- Look for errors in MCP client logs (Claude Desktop:
~/Library/Logs/Claude/)
Example of correct configuration:
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": ["-y", "@bitnovopay/mcp-bitnovo-pay"],
"env": {
"BITNOVO_DEVICE_ID": "12345678-abcd-...",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com"
}
}
}
}
❌ Error: INVALID_DEVICE_ID
Cause: Incorrect or invalid Device ID.
Solutions:
- Copy Device ID from Bitnovo Pay dashboard
- Verify it's a valid UUID (format:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) - Ensure no spaces before/after the ID
- Confirm you're using the correct device for the environment (dev/prod)
❌ Error: CURRENCY_NOT_SUPPORTED
Cause: Requested cryptocurrency not available.
Solutions:
- Use
list_currencies_catalogto see available options - Verify exact symbol (e.g.,
BTC, notBitcoinorbtc) - Confirm currency is active in your Bitnovo account
Example:
// ❌ Incorrect
{ "input_currency": "Bitcoin" }
// ✅ Correct
{ "input_currency": "BTC" }
❌ Error: AMOUNT_TOO_LOW / AMOUNT_TOO_HIGH
Cause: Amount outside cryptocurrency limits.
Solutions:
- Check limits with
list_currencies_catalog - Adjust amount within allowed range
- For large payments, consider splitting into multiple transactions
Example of limits query:
// Request
{ "filter_by_amount": 50.0 }
// Response shows min_amount and max_amount
[
{
"symbol": "BTC",
"min_amount": 0.01,
"max_amount": null // No upper limit
}
]
❌ Error: PAYMENT_EXPIRED
Cause: Payment exceeded expiration time limit.
Solutions:
- Create new payment with
create_payment_onchainorcreate_payment_link - Warn your customer about time limit before it expires
- Use
get_payment_statusto monitorexpires_atfield
{% hint style="warning" %} Important: Onchain payments with specific cryptocurrency start their timer immediately upon creation. Communicate this to the user. {% endhint %}
❌ Error: WEBHOOK_NOT_ENABLED
Cause: Attempting to use webhook tools without enabling them.
Solutions:
- Add
"WEBHOOK_ENABLED": "true"to configuration - (Optional) Configure tunnel with
TUNNEL_ENABLEDand provider - Restart MCP server
Minimal webhook configuration:
{
"env": {
"BITNOVO_DEVICE_ID": "...",
"BITNOVO_BASE_URL": "...",
"BITNOVO_DEVICE_SECRET": "...",
"WEBHOOK_ENABLED": "true"
}
}
❌ Error: TUNNEL_CONNECTION_FAILED
Cause: Failed to connect with ngrok or zrok.
ngrok Solutions:
- Verify your authtoken:
ngrok config check - Confirm your static domain is claimed in ngrok dashboard
- Test manually:
ngrok http --domain=your-domain.ngrok-free.app 3000
zrok Solutions:
- Verify zrok is enabled:
zrok status - Confirm reserved share exists:
zrok share reserved - Test connection:
zrok share reserved your-share-name
❌ Error: INVALID_SIGNATURE (webhooks)
Cause: Webhook HMAC signature doesn't match.
Solutions:
- Verify
BITNOVO_DEVICE_SECRETis correct (64 hex characters) - Confirm it's the same secret configured in Bitnovo dashboard
- Don't modify webhook body before validation
- Ensure using correct device secret (dev/prod)
Complete Error Codes
| Code | Description | Recommended Action |
|---|---|---|
INVALID_DEVICE_ID | Invalid Device ID | Verify credentials in Bitnovo dashboard |
INVALID_DEVICE_SECRET | Incorrect Device Secret | Verify 64-character hex format |
CURRENCY_NOT_SUPPORTED | Unsupported cryptocurrency | Use list_currencies_catalog |
AMOUNT_TOO_LOW | Amount below minimum | Increase payment amount |
AMOUNT_TOO_HIGH | Amount above maximum | Reduce amount or split payment |
PAYMENT_NOT_FOUND | Payment not found | Verify identifier |
PAYMENT_EXPIRED | Payment expired | Create new payment |
WEBHOOK_NOT_ENABLED | Webhooks not enabled | Configure WEBHOOK_ENABLED=true |
TUNNEL_CONNECTION_FAILED | Tunnel connection failed | Verify ngrok/zrok credentials |
INVALID_SIGNATURE | Invalid HMAC signature | Verify BITNOVO_DEVICE_SECRET |
Error Response Format
All errors follow this standard format:
{
"error": {
"code": "CURRENCY_NOT_SUPPORTED",
"message": "The specified currency is not supported",
"details": {
"input_currency": "INVALID_COIN",
"supported_currencies": ["BTC", "ETH", "USDC", "..."]
}
}
}
Development
Available Commands
npm run build # Compile TypeScript to JavaScript
npm run dev # Development server with hot reload
npm start # Start production server
Performance
Event Store
- Storage: In-memory (fast, no persistence)
- Capacity: 1000 events (configurable)
- TTL: 1 hour (configurable)
- Cleanup: Automatic every 5 minutes
- Indexing: Fast search by payment identifier
Tunnels
- ngrok: ~99% uptime, ~10-50ms added latency
- zrok: Medium-high uptime, ~20-100ms added latency
- manual: Server-dependent, no tunnel overhead
Memory Usage
Event Store:
- Estimated memory per event: ~2KB
- 1000 events ≈ 2MB
- 10000 events ≈ 20MB
Tunnel Manager:
- ngrok: ~5-10MB overhead
- zrok: ~10-20MB overhead (includes OpenZiti)
- manual: ~0MB (no tunnel process)
Support and Resources
- GitHub Repository: github.com/bitnovo/mcp-bitnovo-pay
- Issues: github.com/bitnovo/mcp-bitnovo-pay/issues
- Bitnovo Support: bitnovo.com
- MCP Protocol: modelcontextprotocol.io
- ngrok: ngrok.com | docs
- zrok: zrok.io | docs
Known Limitations
- Single-tenant operation: One Device ID per server instance
- No local persistence: All queries are real-time to API
- No exchange rates: For privacy and accuracy, exchange rates not exposed
- Timeouts: 5 seconds maximum per API operation
- Retries: Maximum 2 retries with exponential backoff
- Event Store: In-memory (lost on restart)
- Free tunnels: Each provider's limitations apply
Changelog
v1.1.0 (2025-09-30) - Tunnel System
- ✅ Automatic tunnel management with 3 providers (ngrok, zrok, manual)
- ✅ Context auto-detection (N8N, Opal, Docker, Kubernetes, VPS, local)
- ✅ Persistent URLs with free ngrok static domains and zrok reserved shares
- ✅ Auto-reconnection with exponential backoff (up to 10 retries)
- ✅ Health monitoring every 60 seconds with automatic recovery
- ✅ New MCP tools:
get_webhook_url,get_tunnel_status - ✅ Zero configuration for common deployment scenarios
v1.0.0 (2025-09-28) - Initial Webhook Implementation
- ✅ Initial webhook implementation
- ✅ Event store (in-memory)
- ✅ HMAC signature validation
- ✅ Replay attack prevention
- ✅ Dual-server mode (stdio + HTTP)
- ✅ New MCP tool:
get_webhook_events - ✅ Health check and stats endpoints
License
This project is licensed under the MIT License - see the LICENSE file for details.
Last updated: September 30, 2025 Server version: v1.1.0
Server Config
{
"mcpServers": {
"bitnovo-pay": {
"command": "npx",
"args": [
"-y",
"@bitnovopay/mcp-bitnovo-pay"
],
"env": {
"BITNOVO_DEVICE_ID": "your_device_id_here",
"BITNOVO_BASE_URL": "https://pos.bitnovo.com"
}
}
}
}