Skip to content

feat(x402): optional beforePayment hook to screen the recipient before paying - #1454

Open
hypeprinter007-stack wants to merge 1 commit into
coinbase:mainfrom
hypeprinter007-stack:x402-before-payment-hook
Open

feat(x402): optional beforePayment hook to screen the recipient before paying#1454
hypeprinter007-stack wants to merge 1 commit into
coinbase:mainfrom
hypeprinter007-stack:x402-before-payment-hook

Conversation

@hypeprinter007-stack

Copy link
Copy Markdown

What & why

Implements the neutral pre-payment hook proposed in #1402.

Today the x402 action provider gates where the agent can pay (registeredServices) and how much (maxPaymentUsdc), but nothing checks who the recipient is. A compromised or mistaken agent can pay a sanctioned / drainer / phishing address that passes both existing gates. This adds the missing seat: an optional recipient check at the pre-payment chokepoint.

The change

A new optional beforePayment config hook:

const config: X402Config = {
  registeredServices: ["https://api.example.com"],
  beforePayment: async ({ payTo, url, network, asset, amount, method }) => {
    if (payTo && (await isSanctioned(payTo))) return { abort: true, reason: "recipient flagged" };
  },
};
  • Runs in retry_http_request_with_x402 after amount + network validation and immediately before any signing/settlement.
  • Receives the payment context including the selected option's payTo.
  • Returning { abort: true, reason } refuses the payment — no signature, no settlement; the action returns a structured error.
  • Provider-neutral by design: no screening backend is imported. Any implementation plugs in (sanctions / reputation / allowlist). The README shows anchor-x402-safe-pay's allow/review/block verdict as one example.

This matches the design discussion on #1402 (three-state-friendly: the hook returns a decision + reason and is agnostic to how the verdict was reached).

Scope & compatibility

  • Backward compatible — no behavior change unless beforePayment is set.
  • Covers the recommended two-step flow (make_http_requestretry_http_request_with_x402), which exposes payTo before paying. The one-shot make_http_request_with_x402 auto-settles a 402 inside wrapFetchWithPayment and does not expose the recipient pre-settlement, so it is intentionally not gated (documented in the README). Gating it would need a larger refactor of that path.

Tests

Two cases added mirroring the existing retryWithX402 tests:

  • beforePayment returns { abort: true } → hook sees the payTo/context, wrapFetchWithPayment is never called, action returns the abort error.
  • beforePayment allows → payment proceeds normally.

Notes for reviewers

  • Files: schemas.ts (config field + X402BeforePaymentContext / X402BeforePaymentDecision types), x402ActionProvider.ts (resolve + call site), x402ActionProvider.test.ts (2 tests), README.md (docs + example).
  • I wasn't able to run the full TypeScript monorepo suite in my environment; the added tests mirror the existing retry tests exactly and the types are consistent by construction — please let CI validate.
  • Happy to add Python parity in the same PR (or a follow-up) if you'd like it here — kept this TS-first to get a read on the shape before duplicating.

Closes #1402 (or partially addresses, if you'd prefer to keep it open for the Python side).

…e paying

Implements the neutral pre-payment hook proposed in coinbase#1402. Today the x402 action
provider gates *where* the agent pays (registeredServices) and *how much*
(maxPaymentUsdc), but nothing checks *who* the recipient is — a compromised or
mistaken agent can pay a sanctioned/drainer address.

Adds an optional `beforePayment` config hook that runs in retry_http_request_with_x402
after amount + network validation and immediately before the payment is signed/
settled, receiving the payment context (incl. the selected option's `payTo`).
Returning { abort: true, reason } refuses the payment — no signature, no settlement.

Deliberately provider-neutral: no screening backend is imported. Any implementation
plugs in (sanctions / reputation / allowlist); the README shows anchor-x402-safe-pay's
allow/review/block verdict as one example.

Scope: the hook covers the recommended two-step flow (make_http_request ->
retry_http_request_with_x402), which exposes payTo before paying. The one-shot
make_http_request_with_x402 auto-settles a 402 inside wrapFetchWithPayment and does
not expose the recipient pre-settlement, so it is intentionally not gated (noted in
the README). Fully backward compatible — no behavior change unless beforePayment is set.

Tests: added two cases mirroring the existing retry tests — abort (no payment signed)
and allow (payment proceeds).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cb-heimdall

Copy link
Copy Markdown

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

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

Labels

action provider New action provider documentation Improvements or additions to documentation typescript

Development

Successfully merging this pull request may close these issues.

Proposal: a neutral pre-payment recipient hook in the x402 action provider (inspect payTo before paying)

2 participants