Hosted Bitcoin and Lightning payment processor. Single API for on-chain + Lightning, pay-ins and payouts, plus fiat conversion.
- 01hosted BTC checkout
- 02Lightning invoicing without running a node
- 03BTC payouts to any wallet
- 04fiat-settled merchant payments
- 05subscription billing
- pnpm add opennode
| Variable | Scope | Description |
|---|---|---|
| OPENNODE_API_KEY | Server | OpenNode API key (use an Invoice key for charge creation). |
| OPENNODE_ENV | Server | 'live' for mainnet or 'dev' for testnet. |
| OPENNODE_WEBHOOK_SECRET | Server | Shared secret used to verify webhook signatures. |
Use OpenNode to accept Bitcoin + Lightning without running a node. Initialize the SDK with `const opennode = require('opennode'); opennode.setCredentials(process.env.OPENNODE_API_KEY, process.env.OPENNODE_ENV)`. Create a charge with `opennode.createCharge({ amount, currency: 'USD', callback_url, success_url, order_id })` — the response includes a BOLT11 Lightning invoice and an on-chain address. Render the hosted checkout URL or the QR client-side. On payment, OpenNode POSTs to `callback_url`; verify by HMAC-SHA256ing `id` with `OPENNODE_WEBHOOK_SECRET` and comparing to `hashed_order` before fulfilling.
- ⚑OpenNode is custodial — they hold incoming BTC until you withdraw or auto-convert. Evaluate counterparty risk vs. self-hosting BTCPay.
- ⚑Lightning payments are instant in UX but the upstream channel liquidity OpenNode manages can fail — always provide an on-chain fallback in the same charge.
- ⚑Webhook verification uses HMAC over the `id` field, not the full body; using the wrong field silently breaks verification.
- ⚑Charges have short expirations (default ~15 min) due to BTC/USD volatility — design the checkout flow to handle expired invoices gracefully.
- ⚑Auto-conversion to fiat carries a spread; check the live rate before quoting net merchant proceeds.
- ⚑Production vs. dev environments use entirely separate keys and webhooks — failing to switch breaks integrations silently.