You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
x402: add an onSettled hook so sites can record paid reads
#3641
I'd like to propose a small, optional onSettled callback on the @emdash-cms/x402 integration config. It would run once each time a payment settles successfully.
Problem
enforce() already verifies and settles the payment and hands back payer and settlement, but there's no hook at the moment of settlement. To record paid reads today you have to add code after every enforce() call on every paid page, and keep that consistent as routes change. That's easy to miss.
Things I'd want to build on top of it:
logging paid reads
analytics (which content earns, and from whom)
sending receipts
revenue dashboards
Proposed API
interfaceX402SettledEvent{payer?: string;settlement: SettleResponse;// from @x402/corerequirements: PaymentRequirements;// what the payment settled againstresource: {url: string;description?: string;mimeType?: string};request: Request;}// New optional config fields, each resolving to a function:
onSettled?: { module: string; export?: string};// (event: X402SettledEvent) => void | Promise<void>
onError?: { module: string; export?: string};// (error: unknown, event: X402SettledEvent) => void
Called only after a successful settle. Never for a 402, a failed verify, a failed settle, or a request skipped in botOnly mode.
Never blocks or breaks the response. It starts after enforce() returns, and throws or rejections go to onError (or console.warn if unset).
On the Cloudflare adapter the hook's promise goes to waitUntil, the same approach core's after() uses. Elsewhere it just runs in the background.
Why a module reference and not a function: the integration serializes its config into virtual:x402/config with JSON.stringify, so a function in astro.config.mjs would be silently dropped. A { module, export } reference becomes a static import at build time. It's also the same shape Proposal: pluggable x402 backends for a Kaspa TN10 adapter #3110 uses for pluggable backends, so the two would line up if that goes ahead.
Fully opt-in. Sites that don't set it see no change.
How it fits with existing threads
Exploring Native Paywall Support in EmDash Core #1467 (native paywall support): @ascorbic said the proposal "does make sense for it to be in core, and to expose plugin hooks", and asked whether x402 should be combined with paywalls. A settle hook is a small, generic primitive that's useful either way.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Summary
I'd like to propose a small, optional
onSettledcallback on the@emdash-cms/x402integration config. It would run once each time a payment settles successfully.Problem
enforce()already verifies and settles the payment and hands backpayerandsettlement, but there's no hook at the moment of settlement. To record paid reads today you have to add code after everyenforce()call on every paid page, and keep that consistent as routes change. That's easy to miss.Things I'd want to build on top of it:
Proposed API
Usage:
Behavior
botOnlymode.enforce()returns, and throws or rejections go toonError(orconsole.warnif unset).waitUntil, the same approach core'safter()uses. Elsewhere it just runs in the background.virtual:x402/configwithJSON.stringify, so a function inastro.config.mjswould be silently dropped. A{ module, export }reference becomes a static import at build time. It's also the same shape Proposal: pluggable x402 backends for a Kaspa TN10 adapter #3110 uses for pluggable backends, so the two would line up if that goes ahead.How it fits with existing threads
Approved for PR): once agents can discover and pay for content, sites will want to see those payments. This is the reporting side of the same flow, and like the discovery work it's additive topackages/x402.SettleResponse, so it stays backend-agnostic.Questions
{ module, export }OK for passing functions into the integration, or is there a pattern you'd prefer?I have a working prototype with tests and can open a PR if this gets the go-ahead.
All reactions