Part 2 of 2 on how free2z accepts Zcash. Options checked October 12, 2026.
A payment arrives. Your application should notice, wait for its confirmation policy, and do something useful: unlock a subscription, record a donation, issue credits, or notify a buyer.
That is what we built Zcash Payment Hooks for: an open-source Rust library and Unix daemon that turns incoming shielded payments into durable events for your code. We run it beside our Zakura node, and use it to power ZEC purchases on free2z.
The wallet options are getting better
Zcash’s Rust ecosystem offers several routes, depending on whether you need a complete wallet, an embedded wallet, or a payment receiver.
| Option | What it gives you | Where automation fits |
|---|---|---|
| Zallet | Rust full-node RPC wallet replacing zcashd’s wallet; currently beta, with incomplete RPCs and possible breaking changes | Its transaction notification command receives a transaction ID when wallet transactions change; your adapter can query RPC and apply business rules |
| Z3 | Docker Compose deployment of Zebra and Zallet, with optional Zaino | Packages the node/wallet stack; application settlement belongs in your integration |
| zingolib | Wallet functionality to embed in an application | Build your receiving and automation interface around the library |
| librustzcash | Cryptographic and wallet-client foundations | Build a specialized integration using established Zcash implementations |
Zallet already offers payment-related command notifications. Choosing our daemon was about the interface we wanted: a receive-only process, beside Zakura, with durable per-payment events and independent delivery retries. Z3 documents a Zebra stack; substituting Zakura deserves its own compatibility testing.
Consumer wallets handle sending and managing keys. Services such as lightwalletd supply chain data. Recognizing your shielded receipts requires viewing authority; deciding what they purchase requires application policy.
A small receiving layer beside the node
zcash-paymentd takes a private Unified Full Viewing Key and reads full blocks through Zakura’s JSON-RPC. Using librustzcash, it decrypts incoming Sapling, Orchard and Ironwood notes, preserving the exact recipient, integer zatoshis and complete memo.
Zakura RPC → viewing-key decryption → private SQLite journal │ versioned payment events ┌──────────┴──────────┐ ▼ ▼ your hook another hook JSON on stdin separate retries
The receiver holds no spending key. A viewing key does expose receiving information, so it and the journal must stay private.
The event contract describes three lifecycle events: payment.observed for an opted-in mined observation, payment.confirmed after the configured confirmation depth, and payment.revoked for a chain correction. Observation is not mempool detection; revocation requires your application’s response policy, not an automatic reversal of external effects.
Memos are opaque data. The daemon does not interpret account names, prices or commands inside them.
Attach the code you want to run
After configuring the receiver and validating a run with no consumers, attach the repository’s harmless recording example with this consumer stanza. Replace the illustrative absolute paths with your installed files:
[[consumers]]id = "record-events-v1"argv = ["/usr/bin/python3", "/absolute/record_event.py", "/absolute/private/hook.sqlite"]timeout_seconds = 30observe_unconfirmed = false
Each invocation gets one versioned JSON event on stdin. The daemon runs literal executable arguments without a shell; memo content cannot become a command. Hooks are trusted code, not sandboxed plugins. A script can forward HTTP or pubsub; the daemon has no HTTP listener.
Exit zero acknowledges durable work. Failure or timeout retries. Consumers acknowledge independently. Delivery is at least once: record event_id with the effect in one transaction, and use the stable payment identity to prevent duplicate credits across lifecycle revisions or re-mining. Fence stale revisions; return success only after committing.
Our private free2z adapter reads {"act":"buy_2z","id":"username"}, credits the named account using a fresh backend price after at least three confirmations, and commits the receipt, balance, notification and outbox together. WebSocket events then show detection and completion and refresh the browser’s authoritative balance. The public daemon supplies payment events; our application owns this accounting and experience.
Proven on our deployment; room to grow
A production purchase sent 0.00867775 ZEC and received 1,112.081 2Z. The buyer confirmed both notifications and the live balance update. That proves this deployed purchase worked; this is early operator software, without a security audit.
Pruning needs care: the node must retain every block needed for the initial scan or catch-up from the saved cursor. Missing history stops scanning; it never silently skips payments. Back up the private journal and application deduplication records. Full-block scanning also has traffic and latency costs to measure.
Next work includes recovery testing, measured throughput and journal retention. A future Zakura indexer source could use the observation interface; it is not implemented.
Memos are optional to the core. Applications can instead route by distinct shielded recipients with backed-up invoice mappings. That is a possible design for other integrations, not a claim that free2z billing works without its memo.
Explore the code and setup guide, licensed MIT or Apache-2.0. We want payment automation to become an ordinary, reusable piece of Zcash infrastructure—and to improve it alongside the teams building the ecosystem.

