Discover incoming payments by paymentId
What this lets you do
Find payments that have been sent against a specific paymentId you generated. The SDK's standard discoverNotes() flow handles this: it builds a tag-to-request map from your open requests and matches Note events whose indexed hint equals a request's tag (Poseidon([paymentId])), then decrypts each hit with that request's paymentPrivKey and marks the request fulfilled.
Payment-request discovery uses the request tag to narrow the event stream before decrypting. You don't call a separate scan function for payment requests. Instead, keep your generated requests loaded in the session and run normal discovery.
Constraints & limits
- Discovery cost. Only events with a matching request tag are decrypted with that request's key. Prune fulfilled requests to keep the tag map small.
- ASP note-events helps a lot. When
aspUrlis configured, the SDK pulls the encrypted Note event stream from/{chainId}/public/note-events(plus RPC gap-fill) instead of chunkingeth_getLogsitself. - Discovery scans from the sync cursor. Discovery walks events from
fromBlock(if you pass it) or theNoteManager's persisted sync cursor up to the chain head. The request'screatedAtis lifecycle metadata, not a scan lower bound. To avoid re-walking history, persist the NoteManager sync cursor across sessions. To backfill a request older than your cursor, pass an explicitfromBlock. - Request persistence is SDK-managed. The SDK persists open requests through its payment-request storage adapter and re-derives each request's
paymentPrivKeyfrom your viewing key, owner address, andpaymentIdon load, so you don't store the private key yourself. Load persisted requests on session build so discovery sees them.
What it unlocks next
- Match payments to off-chain invoices automatically.
- Trigger downstream business logic when an expected payment lands (e.g., release the deliverable, mark the invoice paid).
- Generate a receipt for the matched payment: the noteSecret is decrypted as part of the discovery and can be embedded directly.
How to use it
await session.discoverNotes();
const acct = await session.exportAccount();
const myRequest = acct.paymentRequests?.find((r) => r.status === "FULFILLED");
const matched = myRequest
? acct.notes.filter((n) => myRequest.fulfilledByCommitments.includes(n.commitment))
: [];
for (const note of matched) {
console.log(`Payment received: ${note.value} of ${note.tokenId} (tx ${note.txHash})`);
}
Request tags: payment-request transfers use the request's public tag as the Note event hint. Anyone can see that an encrypted payload exists, but only the matching request key can decrypt it. Discovery matches events to open requests and marks matched requests fulfilled.
Behind the scenes
session.discoverNotes(). Payment-request matching is folded into the standard discovery flow when requests are registered with the session./{chainId}/public/note-events stream (with RPC gap-fill) when aspUrl is set, otherwise paginated eth_getLogs against the poolevent.hint === request.tag, then X25519 ECDH(request.paymentPrivKey, event.ephemeralPubKey) yields the shared secret that decrypts the payload. Finally, check the decoded noteAddressHash is in the request's slot map