Back to docs

Bitcoin Payments

Top up credits with on-chain Bitcoin — unique amount per invoice, no third-party processor

Bitcoin Payments

PrivateRouter accepts on-chain Bitcoin as a first-class payment method. We run a lightweight native payment module — there's no third-party processor, no BTCPay Server middleman, no KYC, and no chargebacks.

How it works

  1. Pick a credit pack ($5, $10, $25, $50, $100, or $500) on the Billing page.
  2. The server fetches the live BTC/USD rate from CoinGecko, converts your USD amount to BTC, and adds a tiny random satoshi delta so your invoice has a unique BTC amount nobody else is paying right now.
  3. You're shown a payment page with:
    • Our receiving Bitcoin address (the same one for every customer).
    • The exact BTC amount to send (down to the satoshi).
    • A QR code (BIP-21 bitcoin: URI — wallets will pre-fill both the address and amount).
    • A 1-hour countdown.
  4. Send the exact BTC amount from any wallet to that address.
  5. Our background monitor sweeps the blockchain every ~30 seconds (via mempool.space with a blockchain.info fallback). When it sees a transaction to our address whose value matches your invoice's unique amount, the invoice flips to processing.
  6. Once the transaction has ≥ 1 confirmation, your credits are added to your balance automatically.

Why a unique amount instead of a fresh address? Because addresses cost us nothing but coordinating wallets across thousands of fresh addresses adds operational complexity. The 50–999-satoshi delta we tack onto your invoice is unique among open invoices on the address — so the incoming tx amount alone tells us which invoice it belongs to. The delta is < $0.10 at current prices, well inside the fee-rounding error your wallet already shows.

How long does it take?

  • Mempool detection — usually within 30 seconds of your wallet broadcasting the tx. We poll on a short interval.
  • First confirmation — typically 10–30 minutes depending on the fee you set. Once we see ≥ 1 confirmation, credits are applied immediately.

What gets stored

  • Your invoice ID, USD amount, the exact BTC amount we asked for, and the USD/BTC rate at the moment of creation.
  • The on-chain txid of the payment once we detect it (so you can look it up on any block explorer).
  • The number of confirmations at the moment of settlement.

We do not store your wallet address, your IP, or any chain-graph data beyond the transaction that paid us.

Bitcoin-only customers

If you only ever pay with BTC, you don't need to give us anything but an email and a password. We never ask for your wallet — you send funds to us; the chain is the receipt.

Common questions

  • Wrong amount sent — invoices look for an exact amount (or slightly more — slight overpay still settles). If you sent less, the invoice will stay new and expire after 1 hour. Open a new invoice and try again. If you sent more, the overage is applied as a bonus.
  • Invoice expired — our invoices expire 1 hour after creation if no matching payment is seen. Funds you do send to our address after expiry will still arrive in our wallet, but won't be auto-credited; contact support with the txid.
  • Can I pay with Lightning? — Not at the moment. Lightning support was part of the previous BTCPay-Server-based flow; we removed that to ship a leaner native module. We may bring it back as a separate plugin.
  • Can I see all my BTC top-ups? — Yes, the Billing page lists every BTC invoice you've created, their status, and links to the on-chain tx for any settled ones.

Operator notes

The receiving address is set in BTC_RECEIVING_ADDRESS. When the env var is unset, the BTC top-up endpoints return HTTP 503. Other tunables in .env:

  • COINGECKO_API_KEY — optional, lifts the public rate limit on the BTC/USD price lookup.
  • BTC_RATE_TTL_SECONDS — how long to cache the BTC/USD rate (default 60s).
  • BTC_INVOICE_WINDOW_MINUTES — how long an invoice is payable for (default 60).
  • BTC_MIN_CONFIRMATIONS — confirmations required before crediting (default 1).
  • BTC_MONITOR_INTERVAL_SECONDS — how often the background sweep runs (default 30).