The recommended integration. Your wallets run on servers you control (DigiByte / Bitcoin / Litecoin Core, encrypted with your passphrase). On each relevant transaction the node pushes a verified payment to your backend over TLS with a shared secret. No wallet — and no watcher — ever runs on the backend side. The backend only ever stores public addresses and fulfills orders.
YOUR NODE SERVER (per coin) YOUR BACKEND
┌───────────────────────────┐ walletnotify %s ┌────────────────────────┐
│ DigiByte/Bitcoin/Litecoin │──► walletnotify.sh ──┐ │ payment-ingest endpoint│
│ Core (localhost RPC, │ blocknotify %s ├──►──┤ (shared secret) │
│ encrypted, your keys) │──► blocknotify.sh ───┘ TLS │ mark invoice paid │
│ │ (cron) refill-dd ─────► │ dd-pool-refill │
└───────────────────────────┘ └────────────────────────┘
keys never leave your server the backend never holds keys / a wallet
- walletnotify (
walletnotify=…/walletnotify.sh %s) fires on every wallet tx. The script reads the tx's receive outputs from the local RPC and POSTs each{asset, network, address, txid, vout, amount, confirmations}to yourINGEST_URL. The backend matches the address to the open invoice (addresses are unique per invoice) and records the payment. - blocknotify (
blocknotify=…/blocknotify.sh %s) fires on every new block and re-reports recent receives so their confirmations advance to the threshold — then the backend marks the invoice paid (idempotently). DigiDollar note: walletnotify is not guaranteed to fire on a DigiDollar receive (0-sat P2TR outputs can fail theIsMinegate) — the blocknotify poll oflistdigidollarunspentis the authoritative DigiDollar detection path. - DigiDollar only: because DigiDollar has no watch-only model, the node also
runs
refill-dd-pool.mjson a cron to push freshgetdigidollaraddressaddresses (public only) into your backend's address pool.
The risky logic (amount/confirmation parsing, integer base units) lives in the
unit-tested @blockindex/crypto-payments library; these scripts are thin I/O.
# digibyte.conf (DigiByte + DigiDollar) — also bitcoin.conf / litecoin.conf
server=1
txindex=1 # MANDATORY for DigiDollar
digidollar=1 # DigiByte only
rpcbind=127.0.0.1 # localhost ONLY — RPC never faces the internet
rpcauth=...
walletnotify=/opt/merchant/walletnotify.sh %s
blocknotify=/opt/merchant/blocknotify.sh %sBitcoin/Litecoin Core use the identical
walletnotify/blocknotifyoptions.
npm install @blockindex/crypto-payments
cp lib.mjs walletnotify.mjs blocknotify.mjs refill-dd-pool.mjs walletnotify.sh blocknotify.sh /opt/merchant/
chmod +x /opt/merchant/*.sh
cp .env.example /opt/merchant/.env # then fill it in and restart the nodeSee .env.example for every variable. The short version:
CRYPTO_ASSET=DIGIDOLLAR # or BTC | LTC | DGB (one per coin daemon)
CRYPTO_NETWORK=mainnet # mainnet | testnet | regtest
CORE_RPC_URL=http://127.0.0.1:14022
CORE_RPC_USER=...
CORE_RPC_PASSWORD=...
CORE_WALLET=dd_hot # wallet name if multi-wallet
INGEST_URL=https://example.com/api/crypto-payment-ingest
INGEST_SECRET=<long random shared secret, also set on the backend>
DD_REFILL_URL=https://example.com/api/crypto-dd-pool-refill # DigiDollar only
DD_REFILL_SECRET=<a DIFFERENT long random secret> # DigiDollar only
DigiDollar pool cron:
*/5 * * * * cd /opt/merchant && CRYPTO_ASSET=DIGIDOLLAR node refill-dd-pool.mjs >> /var/log/dd-pool.log 2>&1- Node RPC bound to
127.0.0.1; never internet-exposed. Keys never leave your server. - Transport is HTTPS/TLS; requests carry the
x-crypto-ingest-secretshared secret, verified (constant-time) before any side effect. - Only public addresses + amounts + confirmations are ever sent — never keys.
- Use a separate
DD_REFILL_SECRETfor the pool refill: whoever holds the refill secret can inject attacker-owned addresses and capture customer payments. Audit the pool periodically: every unused address must beismineon the hot wallet. - The backend re-validates the reported amount against the invoice and credits
only at the required confirmation depth, idempotently by
(asset,network,txid,vout).