How it works
A DigiPay invoice flows through five states: pending → seen → paid → confirmed,
plus expired or underpaid as terminal error cases.
Here's the full lifecycle, from merchant sign-up to webhook delivery.
1. Sign up with Digi-ID
There's no password flow. You scan a QR with your DigiByte wallet, the wallet signs a challenge URI, and DigiPay creates a merchant record keyed by your Digi-ID address.
Your Digi-ID address is just a public address — not a spending key — so this is purely for identity. No funds are ever linked to it.
2. Configure a store
Every account starts with a Default Store. Add more if you run multiple shops — each store has its own receive address, webhook, and sessions. For each store, set a receive target in one of two modes:
- Xpub (recommended). Paste your BIP84 account-level extended public key (
xpub6...). DigiPay derives a fresh address atm/0/nfor every invoice — no reuse, proper per-invoice isolation. - Single address. Paste one
dgb1…. All invoices share it. Simpler but your tx history is linked. Fine for a tip jar.
You can copy the xpub from your DigiByte Wallet app: Settings → Share xpub (watch-only).
3. Create a session
Create an API key from the dashboard's API keys tab (the raw secret is shown once — save it). From your server, POST to /v1/pay/sessions with the amount in DGB. You get back an invoice URL your buyer can load, plus a BIP21 URI any DigiByte wallet understands.
curl -X POST https://pay.dgbwallet.app/v1/pay/sessions \
-H "Authorization: Bearer $DIGIPAY_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 12.5,
"storeId": "sto_…", // optional — defaults to first store
"label": "Order #4201"
}'
Redirect the buyer to the returned checkout_url, or open it in-page with the JS embed (live demo →):
<script src="https://pay.dgbwallet.app/embed/digipay.js"></script>
<button data-digipay-checkout data-session-id="ses_...">Pay with DigiByte</button>
4. What the buyer sees
The hosted checkout shows a QR code, an "Open in wallet" deep link, a countdown to expiry (default 30 minutes), and a live status pill. The pill updates in real time via SignalR as the tx moves through states:
- Waiting — no tx detected yet.
- Detected — tx is in the mempool.
- Paid — one confirmation. Usually ~15 seconds on DigiByte.
- Confirmed — six confirmations. Fully settled.
5. Receive webhooks
Every state change hits the store's webhook URL with a signed POST. Every attempt is recorded in the dashboard's delivery log — if your receiver was down, hit Replay and DigiPay re-sends the exact same payload. Verify the signature with HMAC-SHA256 using the secret DigiPay generated when you saved the webhook URL:
const expected = crypto.createHmac('sha256', secret)
.update(rawBody).digest('hex');
const provided = req.headers['x-digipay-signature'].replace('sha256=', '');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided))) {
return res.status(401).end();
}
Headers on every webhook:
X-DigiPay-Event—session.seen,session.paid,session.confirmed,session.expired,session.underpaid.X-DigiPay-Signature—sha256=…of the raw body.X-DigiPay-Delivery— unique id for idempotency.
6. Late payments
If a buyer pays after expiry, DigiPay still reconciles — we keep polling expired sessions for three days. The webhook fires with the normal session.paid event, so your server doesn't need special-case code for this.
Next
Ready to build? Head to the API reference for the full endpoint list, or sign in and try it.