Skip to content

feat(paywall): show a receipt for the settled payment before loading the content - #82

Open
pedro-pelicioni wants to merge 4 commits into
stellar:mainfrom
Nearx-Labs:fix/74-payment-receipt
Open

feat(paywall): show a receipt for the settled payment before loading the content#82
pedro-pelicioni wants to merge 4 commits into
stellar:mainfrom
Nearx-Labs:fix/74-payment-receipt

Conversation

@pedro-pelicioni

Copy link
Copy Markdown

feat(paywall): show a receipt for the settled payment before loading the content

Branch: fix/74-payment-receipt
Screenshot: harness/shots/after-receipt.png

The gap

On success the paywall calls onSuccessfulResponse immediately, and the default
implementation in entry.tsx does:

document.documentElement.innerHTML = await response.text();

The whole document is replaced. The payer never sees what they were charged,
which account was paid, or the hash it settled under — the one piece of evidence
that the payment was real and checkable on-ledger.

examples/simple-paywall works around this by injecting a {{TX_LINK}}
placeholder into its own paid page (txHashInjector.ts), but that only helps
hosts serving HTML they control. Anyone serving JSON, a file, or a page they do
not template gets nothing.

The change

  • useStellarPayment decodes the PAYMENT-RESPONSE header on the settled
    response and times the settlement, then reports both through a new optional
    onReceipt.
  • onReceipt is awaited, so the caller can hold the hand-off. The paywall
    shows amount, recipient, transaction hash linked to Stellar Expert, and how
    long settlement took, then loads the content after receiptDelayMs (default
    3000) or as soon as Continue is pressed.
  • Hosts control this through stellarReceiptDelayMs on PaywallConfig.
    Setting it to 0 skips the receipt and restores exactly the previous
    hand-off-immediately behaviour.

Behaviour change

The 3000ms pause is the one behavioural change in this PR. I think a payer
should see proof of a payment they just authorised, but it is your call —
happy to ship it as 0 by default so the receipt is opt-in. Everything else
here is additive.

Details worth flagging

  • The explorer link falls back to the network named in the payment requirement
    when the facilitator reports one that is not CAIP-2, rather than dropping the
    link.
  • A hand-off left pending at unmount is resolved in a cleanup effect, so the
    paid content can never be stranded behind an unresolved promise.
  • Both success paths — first response and the 402-retry path — go through the
    same completePayment, each timed from its own submission.

Tests

12 new cases covering the explorer URL builder (testnet, the pubnetpublic
mapping, missing inputs, non-Stellar networks, and path-escaping a hostile
hash), hash truncation, and duration formatting.


Part of #74.

pedro-pelicioni and others added 4 commits August 11, 2026 21:19
`getExplorerTxUrl` maps the CAIP-2 `pubnet` reference onto Stellar
Expert's `public`, returns null for anything non-Stellar, and escapes the
hash into the path. `truncateHash` keeps both ends so a hash can be
compared against an explorer by eye.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
Decodes the `PAYMENT-RESPONSE` header on the settled response and times
the settlement, then hands both to an optional `onReceipt`.

It is awaited, so a caller can hold the hand-off — the paid response
usually replaces the whole document, and anything rendered after it is
gone before it can be read. Both success paths, the first response and
the 402-retry, go through the same helper, each timed from its own
submission.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
The payer had no way to see what they were charged, which account was
paid, or the hash it settled under — the one piece of evidence the
payment was real and checkable on-ledger. `examples/simple-paywall`
works around this by templating a tx link into its own paid page, which
only helps hosts serving HTML they control.

Show amount, recipient, hash linked to Stellar Expert, and settlement
time, then load the content after the configured delay or as soon as
Continue is pressed. The explorer link falls back to the network named in
the requirement when the facilitator reports one that is not CAIP-2,
rather than dropping the link. A hand-off left pending at unmount is
resolved, so the paid content cannot be stranded behind an unresolved
promise.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
`stellarReceiptDelayMs` on `PaywallConfig` reaches the browser as
`window.x402.config.receiptDelayMs`. Defaults to 3000; `0` skips the
receipt and restores the previous hand-off-immediately behaviour exactly.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
Copilot AI balanced review requested due to automatic review settings August 12, 2026 00:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants