payAndFetch takes, from the first unpaid request to the settled transaction, and be able to read the Result it returns.
This uses @rail402.dev/sdk. The Quickstart got you a settlement; this page explains what happened between the call and the hash.
The shape of a payment
x402 is a retry protocol built on HTTP402 Payment Required. On Stellar the payment is a signed Soroban authorization entry, not a pre-signed transaction, which is why the facilitator can submit it and pay the fee for you.
1
The unpaid probe
The client requests the resource with no payment. The seller answers
402 and returns the payment terms: the price, the asset, the recipient (payTo), the scheme (exact or upto), and a maxTimeoutSeconds that bounds how long an authorization stays valid.2
The cap check
Before anything is signed, your
maxAmount is compared against the price on the challenge that will actually be paid. If the price is over the cap, the client throws in the selector and nothing is signed. This is why the cap is reliable: it runs on the paid quote, not on a guess.3
Signing the authorization entry
The client builds the Soroban call the payment requires and signs one authorization entry for it. The entry authorizes exactly this call: this asset, this amount, this recipient. It is valid only until
signatureExpirationLedger, roughly 12 ledgers or about 60 seconds, derived from maxTimeoutSeconds. Tampering with any field invalidates the signature.4
The retry
The client repeats the request, now carrying the signed payload. The seller hands it to the facilitator, which verifies the authorization, submits the transaction, sponsors the fee, and returns the settlement in the
PAYMENT-RESPONSE header. The seller then returns the real 200 response and its body.The v2 settlement header is
PAYMENT-RESPONSE, with no X- prefix. Bazaar cataloging outcomes come back in EXTENSION-RESPONSES. You rarely touch these directly: the SDK reads them for you.payAndFetch, line by line
payAndFetch pays a URL you already know. It is the direct path when you are not searching the Bazaar first.
configcarries the signing secret and the facilitator URL. The secret is a testnetS...key. Omit it and the call cannot pay.urlis the endpoint to pay, exactly as you have it (or exactly as search returned it).maxAmountis required and has no default. It is an atomic-unit string, so"100000"is 0.01 USDC at 7 decimals. Never pass a number, a bigint, or a float. A missing cap is an unbounded spender by omission, so the SDK refuses to guess one for you.
The Result shape
Every buyer call returns a discriminated union. There is no thrown error to catch for a normal refusal: you branch on ok.
data carries:
bodyis the resource’s actual response, the thing you paid to receive.paidis the settlement:amount,asset,network, andtransaction. Thetransactionhash is what you look up onexplorer.rail402.dev.
error is always { code, reason, retryable }, and reason is never null. Branch on code, respect retryable.
The cap is checked on the paid quote, not the probe
This is the subtle part, and getting it wrong is a real bug. The unpaid probe in step 1 returns a quote, but that quote is not authoritative: a hostile seller can quote cheap when asked for free and expensive when asked to pay. So the SDK re-applies yourmaxAmount to the price on the request that is actually signed, immediately before signing. Both the probe quote and the paid quote must fit the cap, or nothing is signed.
If you build the payment with the stock
@x402/* client instead of the SDK, put the cap in the client’s payment-requirements selector, which runs on the paid request. Checking only an earlier probe leaves the gap the SDK closes for you. The interop client is shown in Packages.Next steps
Spend controls
The mandatory cap, atomic units, and per-account ceilings in depth.
Discover services
Search the Bazaar and read a listing before you pay it.
The exact scheme
The fixed-price scheme this page pays with.
Auth entries
Why Stellar signs authorization entries, not transactions.
When it fails
Refusals arrive as{ code, reason, retryable }, never as a bare status. The full registry, including which codes mean money already moved, is in Rejection reasons.