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 at m/0/n for 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-Eventsession.seen, session.paid, session.confirmed, session.expired, session.underpaid.
  • X-DigiPay-Signaturesha256=… 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.

An unhandled error has occurred. Reload 🗙

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.