Tutorial · For developers

Accept DIVI Payments on Your Website

Give every invoice its own address, watch for the payment from your own node or from the public address index, and release the goods once it has enough confirmations. Every command below was run on testnet.

About 25 minutes · Checked 2026-10-21

1How a payment works

There is no payment processor in the middle. Your shop shows the customer an address and an amount; the customer's wallet sends a transaction; the transaction sits in the mempool for a moment, then lands in a block; every block after that is one more confirmation. Your job is to notice the transaction, count its confirmations, and match it to an order.

A block every
1 minute
1 DIVI
100,000,000 sat
Ways to watch
3

You can watch in three ways, from simplest to fastest: ask your own node's wallet, ask the public address index at services.divi.domains, or subscribe to the node's ZMQ feed. This page does all three, and finishes with a small Python script you can drop into a shop backend.

Build it on testnet first

Everything here was run on Divi testnet, where addresses start with x or y and coins are free from the faucet. On mainnet the commands are the same; only the addresses (D…), the ports and the money are real.

2One address per invoice

Never reuse an address between customers. If two orders share one address you cannot tell who paid. Ask the wallet for a fresh address when the invoice is created and store it with the order. The optional account name (IronDivi calls it a label) just groups the addresses in the wallet.

divi-cli getnewaddress invoice
yFA5y6f2pKUSeUMT7Lk7da3pydX2sG1WqF

divi-cli getnewaddress invoice
xwXtKp85QcED8wCrCRLdHJfctMk458jUr9

Show the customer the address and the exact amount in DIVI. Keep the amount in satoshis in your database (1 DIVI = 100,000,000 satoshis) so you never compare floating-point numbers; the address index below reports satoshis too. Two invoices are used on this page: 50 DIVI to yFA5y6…G1WqF and 12.5 DIVI to xwXtKp…8jUr9.

Both divid (C++ Divi v3.0.0) and IronDivi have getnewaddress, getreceivedbyaddress, listreceivedbyaddress, listsinceblock and gettransaction, so the wallet steps below work on either. The ZMQ step needs divid.

3Watch from your own wallet

You need a node with a wallet for this step: Install divid and Configuration files cover testnet=1, server=1 and the RPC user and password. The node that made the address sees the payment first, in the mempool, before any block. The simplest check is getreceivedbyaddress, which adds up everything an address has received with at least minconf confirmations. Ask twice: once with 0 to see an unconfirmed payment, once with your real threshold.

# seconds after the customer paid 12.5: in the mempool, no block yet
divi-cli getreceivedbyaddress xwXtKp85QcED8wCrCRLdHJfctMk458jUr9 0
12.50000000
divi-cli getreceivedbyaddress xwXtKp85QcED8wCrCRLdHJfctMk458jUr9 1
0.00000000

# about a minute later, after the next block
divi-cli getreceivedbyaddress xwXtKp85QcED8wCrCRLdHJfctMk458jUr9 1
12.50000000

To see which transaction paid, and how many confirmations it has now, use listreceivedbyaddress (every address with its txids) or gettransaction with the txid:

divi-cli listreceivedbyaddress 1
[
  {
    "address": "yFA5y6f2pKUSeUMT7Lk7da3pydX2sG1WqF",
    "account": "invoice",
    "amount": 50.00000000,
    "confirmations": 24,
    "bcconfirmations": 24,
    "txids": ["36a1bb249bec76e38e5b4d67bcc13131b60952b782c705aea8f09df024c0f5d5"]
  },
  …
]

divi-cli gettransaction 36a1bb249bec76e38e5b4d67bcc13131b60952b782c705aea8f09df024c0f5d5
{
  "amount": 50.00000000,
  "confirmations": 1,
  "txid": "36a1bb2…c0f5d5",
  "details": [
    { "account": "invoice", "address": "yFA5y6f2pKUSeUMT7Lk7da3pydX2sG1WqF",
      "category": "receive", "amount": 50.00000000, "vout": 0 }
  ]
}

If you would rather be told what is new since you last looked, listsinceblock takes the block hash you saved last time and returns every wallet transaction since, plus a lastblock hash to save for next time. The second argument is the number of confirmations you want lastblock to be behind the tip, so a short reorg cannot make you miss one.

divi-cli listsinceblock cd497a17a5355d44847ca5cc79162d4289f24fdb9cdbd2ac163e87463cfc5512 1
{
  "transactions": [
    { "account": "invoice", "address": "yFA5y6f2pKUSeUMT7Lk7da3pydX2sG1WqF",
      "category": "receive", "amount": 50.00000000, "confirmations": 1, … },
    { "category": "move", "amount": 12.50000000, "confirmations": 0, … }
  ],
  "lastblock": "…"
}
Why the 12.5 payment says "move"

