Skip to content

fix: move payment settlement from requirePayment() to middleware#164

Merged
badjer merged 2 commits intomainfrom
fix/settle-in-middleware
Apr 15, 2026
Merged

fix: move payment settlement from requirePayment() to middleware#164
badjer merged 2 commits intomainfrom
fix/settle-in-middleware

Conversation

@badjer
Copy link
Copy Markdown
Contributor

@badjer badjer commented Apr 15, 2026

Summary

  • Moves payment credential settlement from requirePayment() to the atxpExpress middleware
  • Settlement now happens before route code runs, so the auth server ledger is credited before any charge() calls
  • Removes settleDetectedCredential() from requirePayment.ts entirely — it just charges now
  • For X402: uses payload.accepted from the credential directly as paymentRequirements, no regeneration needed
  • For MPP/ATXP: credentials are self-contained, no extra context needed

Problem

Tool handlers that call requirePayment() multiple times (e.g., image server's pre-flight balance check + post-generation charge) would have the first call consume the credential via settlement, leaving nothing for the second charge. The middleware approach settles once before any route code runs.

Supersedes

Test plan

  • 182 server tests pass
  • Lint passes (0 errors)
  • 4/4 MPP runs pass end-to-end (local image MCP server)
  • 4/4 X402 runs pass end-to-end (local image MCP server)
  • Publish + deploy to image/music servers

🤖 Generated with Claude Code

Settlement now happens in the atxpExpress middleware before route code
runs. This eliminates footguns where tool handlers call requirePayment()
multiple times (e.g., pre-flight balance check + post-generation charge)
and the first call consumes the credential, leaving nothing for the
second.

For X402, the credential's parsed `accepted` field is passed directly
as paymentRequirements — no need to regenerate from server config.
For MPP and ATXP, credentials are already self-contained.

The middleware doesn't need the amount from requirePayment() because
the settle endpoints extract amounts from the credential itself.

Tested locally: 4/4 MPP runs + 4/4 X402 runs pass end-to-end against
the image MCP server with its double-requirePayment() pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests now verify credentials are settled in middleware (not stored for
requirePayment). getDetectedCredential() returns null in route handlers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@badjer badjer merged commit d6827b9 into main Apr 15, 2026
1 check passed
@badjer badjer deleted the fix/settle-in-middleware branch April 15, 2026 02:21
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.

1 participant