Accept Bitcoin payments through Arkade — a self-custodial, off-chain Bitcoin Layer 2 — directly inside BTCPay Server.
btcpay-arkade is a BTCPay Server plugin that integrates Arkade as a payment method. It lets merchants accept instant, low-fee Bitcoin payments off-chain while retaining full self-custody — no Lightning node required, no custodian involved.
Payments are settled through Virtual UTXOs (VTXOs), Arkade's off-chain Bitcoin outputs that are cryptographically anchored to real Bitcoin and can be unilaterally exited to the base chain at any time.
Direct VTXO-to-VTXO off-chain payments within the Arkade network. Instant settlement, zero routing fees. Payers need an Arkade-compatible wallet.
Payers with Lightning wallets pay a BOLT11 invoice. The plugin uses Boltz's trustless submarine swap to convert the Lightning payment into a VTXO in your Arkade wallet. No Lightning node needed on the merchant side.
Payers send on-chain Bitcoin to a Taproot "boarding address." The Arkade operator batches this into the next batch, converting the on-chain UTXO into a VTXO. If the operator is unresponsive, the payer can reclaim funds unilaterally after a timelock.
The checkout page presents all applicable methods in a single BIP-21 QR code, letting any wallet pay automatically.
BTCPay Server
└── Arkade Plugin
├── ArkController # HTTP endpoints for store management
├── ArkContractInvoiceListener # Monitors contract state → updates invoice status
├── BoardingTransactionListener# Watches on-chain boarding UTXOs via NBXplorer
├── ArkadeSpendingService # Sends payments (payouts, refunds)
└── NNark (submodule) # .NET Arkade SDK
├── NArk.Core # Wallet, VTXO logic, HD/SingleKey signers
├── NArk.Storage.EfCore # PostgreSQL persistence (EF Core)
└── NArk.Swaps # Boltz submarine/reverse swap client
The plugin persists all state (VTXOs, contracts, swaps, intents, wallets) in BTCPay's existing PostgreSQL database via EF Core migrations.
- BTCPay Server (self-hosted, any recent version)
- PostgreSQL (bundled with standard BTCPay deployments)
- Arkade server (arkd) v0.9.0 or later — accessible over gRPC from your BTCPay host
- .NET 10 SDK (if building from source)
⚠️ Alpha software. This plugin is actively developed and not yet recommended for high-value production deployments. Always maintain a backup of your seed phrase.
- Open your BTCPay Server instance
- Go to Server Settings → Plugins
- Search for "Arkade"
- Click Install and restart when prompted
git clone https://github.com/ArkLabsHQ/btcpay-arkade.git
cd btcpay-arkade
./setup.sh # Pulls submodules, restores workloads, publishes pluginOn Windows:
.\setup.ps1The setup script will:
- Pull the
submodules/btcpayserversubmodule - Restore .NET workloads
- Create a plugin entry in your BTCPay config
- Publish the plugin to the correct location
- Navigate to your BTCPay store → Settings → Arkade
- Enter your Arkade server URL (e.g.
https://arkd.yourdomain.com) - Import your wallet:
- HD Wallet: paste a BIP-39 mnemonic (12 or 24 words)
- SingleKey Wallet: paste a Nostr
nsecprivate key
- Go to Store Settings → Payment Methods
- Enable Arkade as a payment method
- Optionally enable Lightning (via Boltz) if you have a Boltz instance configured
| Setting | Default | Description |
|---|---|---|
| Boarding Address | Enabled | Show boarding address on invoices (on-chain entry to Arkade) |
| Boarding Minimum | 5000 sats | Minimum amount to display boarding address (floor: 330 sats / P2TR dust) |
| Sub-dust Payments | Disabled | Accept payments below 330 sats (no dust limit for VTXOs) |
| Auto-sweep Address | — | Forward all received funds to this on-chain address automatically |
- Full hierarchical deterministic key derivation
- Unique address per invoice (BIP-44 style)
- Supports boarding addresses (requires HD derivation)
- Recommended for merchants
- Single static key — all contracts derive from one key
- Simpler setup
- Boarding addresses not supported
- Suitable for lightweight deployments
- No signing material stored on the server — the merchant pastes a
Taproot account descriptor (e.g.
tr([fingerprint/86'/0'/0']xpub.../0/*)for HD style, ortr(pubkey)for single-key style) and the plugin observes the wallet by deriving addresses and watching VTXOs. - Read-only operations work out of the box: receive, balance display, invoice payment detection, contract listing.
- Signing-dependent operations (batch participation, unilateral
exits, payouts) require a remote signer. Install the companion
BTCPayServer.Plugins.Appplugin and pair a BTCPayApp device — the device holds the private key and signs over a SignalR bridge. Without a paired device the wallet is still useful for monitoring; signing calls fail with a descriptive"install the App companion plugin"error scoped to the operation, not to startup. - Setup: in the initial-setup wizard, pick Pair a watch-only wallet
under I have a wallet and paste the descriptor. Example:
tr([abcd1234/86'/0'/0']xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*)
- Unified BIP-21 QR code covers Arkade native + Lightning in one scan
- NFC-compatible checkout component
- Real-time payment status updates via VTXO subscription
- Boarding payments show as "Processing" until 1 on-chain confirmation, then "Settled"
- Sub-dust toggle for micro-payments
- Import via mnemonic or nsec
- View balance in BTC with show/hide privacy toggle
- Clear wallet configuration without losing on-chain funds
- Contract sync on import
- List, filter, and inspect all virtual UTXOs
- Track spend status, expiry, and settlement transaction
- VTXO asset support (Arkade programmable assets)
- Construct batch payment intents with multiple outputs
- Intent builder UI with fee breakdown
- Unified Send wizard: QR scanning, BIP-21 parsing, multi-output, manual coin selection
- Boltz submarine swaps for incoming Lightning payments
- Boltz reverse swaps for outgoing Lightning payments
- Real-time swap lifecycle monitoring
- LNURL-pay destination support
- Process Arkade payouts through BTCPay's native payout system
- Payout tracking integration in the Send wizard
- "Recent Activity" widget: latest VTXOs, intents, and swaps at a glance
- Balance display on BTCPay store overview
- .NET 10 SDK
- Docker (for test environment)
- PostgreSQL
After running setup.sh, start the local regtest environment (Bitcoin + arkd + Boltz/Fulmine) — a cross-platform Node CLI, no WSL required:
node submodules/NNark/regtest/regtest.mjs start --profile boltz,delegateOn Windows (wraps the same CLI; extra arguments pass through, e.g. start-test-env stop):
start-test-env.cmdThis spins up a regtest Bitcoin node, an Arkade server, and supporting services locally. Then run the E2E test suite:
dotnet test NArk.E2E.Tests/NArk.E2E.Tests.csproj./add-migration.sh <MigrationName>
# or on Windows:
.\add-migration.ps1 <MigrationName>btcpay-arkade/
├── BTCPayServer.Plugins.ArkPayServer/ # Main BTCPay plugin
│ ├── Controllers/ # HTTP controllers
│ ├── Data/ # EF Core entities & migrations
│ ├── Models/ # View models
│ ├── Services/ # Background services
│ ├── Views/ # Razor views
│ └── PaymentHandler/ # BTCPay payment method integration
├── NArk.E2E.Tests/ # End-to-end test suite
├── submodules/
│ ├── NNark/ # .NET Arkade SDK (NArk.Core / .Swaps / .Storage.EfCore)
│ └── btcpayserver/ # BTCPay Server source (build dependency)
├── docs/ # Internal design documents
├── setup.sh / setup.ps1 # First-time setup scripts
└── add-migration.sh / .ps1 # EF Core migration helpers
CI automatically creates a GitHub Release with the changelog body when a version tag is pushed:
git tag v2.1.14
git push origin v2.1.14Off-chain Bitcoin outputs secured via collaborative (user + operator) and unilateral (timelocked) Taproot spending paths. VTXOs are the atomic unit of value in Arkade. The operator can never steal them — the unilateral exit path is always available.
Periodically, the Arkade operator batches pending VTXOs into an on-chain commitment transaction, anchoring off-chain state to Bitcoin. This is how off-chain payments get Bitcoin-level finality.
Payment flows are modeled as Taproot contracts derived from a wallet descriptor and derivation index. Each invoice gets a unique contract address.
Taproot addresses that serve as the entry point into Arkade for on-chain funds. When funded, the operator converts the UTXO into a VTXO in the next batch. Protected by a timelock for unilateral recovery.
At any time, a user can exit to on-chain Bitcoin without the operator's cooperation by broadcasting the VTXO tree transaction. This is the security guarantee that makes Arkade non-custodial.
- The operator is trusted for liveness only — never for custody
- Users can always exit to on-chain Bitcoin unilaterally
- Private keys never leave your server
- The plugin uses POST redirects for private key display (never URL query params)
- All wallet state is stored in your own PostgreSQL database
Never share your mnemonic or nsec with anyone, including Ark Labs.
The plugin exposes a store-scoped REST API under /api/v1/stores/{storeId}/arkade/*. Endpoints are authenticated via BTCPay's standard Greenfield API key scheme and use the same permission policies as the rest of BTCPay (btcpay.store.cansettings, btcpay.store.canmodifystoresettings).
GET /api/v1/stores/{storeId}/arkade/wallet— current Arkade wallet config.POST /api/v1/stores/{storeId}/arkade/wallet— create or import a wallet.PATCH /api/v1/stores/{storeId}/arkade/wallet/settings— update destination, sub-dust, boarding.DELETE /api/v1/stores/{storeId}/arkade/wallet— unlink wallet from the store.GET /api/v1/stores/{storeId}/arkade/balance— available / locked / recoverable / boarding sats.GET|POST /api/v1/stores/{storeId}/arkade/address— read or mint a receive / boarding address.POST /api/v1/stores/{storeId}/arkade/send— send to an Ark address, BIP21 URI, or BOLT11 invoice.POST /api/v1/stores/{storeId}/arkade/estimate-fees— estimate fees for a prospective send (Arkade / Batch / Lightning).POST /api/v1/stores/{storeId}/arkade/parse-destination— classify a destination (Ark / BIP21 / BOLT11 / LNURL).GET /api/v1/stores/{storeId}/arkade/vtxos— list VTXOs.GET /api/v1/stores/{storeId}/arkade/intents— list pending batch intents.DELETE /api/v1/stores/{storeId}/arkade/intents/{intentTxId}— cancel an intent.GET /api/v1/stores/{storeId}/arkade/contracts— list address derivations.GET /api/v1/stores/{storeId}/arkade/swaps— list Lightning / chain swaps.GET /api/v1/stores/{storeId}/arkade/server-info— Ark operator info.GET /api/v1/stores/{storeId}/arkade/status— overall service status.GET /api/v1/stores/{storeId}/arkade/boltz-limits— Boltz swap limits and fees.POST /api/v1/stores/{storeId}/arkade/sync— force a VTXO + boarding sync.
POST /api/v1/stores/{storeId}/arkade/send
Authorization: token <api-key>
Content-Type: application/json
{
"destination": "ark1...",
"amountSats": 25000,
"inputOutpoints": ["abc...:0", "def...:1"]
}amountSatsis required when the destination is a bare Ark address. For BIP21 URIs it overrides any embeddedamount. For Lightning destinations it must be omitted (the BOLT11 invoice fixes the amount).inputOutpointsis optional. When provided, only the listed VTXOs are spent — automatic coin selection is bypassed. Must be omitted for Lightning destinations.
See CHANGELOG.md for the full version history.
Pull requests are welcome. For significant changes, open an issue first to discuss.
- Branch naming:
your-name/short-description - Ensure
dotnet build NArk.slnpasses before submitting - Add or update tests for new payment flows
- Arkade — the Ark protocol implementation
- Ark Labs — the team building Arkade
- BTCPay Server — the self-hosted payment processor
- Boltz Exchange — trustless Lightning ↔ on-chain swaps
MIT © Ark Labs