Documentation
¶
Overview ¶
Package server is the merchant/server side of chit — the half that charges callers over ATXP. It is a clean-room Go port of the MIT-licensed @atxp/server TypeScript SDK (© Circuit and Chisel); chit is unofficial and not affiliated with or endorsed by them.
Like the client, the merchant side does no on-chain crypto: settlement is delegated to the ATXP authorization server over HTTP. The only local crypto is the HMAC that signs the opaque identity carried through an MPP retry.
The flow a merchant wires up:
- CheckToken authenticates the caller's OAuth bearer token (RFC 7662 introspection) and yields its subject.
- RequirePayment gates a metered operation: it attempts an on-demand charge and, failing that, returns a Challenge to emit as an MCP/JSON-RPC payment error. A nil Challenge with a nil error means the caller has paid.
- On a push-payment retry, Verify/Settle finalize the presented credential, and RecoverOpaqueIdentity re-derives the caller identity that the bearer token can no longer carry.
Connection tokens and bearer tokens are wallet-grade secrets: chit never logs them, and the default Logger discards everything.
Index ¶
- Variables
- func ExtractX402PayerAddress(credential string) (string, error)
- type ATXPPaymentServer
- type Amount
- type AtxpMcpChallengeData
- type BalanceRequest
- type Challenge
- type ChargeRequest
- type Config
- type CredentialDetection
- type Destination
- type InvalidAccountIDError
- type Logger
- type Merchant
- func (m *Merchant) CheckRequest(r *http.Request) TokenCheck
- func (m *Merchant) CheckToken(ctx context.Context, resourceURL *url.URL, authHeader string) TokenCheck
- func (m *Merchant) CloseSession(ctx context.Context, session *PaymentSession) error
- func (m *Merchant) OpenPaymentSession(detected CredentialDetection, sctx SettlementContext) *PaymentSession
- func (m *Merchant) ProtectedResourceMetadata(resource string) ProtectedResourceMetadata
- func (m *Merchant) RecoverOpaqueIdentity(opaque map[string]any, challengeID string) (string, bool)
- func (m *Merchant) RequirePayment(ctx context.Context, pr PaymentRequest) (*Challenge, error)
- func (m *Merchant) Settlement(ctx context.Context) (*ProtocolSettlement, error)
- type MppChallengeData
- type MppSessionSupport
- type OAuthChallengeResponse
- type PaymentRequest
- type PaymentServer
- type PaymentSession
- type ProtectedResourceMetadata
- type Protocol
- type ProtocolSettlement
- type SettleResult
- type SettlementContext
- type SolanaMppSessionSupport
- type Source
- type StaticDestination
- type StdLogger
- type TokenCheck
- type TokenData
- type TokenProblem
- type UnderpaymentError
- type VerifyResult
- type X402PaymentOption
- type X402PaymentRequirements
Constants ¶
This section is empty.
Variables ¶
var CAIP2Networks = map[string]string{
"base": "eip155:8453",
"base_sepolia": "eip155:84532",
"solana": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
"solana_devnet": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
}
CAIP2Networks maps a human-readable network name to its CAIP-2 identifier, for the chains the CDP facilitator supports.
Ported verbatim from @atxp/common constants.ts (CAIP2_NETWORKS). Source: https://docs.cdp.coinbase.com/x402/network-support
var USDCAddresses = map[string]string{
"base": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"base_sepolia": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"eip155:8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"eip155:84532": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"solana": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"solana_devnet": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
}
USDCAddresses maps a network identifier (human-readable name and CAIP-2 form) to the USDC contract / mint address on that network.
Ported verbatim from @atxp/common constants.ts (USDC_ADDRESSES). Source: https://developers.circle.com/stablecoins/usdc-on-main-networks
Functions ¶
func ExtractX402PayerAddress ¶
ExtractX402PayerAddress returns the actual signing address from an x402 credential's authorization.from field. Unlike a PaymentRequest's User field (sourceAccountId), this address is cryptographically tied to the EIP-3009 signature and cannot be forged, so it is the right thing to rate-limit, cap spend on, or blocklist against on the merchant's own side.
See docs/PROTOCOL.md's fraud-block bypass note: ATXP's account-standing checks (fraud_blocked, etc.) are not enforced on sourceAccountId for the x402 settlement path, so merchants that need their own abuse protection should gate on this address, not on anything from PaymentRequest.User.
Returns an error if the credential is not a parseable x402 credential or carries no authorization.from field (e.g. it is not the "exact" scheme).
Types ¶
type ATXPPaymentServer ¶
type ATXPPaymentServer struct {
// contains filtered or unexported fields
}
ATXPPaymentServer talks to one authorization server. Ported from paymentServer.ts ATXPPaymentServer.
func (*ATXPPaymentServer) Charge ¶
func (s *ATXPPaymentServer) Charge(ctx context.Context, req ChargeRequest) (bool, error)
func (*ATXPPaymentServer) CreatePaymentRequest ¶
func (s *ATXPPaymentServer) CreatePaymentRequest(ctx context.Context, req ChargeRequest) (string, error)
func (*ATXPPaymentServer) GetBalance ¶
func (s *ATXPPaymentServer) GetBalance(ctx context.Context, req BalanceRequest) (Amount, error)
type Amount ¶
type Amount struct {
// contains filtered or unexported fields
}
Amount is a non-negative USDC money value held as an exact integer count of micro-units (6 decimals). Money is never represented as a float: every amount that crosses the wire — the chargeAmount string, the x402 integer amount, the MPP amounts — is derived from this exact representation.
The TS SDK uses BigNumber for the same role; Amount is the Go equivalent constrained to the one currency chit settles (USDC/pathUSD, both 6-decimal).
func AmountFromMicroString ¶
AmountFromMicroString parses an atomic micro-unit integer string (e.g. the x402/Solana-MPP on-chain amount, "10000") into an Amount. Unlike ParseAmount (which takes a human-readable decimal), this expects no decimal point.
func AmountFromMicros ¶
AmountFromMicros builds an Amount directly from a micro-unit count. A negative count is clamped to zero rather than producing an invalid (negative) Amount.
func ParseAmount ¶
ParseAmount parses a decimal USDC string (e.g. "0.01", "1", "0.000001") into an exact Amount. It rejects negatives, blanks, non-numeric input, and any value with more than 6 decimal places (which cannot be represented on-chain).
Rejecting rather than rounding is deliberate: silently truncating a sub-micro fraction would let a caller request a price the merchant cannot actually charge.
func (Amount) GreaterThan ¶
GreaterThan reports whether a > b.
func (Amount) IsPositive ¶
IsPositive reports whether the amount is strictly greater than zero.
func (Amount) MicroString ¶
MicroString renders the amount as its integer micro-unit count, e.g. 0.01 USDC -> "10000". This is the x402 / Solana-MPP on-chain amount (BigNumber.times(1e6).toFixed(0) in the TS SDK).
type AtxpMcpChallengeData ¶
type AtxpMcpChallengeData struct {
PaymentRequestID string `json:"paymentRequestId"`
PaymentRequestURL string `json:"paymentRequestUrl"`
ChargeAmount string `json:"chargeAmount,omitempty"`
}
AtxpMcpChallengeData is the ATXP-native portion of a challenge. Ported from protocol.ts AtxpMcpChallengeData.
type BalanceRequest ¶
type BalanceRequest struct {
SourceAccountID string `json:"sourceAccountId"`
DestinationAccountID string `json:"destinationAccountId"`
SourceAccountToken string `json:"sourceAccountToken,omitempty"`
}
BalanceRequest is the body for POST /balance. Ported from @atxp/server.
type Challenge ¶
type Challenge struct {
Code int
Message string
Data map[string]any
// Structured views of the same data, for callers that want typed access
// (e.g. to emit an HTTP 402 instead of an MCP error).
AtxpMcp AtxpMcpChallengeData
X402 X402PaymentRequirements
MPP []MppChallengeData
}
Challenge is a built omni-challenge ready to be returned to a caller as a JSON-RPC / MCP error. Code and Message are the MCP error fields; Data is the error.data object carrying all three protocols' challenge data.
type ChargeRequest ¶
type ChargeRequest struct {
Options []chargeOptionWire `json:"options"`
SourceAccountID string `json:"sourceAccountId"`
DestinationAccountID string `json:"destinationAccountId"`
PayeeName string `json:"payeeName"`
// SourceAccountToken is the caller's OAuth/connection token for on-demand
// (pull-mode) charging. Wallet-grade; never logged.
SourceAccountToken string `json:"sourceAccountToken,omitempty"`
// PaymentRequestID ties /settle and the follow-up /charge to one payment.
PaymentRequestID string `json:"paymentRequestId,omitempty"`
}
ChargeRequest is the body for POST /charge and POST /payment-request. Ported from the @atxp/server Charge type. When talking to the authorization server the merchant does not send resource/resourceName — the AS already knows them and must not trust the merchant to self-report them.
type Config ¶
type Config struct {
// Destination is where payments are received. Required.
Destination Destination
// ConnectionToken is the merchant's own ATXP connection token. It is sent
// (over HTTPS only) as the X-ATXP-TOKEN header during dynamic client
// registration so the registered client is bound to the merchant's ATXP
// account. Wallet-grade secret — never logged or echoed. Optional only if
// the authorization server permits unauthenticated registration.
ConnectionToken string
// AuthServer is the ATXP authorization server base URL. Defaults to
// https://auth.atxp.ai.
AuthServer string
// PayeeName labels the merchant in challenges and metadata, and doubles as
// the dynamic-client-registration client_name sent to the authorization
// server. Defaults to "An ATXP Server"; set this to something distinctive
// in production. The auth server treats client_name as claimed once
// registered; with the default Store (in-memory, lost on restart) a second
// process registering under the same unset-default name, whether a
// restart of this merchant or an unrelated one reusing the same
// ConnectionToken, gets a permanent 409 for that name, with no way to
// recover the original client_secret. See Store's doc comment.
PayeeName string
// Currency is the settlement currency. Defaults to "USDC".
Currency string
// MinimumPayment is a price floor applied to every charge. Must be <= $1.00.
// The zero Amount means no floor.
MinimumPayment Amount
// AppName is an observability label (1–64 chars of [a-zA-Z0-9._-]) sent on
// settle/verify calls. Untrusted by auth; do not use for billing.
AppName string
// ExpectedAudience, when set, is required to appear in an introspected
// token's `aud`. Leaving it empty disables the audience check (the TS
// default), but setting it to this resource's URL is strongly recommended.
ExpectedAudience string
// Store persists DCR client credentials (keyed by AS issuer). Defaults to a
// process-local in-memory store, which loses its client_secret on every
// restart. The authorization server does not let a new registration
// reclaim an already-claimed client_name, so a restarted (or
// ConnectionToken-sharing) merchant that doesn't set a distinctive
// PayeeName will permanently fail dynamic client registration with a 409.
// Production deployments should supply a persistent Store implementation.
Store atxp.Store
// HTTPClient is used for all authorization-server calls. Optional.
HTTPClient *http.Client
// Logger receives operational messages. Never receives secrets. Defaults to
// a no-op logger.
Logger Logger
// OpaqueKey is the HMAC key for opaque-identity signing. When nil, the key
// is loaded from ATXP_OPAQUE_KEY (base64) or randomly generated per process.
OpaqueKey []byte
// AllowHTTP permits plaintext HTTP to the authorization server. For local
// development and tests ONLY — it disables the HTTPS requirement that keeps
// the connection token from traversing the network in the clear.
AllowHTTP bool
}
Config configures a Merchant. Destination is the only required field.
type CredentialDetection ¶
CredentialDetection is the result of sniffing a retry request's headers for a payment credential. Ported from protocol.ts CredentialDetection.
func DetectProtocol ¶
func DetectProtocol(h interface{ Get(string) string }) *CredentialDetection
DetectProtocol inspects inbound request headers on a retry and reports which payment rail (if any) the caller used. Ported from protocol.ts detectProtocol.
Header precedence is preserved exactly: X-ATXP-PAYMENT (atxp) > PAYMENT-SIGNATURE/X-PAYMENT (x402) > Authorization: Payment (mpp).
h.Get is case-insensitive (net/http canonicalizes header keys), matching the lowercased keys the TS version reads.
type Destination ¶
type Destination interface {
AccountID(ctx context.Context) (string, error)
Sources(ctx context.Context, include []string) ([]Source, error)
}
Destination is where the merchant receives payment. It maps to the @atxp/common PaymentDestination interface (only the two methods the merchant side needs).
AccountID returns a fully-qualified "network:address" id (e.g. "atxp:<uuid>" for a hosted account, or "base:0x..." for a chain account). Sources returns the per-chain receive addresses used to build x402 and MPP challenge options; returning an empty slice is fine — the challenge then advertises only the ATXP-native rail, which is always included.
type InvalidAccountIDError ¶
type InvalidAccountIDError struct{ AccountID string }
InvalidAccountIDError is returned when an account id is not "network:address".
func (*InvalidAccountIDError) Error ¶
func (e *InvalidAccountIDError) Error() string
type Logger ¶
type Logger interface {
Debugf(format string, args ...any)
Infof(format string, args ...any)
Warnf(format string, args ...any)
Errorf(format string, args ...any)
}
Logger is the minimal logging surface the merchant side uses. It mirrors the TS SDK's Logger (debug/info/warn/error).
Security note: chit never passes a connection string, bearer token, opaque HMAC key, or payment credential to any Logger method. Implementations may send log output anywhere; the contract is that nothing handed to a Logger is a secret, so a custom Logger cannot accidentally exfiltrate one.
type Merchant ¶
type Merchant struct {
// contains filtered or unexported fields
}
Merchant charges callers over ATXP. Construct one with New and reuse it; it is safe for concurrent use.
func (*Merchant) CheckRequest ¶
func (m *Merchant) CheckRequest(r *http.Request) TokenCheck
CheckRequest is CheckToken applied to an *http.Request, deriving the resource URL from the request.
func (*Merchant) CheckToken ¶
func (m *Merchant) CheckToken(ctx context.Context, resourceURL *url.URL, authHeader string) TokenCheck
CheckToken authenticates a caller's Authorization header against the authorization server. resourceURL is this resource's URL (used to build the WWW-Authenticate metadata pointer and, when ExpectedAudience is set, to bound the audience). A passing TokenCheck carries the caller's identity in Data.Sub.
func (*Merchant) CloseSession ¶
func (m *Merchant) CloseSession(ctx context.Context, session *PaymentSession) error
CloseSession settles a payment session at most once, for the amount actually charged (Spent), and is safe to call more than once (e.g. from a deferred call on every return path) — later calls are no-ops once settled.
Two protocol-shape-dependent rules apply, mirroring settlePaymentSession:
- An MPP session (channel) credential settles even at Spent()==0, since that close refunds the deposit locked at authorize. One-shot ATXP/x402 credentials have nothing to settle at zero spend and no-op instead.
- On settle failure, a channel credential is left unsettled so a later call can re-drive the on-chain close (idempotent); one-shot credentials are marked settled regardless (nothing to re-drive; reconcile from the returned error).
func (*Merchant) OpenPaymentSession ¶
func (m *Merchant) OpenPaymentSession(detected CredentialDetection, sctx SettlementContext) *PaymentSession
OpenPaymentSession opens a payment session for a detected retry credential. Deriving the cap is local (no network call) — it parses the credential (and, for x402, resolves the matching accept from sctx.PaymentRequirements) to find the authorized amount. Ported from atxpContext.ts openPaymentSession / paymentSession.ts buildPaymentSession.
func (*Merchant) ProtectedResourceMetadata ¶
func (m *Merchant) ProtectedResourceMetadata(resource string) ProtectedResourceMetadata
ProtectedResourceMetadata builds the RFC 9728 document for a resource URL.
func (*Merchant) RecoverOpaqueIdentity ¶
RecoverOpaqueIdentity verifies the opaque identity echoed back in an MPP retry and returns the caller's subject. The caller extracts the opaque object from the presented credential and supplies the challenge id it was bound to. Returns ("", false) on any mismatch — fail closed.
func (*Merchant) RequirePayment ¶
RequirePayment gates a metered operation. It returns:
- (nil, nil) the charge settled; the caller may proceed.
- (*Challenge, nil) payment is required; emit the Challenge as an MCP/JSON-RPC error (or HTTP 402) and do not proceed.
- (nil, error) an infrastructure error; do not proceed.
func (*Merchant) Settlement ¶
func (m *Merchant) Settlement(ctx context.Context) (*ProtocolSettlement, error)
Settlement returns a ProtocolSettlement bound to the merchant's destination account, for finalizing push-payment credentials on a retry.
type MppChallengeData ¶
type MppChallengeData struct {
ID string `json:"id"`
Method string `json:"method"`
Intent string `json:"intent"`
Amount string `json:"amount"`
Currency string `json:"currency"`
Network string `json:"network"`
Recipient string `json:"recipient"`
Expires string `json:"expires,omitempty"`
Resource *resourceRef `json:"resource,omitempty"`
Request map[string]any `json:"request,omitempty"`
Opaque map[string]any `json:"opaque,omitempty"`
}
MppChallengeData is one MPP challenge (one supported chain). JSON tags match the wire shape in protocol.ts so the emitted `data.mpp[]` is identical to the reference SDK.
The `amount` encoding is chain-dependent (see omnichallenge.go):
- method "solana": micro-units integer string, e.g. "10000"
- method "tempo": human-readable decimal string, e.g. "0.01"
type MppSessionSupport ¶
type MppSessionSupport struct {
EscrowContract string `json:"escrowContract"`
AuthorizedSigner string `json:"authorizedSigner"`
Operator string `json:"operator"`
ChainID int `json:"chainId"`
}
MppSessionSupport is the Tempo MPP session (TIP-1034) params advertised by the authorization server's GET /mpp/supported: the channel's authorized signer + operator (auth's settler key), the escrow precompile, and the chain id. Ported from omniChallenge.ts MppSessionSupport.
type OAuthChallengeResponse ¶
OAuthChallengeResponse is the HTTP response a resource server returns when a token check fails. Ported from core/oauth.ts createOAuthChallengeResponseCore.
func ChallengeResponse ¶
func ChallengeResponse(tc TokenCheck) *OAuthChallengeResponse
ChallengeResponse maps a failed TokenCheck to the RFC 6750 HTTP challenge a resource server should return. Returns nil when the check passed.
type PaymentRequest ¶
type PaymentRequest struct {
// Price is what to charge for this call. The amount actually charged is
// max(Price, the merchant's MinimumPayment).
Price Amount
// User is the caller's account id — the `sub` from a passing CheckToken.
// Required; without an authenticated caller there is nobody to charge.
User string
// SourceAccountToken is the caller's connection/OAuth token for on-demand
// (pull-mode) charging. Wallet-grade secret. Optional: without it, the
// caller is sent a challenge to pay out-of-band.
SourceAccountToken string
// PaymentRequestID ties this charge to an existing payment lifecycle
// (idempotency). Optional.
PaymentRequestID string
// Session, when set, charges locally against the request-scoped payment
// session (opened from a detected retry credential via
// Merchant.OpenPaymentSession) instead of issuing a network /charge call.
// Multiple RequirePayment calls sharing a Session settle once, for the
// sum actually charged, when the caller closes it via
// Merchant.CloseSession. Optional — nil preserves the on-demand /charge
// behavior.
Session *PaymentSession
// Resource is the resource URL recorded in the challenge for activity labels.
// Optional.
Resource string
// ExistingPaymentID, when set, is consulted before creating a new payment
// request so a re-challenge reuses an in-flight payment. Optional.
ExistingPaymentID func(ctx context.Context) (string, error)
}
PaymentRequest describes one metered call to gate.
type PaymentServer ¶
type PaymentServer interface {
// Charge attempts an on-demand pull. Returns true when the charge settled
// (HTTP 200 or 202), false when payment is still required (HTTP 402), and a
// non-nil error for every other outcome — including network failures — so a
// caller that treats (false, nil) as "unpaid" never mistakes an
// infrastructure error for a definite "unpaid".
Charge(ctx context.Context, req ChargeRequest) (bool, error)
// CreatePaymentRequest registers a payment request and returns its id.
CreatePaymentRequest(ctx context.Context, req ChargeRequest) (string, error)
// GetBalance returns the caller's available USDC balance.
GetBalance(ctx context.Context, req BalanceRequest) (Amount, error)
}
PaymentServer is the merchant's HTTP client to the ATXP authorization server's money endpoints. Ported from the @atxp/server PaymentServer interface.
All settlement is delegated to the AS over HTTP; chit performs no on-chain crypto here. Charge reports whether the pull-mode charge settled.
type PaymentSession ¶
type PaymentSession struct {
// contains filtered or unexported fields
}
PaymentSession accumulates local charges against one detected payment credential across multiple RequirePayment calls, so N calls within one request settle once (for the sum actually charged) instead of issuing N network round trips. Ported from paymentSession.ts PaymentSessionState.
Unlike the TS reference (whose Express middleware opens/closes a session implicitly per request via AsyncLocalStorage), chit has no framework layer: the caller opens a session from a detected retry credential (Merchant.OpenPaymentSession), threads it into each PaymentRequest.Session, and closes it exactly once — typically via defer — when its request scope ends (Merchant.CloseSession).
func (*PaymentSession) Cap ¶
func (s *PaymentSession) Cap() (cap Amount, unlimited bool)
Cap returns the session's authorized ceiling and whether it is unlimited (the credential's amount could not be derived).
func (*PaymentSession) Charge ¶
func (s *PaymentSession) Charge(cost Amount) bool
Charge records a charge of cost against the session. It returns false (and leaves the accumulated total unchanged) if this charge would exceed the credential's authorized cap — the caller should then fall through to building a new payment challenge, exactly as an on-demand charge decline would. Ported from PaymentSessionState.charge.
func (*PaymentSession) SettleResult ¶ added in v0.1.2
func (s *PaymentSession) SettleResult() (SettleResult, bool)
SettleResult returns the result of the settle call CloseSession made, and whether one has happened yet (false before Close, or if Close no-op'd on a zero-spend one-shot credential). Check this after CloseSession succeeds before trusting the session as paid for its full intended amount: for the x402 "exact" scheme in particular, chit cannot verify that a credential's self-reported accepted.amount matches what the payer actually signed in authorization.value. The facilitator only ever settles the real signed value; SettledAmount is the actual amount that moved on-chain. Compare it against your own expected price before crediting anything.
func (*PaymentSession) Spent ¶
func (s *PaymentSession) Spent() Amount
Spent returns the sum of charges recorded so far.
type ProtectedResourceMetadata ¶
type ProtectedResourceMetadata struct {
Resource string `json:"resource"`
ResourceName string `json:"resource_name"`
AuthorizationServers []string `json:"authorization_servers"`
BearerMethodsSupported []string `json:"bearer_methods_supported"`
ScopesSupported []string `json:"scopes_supported"`
}
ProtectedResourceMetadata is the RFC 9728 document a resource server serves at /.well-known/oauth-protected-resource. Ported from protectedResourceMetadata.ts.
type Protocol ¶
type Protocol string
Protocol identifies one of the three inbound payment rails. Ported from @atxp/common PaymentProtocolEnum.
type ProtocolSettlement ¶
type ProtocolSettlement struct {
// contains filtered or unexported fields
}
ProtocolSettlement calls the AS verify/settle endpoints. Ported from protocol.ts ProtocolSettlement.
func (*ProtocolSettlement) Settle ¶
func (p *ProtocolSettlement) Settle(ctx context.Context, protocol Protocol, credential string, sctx *SettlementContext, actualAmount *Amount) (SettleResult, error)
Settle finalizes a payment at request end. A non-2xx response is an error (the payment did not settle). actualAmount, when non-nil, is the metered "up-to" amount actually spent (e.g. the sum of a PaymentSession's charges): for x402 (upto scheme only) and MPP session credentials, this settles that actual amount (≤ the authorized cap) instead of the cap. nil settles the cap, as before. Ported from ProtocolSettlement.settle.
func (*ProtocolSettlement) Verify ¶
func (p *ProtocolSettlement) Verify(ctx context.Context, protocol Protocol, credential string, sctx *SettlementContext) (VerifyResult, error)
Verify checks a payment credential at request start. Returns {valid:false} on any non-2xx response — fail closed. Ported from ProtocolSettlement.verify.
type SettleResult ¶
type SettleResult struct {
TxHash *string `json:"txHash"`
SettledAmount string `json:"settledAmount"`
AlreadySettled bool `json:"alreadySettled,omitempty"`
}
SettleResult is the /settle response. TxHash is nil when the payment was already settled by a prior call. Ported from protocol.ts SettleResult.
type SettlementContext ¶
type SettlementContext struct {
PaymentRequirements *X402PaymentRequirements
PaymentRequestID string
SourceAccountID string
DestinationAccountID string
Options any
}
SettlementContext carries the data verify/settle need to build protocol bodies. Ported from protocol.ts SettlementContext.
type SolanaMppSessionSupport ¶
type SolanaMppSessionSupport struct {
AuthorizedSigner string `json:"authorizedSigner"`
}
SolanaMppSessionSupport is the Solana MPP session-channel params advertised by GET /mpp/supported. accounts opens the channel and uses its own operator/fee-payer, so the SDK only needs auth's Solana authorizedSigner. Ported from omniChallenge.ts SolanaMppSessionSupport.
type Source ¶
Source is a destination chain address the merchant can receive USDC at. Ported from the @atxp/common Source shape (only the fields the challenge builder uses).
type StaticDestination ¶
type StaticDestination struct {
ID string // "network:address"
Addresses []Source // per-chain receive addresses (may be empty)
}
StaticDestination is a Destination with a fixed account id and address set. Suitable for a merchant that knows its receive addresses up front.
type StdLogger ¶
type StdLogger struct {
// contains filtered or unexported fields
}
StdLogger writes warn- and error-level lines to the standard library logger (stderr by default) and drops debug/info. It is a convenience for merchants that want operational visibility without a logging dependency.
func NewStdLogger ¶
func NewStdLogger() *StdLogger
NewStdLogger returns a StdLogger writing to stderr.
type TokenCheck ¶
type TokenCheck struct {
Passes bool
Problem TokenProblem
Token string
Data *TokenData
ResourceMetadataURL string
}
TokenCheck is the result of CheckToken. Passes reports whether the caller is authenticated; on failure Problem says why and ResourceMetadataURL is the value for the WWW-Authenticate challenge.
type TokenData ¶
type TokenData struct {
Active bool `json:"active"`
Scope string `json:"scope,omitempty"`
Sub string `json:"sub,omitempty"`
Aud stringOrSlice `json:"aud,omitempty"`
Exp int64 `json:"exp,omitempty"`
}
TokenData is the RFC 7662 introspection result. Ported from @atxp/common TokenData. `Exp` is a Unix timestamp in SECONDS when the AS provides it.
type TokenProblem ¶
type TokenProblem string
TokenProblem categorizes why a token check failed. Ported from @atxp/server TokenProblem.
const ( ProblemNoToken TokenProblem = "NO-TOKEN" ProblemNonBearer TokenProblem = "NON-BEARER-AUTH-HEADER" ProblemInvalidToken TokenProblem = "INVALID-TOKEN" ProblemInvalidAud TokenProblem = "INVALID-AUDIENCE" ProblemNSF TokenProblem = "NON-SUFFICIENT-FUNDS" ProblemIntrospectErr TokenProblem = "INTROSPECT-ERROR" )
type UnderpaymentError ¶ added in v0.1.2
type UnderpaymentError struct {
Spent Amount
Settled Amount
// ParseError is set instead of a meaningful Settled when the settle
// response's amount could not be parsed at all. Treated as a failure
// too, since an amount that can't be verified isn't a verified amount.
ParseError error
}
UnderpaymentError is returned by CloseSession when a settle call succeeded but for less than the amount actually charged locally (Spent). Real money moved, so there is nothing to retry, but the caller must not treat this as a completed payment: do not serve the resource or credit anything for it.
This is deliberately not a network/infrastructure failure: it means the settle response itself came back clean, just short. See CloseSession's doc comment for why this check exists (the x402 "exact" scheme in particular can settle for less than a credential claims elsewhere in the same payload).
Confirmed live against production (2026-08-07, Base mainnet): for x402 "exact", auth.atxp.ai's own /settle/x402 already rejects a credential whose accepted.amount doesn't match its actual signed authorization.value (HTTP 400, nothing settles), so this error path did not fire for that specific attack shape; the AS closed it one layer down. This check is still real defense-in-depth (a merchant-side pricing bug, or any future change in the AS's behavior, would still need it), but for x402 exact its load-bearing-ness is unconfirmed rather than demonstrated. Whether the AS enforces the same consistency for the ATXP-native protocol or MPP is untested. ATXP-native's trust model differs (the AS computes the charge itself against its own ledger rather than verifying a third-party signature), so the same attack shape may not even apply there; MPP is simply unverified. Don't assume either has the same backend guarantee x402 exact was shown to have.
func (*UnderpaymentError) Error ¶ added in v0.1.2
func (e *UnderpaymentError) Error() string
func (*UnderpaymentError) Unwrap ¶ added in v0.1.2
func (e *UnderpaymentError) Unwrap() error
type VerifyResult ¶
type VerifyResult struct {
Valid bool `json:"valid"`
}
VerifyResult is the /verify response. Ported from protocol.ts VerifyResult.
type X402PaymentOption ¶
type X402PaymentOption struct {
Scheme string `json:"scheme"`
Network string `json:"network"`
Amount string `json:"amount"`
Resource string `json:"resource"`
Description string `json:"description"`
MimeType string `json:"mimeType,omitempty"`
PayTo string `json:"payTo"`
MaxTimeoutSeconds int `json:"maxTimeoutSeconds,omitempty"`
Asset string `json:"asset,omitempty"`
Extra map[string]any `json:"extra,omitempty"`
}
X402PaymentOption is one entry of an x402 `accepts` array. Ported from protocol.ts X402PaymentOption.
type X402PaymentRequirements ¶
type X402PaymentRequirements struct {
X402Version int `json:"x402Version"`
Accepts []X402PaymentOption `json:"accepts"`
}
X402PaymentRequirements is the x402 challenge body. Ported from protocol.ts.