On this test both invoices lived in the same wallet that paid them. A payment from a wallet to one of its own addresses is reported as category: "move" with no address. A real customer pays from their own wallet, and you get category: "receive" with the address, like the 50 DIVI above. getreceivedbyaddress counts both the same way.

4Watch from the public address index

If your shop has no node of its own (or you want a second opinion), the public testnet endpoint https://services.divi.domains/api/testnet/rpc/ runs a node with the address index on. It needs no password and only answers read-only methods; Talk to a Divi node from Python and JavaScript explains the endpoint. Three methods matter here:

MethodReturns
getaddressdeltasEvery change to the address: txid, block height, output index, satoshis
getaddressbalancebalance and received, in satoshis
getaddresstxidsJust the txids
curl -s https://services.divi.domains/api/testnet/rpc/ \
  -H 'Content-Type: application/json' \
  -d '{"method":"getaddressdeltas","params":[{"addresses":["xwXtKp85QcED8wCrCRLdHJfctMk458jUr9"]}]}'

While the 12.5 payment was only in the mempool, the index returned nothing at all:

{"error":null,"id":null,"result":[]}
# getaddressbalance at the same moment
{"error":null,"id":null,"result":{"balance":0,"received":0}}

One block later it was there, with the height, so you can count confirmations as getblockcount − height + 1:

{"error":null,"id":null,"result":[{"address":"xwXtKp85QcED8wCrCRLdHJfctMk458jUr9",
  "blockindex":3,"height":359949,"index":0,"satoshis":1250000000,
  "txid":"39f86883f742c3acdbca2c0934a1e5184008233f03c3ff287ec97b2537a481c6"}]}

# getaddresstxids
{"error":null,"id":null,"result":["39f86883f742c3acdbca2c0934a1e5184008233f03c3ff287ec97b2537a481c6"]}
The index only knows confirmed payments

The address index is built from blocks, so an unconfirmed payment is invisible to it. Divi v3.0.0 has no getaddressmempool (the node answers Method not found, and the public endpoint answers Method not allowed). If you need to show "payment seen, waiting for a block", use your own wallet (step 3) or ZMQ (step 5).

5Hear about it instantly with ZMQ

Polling every 15 seconds is fine for a shop. If you want the payment the moment it reaches your node, divid can publish every new transaction hash and block hash over ZeroMQ. When the 50 DIVI payment above was broadcast, a subscriber on the node printed its hash twice: once as it entered the mempool and again when the block that holds it arrived:

hashtx    36a1bb249bec76e38e5b4d67bcc13131b60952b782c705aea8f09df024c0f5d5  #721914
…
hashtx    36a1bb249bec76e38e5b4d67bcc13131b60952b782c705aea8f09df024c0f5d5  #721921
hashblock cd497a17a5355d44847ca5cc79162d4289f24fdb9cdbd2ac163e87463cfc5512  #369

On each hashtx, call gettransaction with the hash; if it is not a wallet transaction the node returns an error and you ignore it. On each hashblock, re-count confirmations for your open invoices. Setup, the message format and Python and Node subscribers are in React to new blocks and transactions with ZMQ. IronDivi has no ZMQ; use polling there.

6How many confirmations to wait for

A transaction in the mempool can still be dropped or replaced, and the newest block can, rarely, be replaced by a competing one (a reorg), which pushes its transactions back into the mempool. Each confirmation makes that less likely. Divi makes a block about every minute, so waiting costs the customer little:

ConfirmationsRoughlyGood for
0secondsShowing "payment seen" on the checkout page. Never release goods.
11 minuteDigital goods of small value
66 minutesA sensible default for anything you cannot take back
moreminutesLarge amounts; the cost is only the customer's time

Whatever number you pick, keep counting until the order is marked paid: ask with getreceivedbyaddress <address> <N> and only act when the amount is there with N confirmations. Do not mark an order paid on the 0-confirmation answer and forget about it.

Also accept over-payment (compare with >=) and decide what to do with under-payment; wallets round, and customers mistype. Keep the txid with the order so support can find it in Explorer 2.0: the 50 DIVI payment and the 12.5 DIVI payment from this page.

7A script that waits for the payment

This Python 3 script uses only the standard library. It takes an invoice address and amount, asks either your own node (getreceivedbyaddress) or the public index (getaddressdeltas plus getblockcount), prints what it sees, and exits with PAID once the amount is there with enough confirmations. The node's RPC user and password come from the environment, never from the command line.

# wait_for_payment.py
import argparse, base64, json, os, sys, time, urllib.request

PUBLIC = "https://services.divi.domains/api/testnet/rpc/"
SATOSHI = 100_000_000

