Accepting cryptocurrency usually forces an awkward choice: either you hand your customers' payments to a custodial processor that holds the coins (and the risk, and a cut), or you wire up a node and a mountain of edge cases yourself. VBWD's crypto payments bundle takes a third path — the money settles directly to the merchant's own wallet, and the platform never holds a key. This is a walkthrough of how it works, end to end, from the buyer tapping "pay with Bitcoin" to the invoice flipping to paid.

Non-custodial, in one sentence

A buyer pays a real invoice in Bitcoin, Ether, or a stablecoin; the platform derives a unique receiving address for that invoice from the merchant's watch-only key, locks a fiat-to-crypto rate, watches the chain for the deposit, and — only once the confirmation threshold is met — captures the invoice through the same payment seam every other provider uses. VBWD holds zero private keys. The coins land in your wallet, not a platform's.

That single design decision — watch-only derivation instead of custody — is what makes this different from a hosted crypto checkout. It follows the same model self-custodial tools like BTCPay Server popularised, wired into VBWD's existing billing and invoice engine so a crypto payment behaves like any other payment on the platform.

The buyer's side

At checkout, choosing "Crypto (on-chain)" opens a pay panel. The coin selector — BTC, ETH, USDT, USDC — is driven by configuration, not hardcoded, so a merchant offers exactly the coins they want to receive.

VBWD crypto checkout: buyer selects a coin (BTC, ETH, USDT, USDC)

Pick a coin and the platform creates a charge: it derives a fresh per-invoice address from the watch-only key at the next HD index (so no address is ever reused), locks the rate, and shows a BIP21 QR code rendered client-side as an inline SVG — no remote image, nothing leaking to a third party. The panel tells the buyer to "send exactly 0.00025 BTC", offers a copyable address, and runs a rate-lock countdown so both sides are protected from volatility during the window.

The live charge: BIP21 QR code, exact amount, copyable per-invoice address, and a rate-lock countdown

The panel polls the charge status. Below the confirmation threshold, capture is deliberately withheld. Once the deposit is confirmed on-chain, the poller flips to "settled" and the buyer lands on Payment Successful — status Paid, amount €15.00, payment method: crypto. Notably, the invoice was marked paid by the core's payment-captured handler, not by the plugin reaching in and touching the invoice.

Payment Successful screen: status Paid, amount, payment method crypto

The money loop, in the open

The heart of the bundle is a small, honest set of routes. Creating a charge returns the derived address, the locked crypto amount, the rate and its source, an expiry, and a BIP21 URI:

POST /api/v1/plugins/crypto/create-charge  {invoice, coin: BTC}
→ 201 { address: "…0001", crypto_amount: "0.00025", coin: "BTC",
        rate: "60000", rate_source: "mock", status: "awaiting",
        uri: "bitcoin:…0001?amount=0.00025", expires_at: … }

Polling status before the threshold returns the truth and withholds capture:

GET …/charges/<invoice>/status?coin=BTC
→ { status: "awaiting", confirmations: 0, confirmations_required: 2 }

When a signed webhook arrives with enough confirmations and a matching amount, the charge captures exactly once — and a re-delivered webhook for the same transaction is rejected by an idempotency guard, so there is no double-capture:

POST …/webhook (X-Crypto-Signature) {coin, address, tx_hash, confirmations: 6, amount: "0.00025"}
→ { "captured": true }   # 6 ≥ 2 confs, amount matches → emit_payment_captured (once) → invoice PAID
POST …/webhook (same tx re-delivered)
→ { "captured": false }  # idempotency guard on tx_hash

Amounts are handled as decimals, not floats, because money bugs hide in rounding. And the whole flow runs in tests against deterministic mock chain and rate adapters — zero secrets, zero network — which is how it can be demonstrated and verified in CI without touching a real blockchain.

The merchant's side

Admins get a dedicated area at Sales → Crypto, built with the same settings-block pattern as the rest of the backoffice. The Charges tab lists every charge with coin and status filters, per-invoice addresses (each distinct — no reuse), amounts, transaction hashes, and status badges; invoice cells link straight to the canonical invoice detail page.

Admin Charges tab: coin/status filters, charges table, status badges, per-invoice addresses

The Settlements tab rolls the charges up: totals by status and a coin-by-status matrix, so a merchant can see at a glance what has settled and what is still awaiting confirmation.

Admin Settlements tab: totals by status and a coin-by-status matrix

Before going live, the merchant can verify the receiving wallet — confirming that the address derivation matches the wallet they control. It is the step that turns "trust me" into "check it yourself", which is exactly the posture a non-custodial system should take.

Admin: verify the receiving wallet before going live

How it fits the platform

Two things make this clean rather than bolted-on. First, it captures through the existing payment seam — the same emit_payment_captured path that Stripe or PayPal use — so an invoice paid in Bitcoin is, to the rest of the system, just a paid invoice. The core's agnostic design means it never learns what "Bitcoin" is; all of that lives in the payment plugin. Second, the chain and the rate are behind ports: a chain provider that watches for deposits and a rate provider that prices the invoice. Swap the mock adapters for a real node or service and the flow is unchanged — the same architecture that lets VBWD swap any payment provider applies to crypto too.

The honest limits

Non-custodial is a feature and a responsibility. You supply and control the watch-only key, and you run (or connect to) something that watches the chain and reports confirmations — a node or a provider. The confirmation threshold is a security trade-off you choose: more confirmations means stronger finality but a slower "paid" for the buyer. Rate-locking protects a window, not forever, so an expired charge needs a fresh rate. And receiving crypto has tax and accounting consequences that are yours to handle — the platform records the payment; it does not file your return. What the bundle gives you is the hard part done correctly: unique addresses, locked rates, threshold-gated capture, idempotent webhooks, and coins that go straight to your wallet.

The takeaway

Taking crypto does not have to mean handing your payments to a custodian. VBWD's crypto bundle derives a per-invoice address from your own watch-only key, prices and locks the invoice, waits for real confirmations, and captures through the same seam as every other payment — while the coins settle directly to you and the platform holds nothing. Read the walkthrough above, then wire in a real chain and rate provider when you are ready to go live.

The crypto bundle is a payment plugin like any other — enable it, configure your coins and watch-only key, verify the wallet, and the rest of the billing engine treats crypto exactly like a card.