btpay

BTPay

Self-hosted Bitcoin payment processor.
No third parties. No monthly fees. Full control of your keys.

Live Demo · Test a Donation · Website · Quick Start · API Reference · Configuration · Deployment · License

Alpha Software – BTPay is in early alpha and under active development. Things may change. Please test thoroughly before using in production and keep regular backups.

Deploy to Render Deploy on Railway Deploy to Heroku

BTPay Dashboard — Dark Mode


A lightweight, privacy-focused alternative to BTCPay Server.

Q: Why is the name so similar to BTCPay?

A: My hope is the confusion makes Nicolas 🫶 and Kukks 🫶 learn Python and adopt this. Just kidding, what they built is amazing. They will never learn to python. So we will keep this alternative running for the very few who need it done correctly but with a much smaller subset of features. The real asnwer the is pip and domains were available. Please consider checking out their project BTCPS and donating to OpenSats

Features

Payments

Bitcoin Verification

Invoicing & Checkout

Storefronts & Donations

Integrations

Access & Security

Software Updates

Infrastructure

Quick Start

# Clone
git clone https://github.com/btpay-org/btpay.git
cd btpay

# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate

# Install (reproducible)
pip install --require-hashes -r requirements.lock
pip install -e . --no-deps

# Or: install latest (unpinned)
# pip install -e ".[dev]"

# Create admin user
flask --app app user-create \
  --email you@example.com \
  --password your-secure-password \
  --first-name Admin \
  --last-name User

# Run
python app.py

Open http://localhost:5000 in your browser.

Requirements

Configuration

BTPay uses config_default.py for defaults. Override settings by creating a config.py in the project root or via environment variables prefixed with BTPAY_. See the full Configuration Guide.

Essential Settings

Setting Env Var Default Description
SECRET_KEY BTPAY_SECRET_KEY (dev default) Flask secret key — change in production
REFNUM_KEY BTPAY_REFNUM_KEY (dev default) NaCl SecretBox key for reference numbers (32 bytes hex)
REFNUM_NONCE BTPAY_REFNUM_NONCE (dev default) NaCl SecretBox nonce for reference numbers (24 bytes hex)

Bitcoin Settings

Setting Default Description
BTC_QUOTE_DEADLINE 30 Minutes to lock the BTC exchange rate
BTC_MARKUP_PERCENT 0 Markup percentage on the exchange rate
MAX_UNDERPAID_GIFT 5 USD threshold to accept small underpayments
EXCHANGE_RATE_SOURCES ['coingecko', 'coinbase', 'kraken'] Rate source APIs
EXCHANGE_RATE_INTERVAL 300 Seconds between rate fetches
BTC_CONFIRMATION_THRESHOLDS [(100,1), (1000,3), (None,6)] Amount (USD) to required confirmations

Privacy Settings

Setting Env Var Default Description
SOCKS5_PROXY BTPAY_SOCKS5_PROXY (empty) SOCKS5 proxy for Tor (e.g. socks5h://127.0.0.1:9050)
MEMPOOL_API_URL BTPAY_MEMPOOL_URL https://mempool.space/api Can point to your own instance

Email Settings

Setting Env Var Default Description
SMTP_CONFIG.host BTPAY_SMTP_HOST (empty) SMTP server hostname
SMTP_CONFIG.port BTPAY_SMTP_PORT 587 SMTP port (587=TLS, 465=SSL)
SMTP_CONFIG.username BTPAY_SMTP_USER (empty) SMTP username
SMTP_CONFIG.password BTPAY_SMTP_PASS (empty) SMTP password
SMTP_CONFIG.from_email BTPAY_SMTP_FROM (empty) From email address

See config_default.py for the complete list of all configuration options.

CLI Commands

# User management
flask --app app user-create          # Create admin user (interactive)
flask --app app user-list            # List all users
flask --app app user-reset-totp --email user@example.com  # Reset 2FA

# Wallet management
flask --app app wallet-create --org-id 1 --name "Main Wallet" --type xpub --xpub "zpub..."
flask --app app wallet-import --wallet-id 1 --file addresses.txt

# Data management
flask --app app db-export [output_dir]   # Export data to JSON
flask --app app db-import [input_dir]    # Import data from JSON
flask --app app db-backup                # Create timestamped backup
flask --app app db-stats                 # Show storage statistics

# Exchange rates
flask --app app rates                    # Show current rates

# Software updates
flask --app app check-updates            # Check for new versions
flask --app app update --version v0.2.0  # Update via git
flask --app app update --zip release.zip # Update from ZIP (air-gapped)
flask --app app update-rollback          # Rollback to previous version

Wallet Types

BTPay supports three wallet types:

Provide your hardware wallet’s extended public key. BTPay derives fresh addresses using BIP32. Supports:

Output Descriptor

Standard output descriptors for precise script control:

Address List

Import a plain text file with one Bitcoin address per line. Useful for pre-generated addresses or cold storage setups.

Storefronts

Create public-facing product stores and donation pages — inspired by BTCPay Server’s “Apps” feature.

Store Mode

Set up a product catalog with fixed prices. Buyers browse at /s/<your-slug>, pick items, and check out through the standard invoice flow. Supports:

Donation Mode

Create a donation/tip page with preset amounts (e.g. $5, $10, $25, $50, $100) and optional custom amounts. Supports:

How It Works

  1. Go to Storefronts in the sidebar and create a new storefront
  2. Choose Store or Donation mode
  3. Add items (store) or configure presets (donation)
  4. Share the public URL: https://your-instance.com/s/<slug>

Purchases and donations create invoices automatically using your configured payment methods (Bitcoin, stablecoins, wire, BTCPay, LNbits). Inventory and revenue stats update after payment confirmation.

API

Full REST API at /api/v1/. Authenticate with Bearer tokens.

Create an API Key

Go to Settings > API Keys and create a new key. The raw key is shown once — save it.

Example: Create an Invoice

curl -X POST http://localhost:5000/api/v1/invoices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_email": "customer@example.com",
    "customer_name": "Alice",
    "currency": "USD",
    "lines": [
      {"description": "Widget", "quantity": 2, "unit_price": "49.99"}
    ]
  }'