def rpc(url, method, params, user=None, password=None):
    body = json.dumps({"jsonrpc": "1.0", "id": "shop", "method": method, "params": params}).encode()
    req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
    if user:
        token = base64.b64encode(f"{user}:{password}".encode()).decode()
        req.add_header("Authorization", "Basic " + token)
    try:
        with urllib.request.urlopen(req, timeout=30) as r:
            reply = json.load(r)
    except urllib.error.HTTPError as e:          # divid answers HTTP 500 with a JSON body on errors
        reply = json.load(e)
    if reply.get("error"):
        raise RuntimeError(reply["error"])
    return reply["result"]

def received_on_node(url, user, password, address, minconf):
    return float(rpc(url, "getreceivedbyaddress", [address, minconf], user, password))

def received_on_public(address, minconf):
    deltas = rpc(PUBLIC, "getaddressdeltas", [{"addresses": [address]}])
    tip = rpc(PUBLIC, "getblockcount", [])
    total = 0
    for d in deltas:
        confirmations = tip - d["height"] + 1
        if d["satoshis"] > 0 and confirmations >= minconf:
            total += d["satoshis"]
    return total / SATOSHI

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("address")
    ap.add_argument("amount", type=float, help="invoice amount in DIVI")
    ap.add_argument("--confirmations", type=int, default=6)
    ap.add_argument("--source", choices=["node", "public"], default="node")
    ap.add_argument("--every", type=int, default=15, help="seconds between checks")
    a = ap.parse_args()
    url = os.environ.get("DIVI_RPC_URL", "http://127.0.0.1:51475/")
    user, password = os.environ.get("DIVI_RPC_USER"), os.environ.get("DIVI_RPC_PASSWORD")
    while True:
        if a.source == "node":
            seen = received_on_node(url, user, password, a.address, 0)
            paid = received_on_node(url, user, password, a.address, a.confirmations)
        else:
            seen = received_on_public(a.address, 1)
            paid = received_on_public(a.address, a.confirmations)
        print(f"{time.strftime('%H:%M:%S')}  seen {seen:.8f}  confirmed {paid:.8f}  need {a.amount:.8f}", flush=True)
        if paid + 1e-9 >= a.amount:
            print("PAID")
            return 0
        time.sleep(a.every)

if __name__ == "__main__":
    sys.exit(main())

Run it against the public index for the first invoice, and against your own testnet node for the second (the node's testnet RPC port is 51475 by default; set DIVI_RPC_URL if yours differs):

python3 wait_for_payment.py yFA5y6f2pKUSeUMT7Lk7da3pydX2sG1WqF 50 --source public
20:23:54  seen 50.00000000  confirmed 50.00000000  need 50.00000000
PAID

# set these from your secrets store, not by typing them into a shell that keeps history
export DIVI_RPC_USER=your-rpcuser DIVI_RPC_PASSWORD=your-rpcpassword
python3 wait_for_payment.py xwXtKp85QcED8wCrCRLdHJfctMk458jUr9 12.5 --confirmations 3
20:23:54  seen 12.50000000  confirmed 12.50000000  need 12.50000000
PAID

In a real shop, run one check loop over all open invoices instead of one process per invoice, and store the result with the order. If you use IronDivi, point DIVI_RPC_URL at its port (51473 on testnet); it answers the same getreceivedbyaddress call, with HTTP 200 even on errors, which the script also handles.

8Keep the money safe

If something goes wrong

You seeWhat it means
getreceivedbyaddress … 0 shows the amount but … 1 shows 0The payment is in the mempool and no block has included it yet. Wait a minute and ask again.
getaddressdeltas returns [] while the wallet already shows the paymentNormal: the index only sees confirmed transactions. It fills in with the next block.
{"code":-32601,"message":"Method not allowed: getaddressmempool"}The public endpoint only answers its allowed read-only methods, and this one does not exist in Divi v3.0.0 anyway.
gettransaction says Invalid or non-wallet transaction idThe txid is not in this wallet. Either it pays someone else's address, or the node was restored without the address. Use getrawtransaction <txid> 1 on a node with txindex=1, or the public endpoint, to inspect it.
category: "move" instead of "receive"The paying wallet is the receiving wallet. Fine for testing; a customer's payment shows as receive.
Confirmations went down, or a payment disappearedA reorg replaced the newest block. Keep polling; the transaction normally returns in the next block. This is why orders wait for more than 1 confirmation.

Next: Talk to a Divi node from Python and JavaScript for the RPC basics, React to new blocks and transactions with ZMQ for the push feed, and Raw transactions and multisignature if your sweep address should need two keys. Still stuck? Ask the Divi chatbot, or see all tutorials.