feat: PaymentClient + buildPaymentHeaders (Phase 2)#144
Merged
Conversation
Add PaymentClient class with authorize() method that centralizes the
/authorize/{protocol} call pattern. Resolves protocol via protocolFlag,
builds connection token auth headers, constructs protocol-specific
request bodies, returns opaque credential.
Add buildPaymentHeaders() that takes an AuthorizeResult and builds
protocol-specific headers (X-PAYMENT for x402, Authorization: Payment
for mpp). Extracted from protocol handlers so it can be used by
other callers (e.g. LLM service).
Refactor X402ProtocolHandler and MPPProtocolHandler to use PaymentClient
and buildPaymentHeaders instead of inline authorize logic. Removes
duplicated auth header building and fetch call patterns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add authorize(params: AuthorizeParams) as a required method on the
Account interface. Each account implementation handles authorization
according to its capabilities:
- ATXPAccount: calls /authorize/{protocol} on accounts service via HTTP.
For ATXP, injects connection token client-side into credential.
For X402, returns paymentHeader. For MPP, returns credential.
- BaseAccount: X402 signs Permit2 locally via x402/client. ATXP executes
payment via makePayment() and returns tx evidence as credential.
- SolanaAccount, PolygonServerAccount, PolygonBrowserAccount,
BaseAppAccount, WorldchainAccount: ATXP executes payment and returns
evidence. Other protocols throw unsupported.
- TempoAccount: MPP executes payment and returns evidence. Other
protocols throw unsupported.
PaymentClient simplified to a thin protocol resolver that delegates
to account.authorize(). All HTTP/signing logic now lives in the account
implementations where it belongs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
account.authorize() now handles signing per account type — ATXPAccount calls the accounts service, BaseAccount signs locally. The handler no longer needs its own fallback path, getLocalSigner(), or ensureCurrencyIfNeeded(). This prevents ATXPAccount from incorrectly attempting local signing (it has no local key). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add AuthorizationError class to @atxp/common with statusCode and errorCode fields. ATXPAccount.authorize() now throws AuthorizationError with the error code from the accounts service response (e.g. INSUFFICIENT_BALANCE, SPEND_LIMIT_EXCEEDED). MPP handler uses instanceof AuthorizationError instead of fragile string matching on error messages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ationAccountId, credential parsing - PaymentClient: mark accountsServer and fetchFn as deprecated (no longer used since authorization delegates to account.authorize()) - ProtocolSettlement: accept destinationAccountId in constructor for ATXP settle (the server/LLM's own account ID) - ProtocolSettlement: parse self-contained ATXP credential JSON to extract sourceAccountId, sourceAccountToken, and options instead of requiring external SettlementContext - Export AuthorizationError from @atxp/common index Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… in base - serverTestHelpers mock Account was missing the new authorize method - BaseAccount.authorize() x402 path needs explicit null check and cast for paymentRequirements parameter - Add x402-client.d.ts type shim to atxp-base (same as atxp-client) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
PaymentClientclass andbuildPaymentHeaders()utility for programmatic payment authorization via the meta-API. Refactors protocol handlers to use them.New exports from
@atxp/client:PaymentClient— calls/authorize/{protocol}on accounts, resolves protocol viaprotocolFlag, returns opaque credentialbuildPaymentHeaders(result, originalHeaders?)— builds protocol-specific headers from credential (X-PAYMENT for x402, Authorization: Payment for mpp)AuthorizeResult—{ protocol, credential }interfaceHandler refactor:
X402ProtocolHandlerandMPPProtocolHandlernow usePaymentClient.authorize()instead of inline fetch callsbuildPaymentHeaders()instead of privatebuildRetryHeaders()Why: Phase 2 (LLM settlement over protocol rails) needs LLM to call authorize + settle separately. LLM uses
PaymentClientfor the authorize step andProtocolSettlementfrom@atxp/serverfor the settle step. The samePaymentClientis also used internally by the fetcher's protocol handlers.Test plan
🤖 Generated with Claude Code