Example: Get Invoice Status

curl http://localhost:5000/api/v1/invoices/INV-0001/status \
  -H "Authorization: Bearer YOUR_API_KEY"

See the complete API Reference for all endpoints.

Webhooks

Register webhook endpoints to receive real-time notifications:

curl -X POST http://localhost:5000/api/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yoursite.com/webhook",
    "events": ["invoice.paid", "invoice.confirmed"]
  }'

Events: invoice.created, invoice.paid, invoice.confirmed, invoice.expired, invoice.cancelled, payment.received, payment.confirmed, or * for all.

Payloads are signed with HMAC-SHA256 via the X-BTPay-Signature header. Verify with the secret returned when creating the endpoint.

Production Deployment

See the full Deployment Guide.

Quick Production Setup

# Install
cd /opt/btpay
python3 -m venv .venv
source .venv/bin/activate
make install-locked

# Create config.py with production secrets
cat > config.py << 'EOF'
SECRET_KEY = 'your-random-64-char-hex-string'
REFNUM_KEY = 'your-random-64-char-hex-string'
REFNUM_NONCE = 'your-random-48-char-hex-string'
DEV_MODE = False
EOF

# Create admin user
flask --app app user-create

# Run with gunicorn
gunicorn -c deploy/gunicorn.conf.py wsgi:app

Pre-built configs are included:

Important: Single Worker

BTPay uses an in-memory data store. You must run gunicorn with a single worker (workers = 1). The default config handles this. Multiple threads are fine (threads = 4).

Data Persistence

Data is stored as JSON files in the data/ directory:

Back up the data/ directory regularly. Use flask db-backup or make backup for manual backups.

Security

Development

# Run tests
make test

# Run tests with coverage
make test-cov

# Run dev server
make run

859 user story tests and unit tests covering ORM, auth, Bitcoin key derivation, invoicing, storefronts, API, webhooks, email, frontend, account security, Electrum server selection, stablecoin monitoring, BTCPay Server, LNbits, and security middleware.

Architecture

btpay/
  app.py                  # Flask app factory
  config_default.py       # Default configuration
  wsgi.py                 # Gunicorn entry point
  btpay/
    orm/                  # In-memory ORM (engine, model, query, columns, persistence)
    auth/                 # Authentication (models, sessions, totp, decorators, views)
    bitcoin/              # Bitcoin (xpub, descriptors, address pool, exchange, electrum, mempool, monitor)
    connectors/           # Payment connectors (BTCPay Server, LNbits, stablecoins, EVM RPC, wire)
    invoicing/            # Invoicing (models, service, checkout, payment methods, PDF, wire)
    storefront/           # Storefronts & donations (models, admin views, public views, fulfillment)
    api/                  # REST API (routes, serializers, webhooks)
    frontend/             # Web UI (dashboard, invoices, checkout, settings, filters)
    security/             # Security (tokens, hashing, crypto, validators, rate limit, csrf, middleware)
    email/                # Email (service, templates)
  templates/              # Jinja2 templates
  static/                 # CSS, JS, images
  tests/                  # 859 tests
  deploy/                 # gunicorn, nginx, systemd configs

Future Plans

Test Coverage

859 tests across 14 test modules:

Module Tests Covers
test_user_stories 378 End-to-end user flows, storefronts, and integration scenarios
test_bitcoin 102 xpub derivation, descriptors, address pool, exchange rates, Electrum, mempool
test_connectors 67 Stablecoin monitoring, EVM RPC, wire transfers
test_auth 60 Login, sessions, TOTP 2FA, account lockout, password changes
test_invoicing 58 Invoice lifecycle, payment methods, PDF generation
test_api 52 REST API endpoints, authentication, webhooks
test_orm 35 In-memory ORM engine, models, queries, columns, persistence
test_btcpay 25 BTCPay Server connector
test_lnbits 24 LNbits connector
test_phase7 21 Security middleware, hack detection, rate limiting
test_security_fixes 19 Security hardening and vulnerability fixes
test_refnums 9 NaCl SecretBox-encrypted reference numbers
test_tokens 5 Token hashing and verification
test_chrono 4 Date/time utilities
# Run all tests
make test

# Run tests with coverage report
make test-cov

Disclaimer

THIS SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

BTPay handles financial transactions. The authors and contributors are not responsible for any loss of funds, data, or business arising from the use of this software. You are solely responsible for securing your deployment, keys, and data. Use at your own risk.

License

MIT