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.
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.
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.

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 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.

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_hashAmounts 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.
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.

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.

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.

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.
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.
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.