Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MPP protocol mechanics

sorocharge-mpp implements MPP’s draft-stellar-charge-00 — the "stellar" payment method for the "charge" intent, layered on the base draft-httpauth-payment-01 “Payment” HTTP authentication scheme. Only the charge-intent mode is implemented. MPP’s session/channel mode is a different signing primitive entirely (a raw ed25519 signature over a one-way-channel contract’s prepare_commitment output, not a SorobanAuthorizationEntry) and does not exist anywhere in this crate.

The flow

Client                                            Server                 Stellar Network
   │  GET /resource                                   │                        │
   ├──────────────────────────────────────────────────>                       │
   │  402 Payment Required                             │                        │
   │  WWW-Authenticate: Payment id=.., request=..       │                        │
   <──────────────────────────────────────────────────┤                        │
   │  (build + sign a charge entry)                     │                        │
   │  GET /resource                                     │                        │
   │  Authorization: Payment <base64url credential>     │                        │
   ├──────────────────────────────────────────────────>│  verify + submit        │
   │                                                     ├───────────────────────>│
   │  200 OK                                            │                        │
   │  Payment-Receipt: <base64url receipt>               │                        │
   <──────────────────────────────────────────────────┤                        │

Unlike x402, this is a genuine HTTP authentication scheme with WWW-Authenticate challenge and Authorization credential headers — not a bespoke header pair — and it’s MppServer’s job (plain async functions again, not a bound HTTP route) to issue the challenge and settle the credential.

Pull mode vs. push mode

Pull mode (type="transaction", default)Push mode (type="hash", fallback)
Who submitsThe serverThe client, directly to the network
payload carriesBase64 XDR of a transactionA 64-character hex transaction hash
Fee sponsorshipAvailable (feePayer: true)Never available
Server’s jobVerify, (maybe rebuild,) submit, pollFetch via getTransaction, verify shape

Pull mode, sponsored (feePayer: true)

The client signs only the authorization entry, using the legacy credential type — this spec never mentions CAP-71 at all, unlike x402, and permits nothing but sorobanCredentialsAddress. The transaction’s source account is the all-zeros account; the server replaces it, the sequence number, and the fee at settlement, exactly as in x402.

Pull mode, unsponsored (feePayer: false or absent)

The client builds a fully signed, network-ready transaction: a real sequence number (from the payer’s own account), a real fee (derived from a simulation), and timeBounds.maxTime set to the challenge’s expires. The server submits it unmodified.

Push mode (type="hash")

The client has already broadcast the transaction itself. The server fetches it via getTransaction, checks its status is SUCCESS, and checks the same transfer shape pull mode does. The spec requires servers to maintain a set of consumed transaction hashes, so a settled push-mode transaction can’t be presented again on a fresh challenge — MppServer::settle_credential takes an already_consumed flag for exactly this, since the replay set itself is the caller’s to own (see Running an MPP server).

Ledger expiration

Both the client and the server derive a ledger expiration from the challenge’s expires timestamp: currentLedger + ceil(remaining_seconds / 5). The client actually uses half that allowance — a margin discovered on live testnet, where ledgers closing slower than the spec’s 5-second assumption made a fully-allowance-sized expiry fail the server’s own bound check. See sorocharge-mpp/src/client.rs for the exact reasoning.

Server safety

The sponsored flow’s facilitator-safety checks mirror x402’s: the server’s own address must not appear as the from argument or in any authorization entry, the transaction source must be the all-zeros account, and a re-simulation must show only the expected transfer (sorocharge_signer::verify_transfer_effects).