# Ziggybank merchant onboarding — accept private stablecoin payments (DAI + BOLD) on Aztec # Everything after setup is API-driven: no server, no node of your own. # (One-time CLI setup uses OUR hosted endpoints — no RPC accounts on your side, ever.) # Custody model: YOUR keys, YOUR funds. You never send us anything spend-capable. # We hold view-only material to detect incoming payments — structurally, we cannot # move your money. Per-order BOLD addresses are bound to a key only YOU can derive. ## The three rails your customers can use (all live) 1. DAI — your tag + 3-word note in the customer's wallet send screen 2. BOLD — one-tap invoice contract call (wallets with a "Pay a store invoice" mode) 3. BOLD — a fresh per-order address (any wallet, plain transfer) All three match to your order automatically (3-word code + amount) and fire the same webhook. ## Step 0 — Install the zkmoney CLI and register your tag (one-time, 15 DAI) The CLI is open source: https://github.com/aztec-labs-eng/zkmoney-cli Releases ship a SHA256SUMS-verified installer; the source builds against zkmoney-public (the public zk.money wallet source) at a pinned commit — auditable. On macOS or Linux, with Node.js 22+ and the GitHub CLI signed in (gh auth login): gh release download -R aztec-labs-eng/zkmoney-cli -p install.sh -O - | sh Then point the CLI at its node endpoints (one-time config). We host them for you — no signups, no API keys, no RPC accounts anywhere on your side: zkmoney config set node.url https://ziggybank.xyz/rpc/aztec-full zkmoney config set l1.rpc https://ziggybank.xyz/rpc/eth ENDPOINTS (use exactly these): node.url → https://ziggybank.xyz/rpc/aztec-full (full Aztec node surface — sends, checkpoints, the aztec_* methods wallets need) l1.rpc → https://ziggybank.xyz/rpc/eth (Ethereum, archive logs) https://ziggybank.xyz/rpc/aztec is a RAW read-only pass-through for tooling — do NOT point your wallet/CLI at it; sends will fail there. Then create your account and register your tag: zkmoney account init --key file # set ZKMONEY_PASSPHRASE first to seal the key file zkmoney register # prints a funding address — send 15 DAI/USDC/USDT once zkmoney register status # wait until "registered" Your account lives at ~/.zkmoney/mainnet/account.json. Back it up with your passphrase. ## Step 1 — Register as a merchant (get your API key) curl -X POST https://ziggybank.xyz/api/merchants \ -H 'Content-Type: application/json' \ -d '{"tag": "", "webhookUrl": "https://your-app/hooks/ziggybank"}' → {"tag": "...", "apiKey": "<64-hex>"} # keep the apiKey secret ## Step 2 — Export your VIEW key locally (your spend key never leaves your machine) Download and run our tiny export tool ON YOUR OWN MACHINE: curl -sL https://ziggybank.xyz/download/export-view-key.mjs -o export-view-key.mjs ZKMONEY_PASSPHRASE='' node export-view-key.mjs What it does (read the source — it's short and uses only Node built-ins plus the crypto already inside your zkmoney CLI install): - Decrypts ~/.zkmoney/mainnet/account.json LOCALLY with your passphrase - Prints ONLY: viewKey (decrypts incoming payments — read-only), your PUBLIC signing key, your L2 address, and your tag - Your privateKey (the spend key) is NEVER printed and NEVER sent anywhere IMPORTANT — never send your sealed account blob or passphrase to anyone, us included. The sealed blob contains your spend key. If any integration asks for it, that is a red flag. Our API rejects sealed/passphrase on sight. ## Step 3 — Send the view-only registration (one call) Paste the export tool's output into this call: curl -X POST https://ziggybank.xyz/api/merchants/register-account \ -H 'Content-Type: application/json' -H 'X-Api-Key: ' \ -d '{ "viewKey": "", "signingPubkey": { "x": "", "y": "" }, "l2Address": "", "consent": "I consent to Ziggybank watching incoming payments to @ with this viewing key. It can see incoming payments and notes. It can never spend funds." }' What we do with it: the viewKey lets our watcher DECRYPT your incoming payment notes so we can detect and match them. That is the whole service. The pubkey binds your per-order BOLD addresses to you — the matching private scalar can only be re-derived from your passkey, on your machine. The consent string must be exact — if rejected, the API replies 400 with the precise string to send. ## Self-hosted watching (MAXIMUM PRIVACY — recommended if you can run a script) The default flow above has Ziggybank watching your incoming payments with your view key. If you'd rather share NOTHING with us, run the watcher yourself: curl -sL https://ziggybank.xyz/download/merchant-kit/selfhost-watch.sh -o selfhost-watch.sh chmod +x selfhost-watch.sh export ZKMONEY_PASSPHRASE='' export ZIGGYBANK_API_KEY='' ./selfhost-watch.sh Your keys never leave your machine. The watcher detects payments locally and posts ONLY the match (3 words + amount + tx hash) to the API, authenticated with your apiKey — scoped so it can only ever touch YOUR orders. Orders, pay pages, and webhooks work identically. (If you self-host the watcher, skip Step 2/3 — no viewKey registration needed at all.) Fully independent mode: the script is also the reference implementation — the whole matcher is "watch account, extract 3-word memo + amount, paid iff amount >= due". Point it at your own order store and Ziggybank drops out entirely. Kit readme: https://ziggybank.xyz/download/merchant-kit/README.md ## Step 4 — Take an order DAI order (default): curl -X POST https://ziggybank.xyz/api/orders \ -H 'Content-Type: application/json' -H 'X-Api-Key: ' \ -d '{"amountDai": "2.50"}' → {"orderId": "legal wood chimney", "payPageUrl": "https://ziggybank.xyz/api/pay/legal-wood-chimney", "payTo": "@yourtag", "instructions": "…"} BOLD order (per-order fresh address + invoice field): curl -X POST https://ziggybank.xyz/api/orders \ -H 'Content-Type: application/json' -H 'X-Api-Key: ' \ -d '{"amountDai": "2.50", "asset": "bold"}' → {"orderId": "happy bacon cancel", "payTo": "0x27c1…", # fresh per-order address, bound to YOUR key "invoiceField": "19331…", # for one-tap contract payments "payPageUrl": "…"} Send your customer to payPageUrl — it shows every way to pay and flips to "paid" on its own when the payment lands. ## Step 5 — Fulfill We POST to your webhook on match: {"event":"order.paid","orderId":"legal wood chimney","amountDai":"2.50","paidTxHash":"0x…"} Or poll: curl https://ziggybank.xyz/api/orders/legal-wood-chimney -H 'X-Api-Key: ' ## Rules of the road - Matching is word-sequence only — capitalization, extra spaces, punctuation ignored. - Settlement is exact: payment must be >= the order amount. Overpayments are accepted; underpayments leave the order open. - Orders expire after 24 hours open. - Your funds arrive directly at your account — no vault, nothing to withdraw from us. - Test first: create a small order, pay it yourself, watch the webhook fire. Questions or issues: reply in chat and we'll sort it.