Skip to main content
This guide walks through writing a custom MPP payment method for USDT0 on Stable and serving an MPP-gated endpoint. The buyer signs an ERC-3009 transferWithAuthorization, the server validates it through mppx’s verify() hook, and settlement happens in a separate step you control.
Concept: For what MPP is and how it relates to x402, see Machine Payments Protocol (MPP). For the x402 equivalent, see Build a pay-per-call API.
The example uses Stable mainnet. Use small amounts when testing.

What you’ll build

An HTTP endpoint that returns 402 Payment Required with an MPP WWW-Authenticate challenge, accepts a signed credential in the Authorization header, verifies it, settles transferWithAuthorization on USDT0, and returns the response with a Payment-Receipt header.

Prerequisites

  • A funded USDT0 wallet on Stable. See Use the faucet or Move USDT0.
  • Node 20+ with mppx, viem, and zod installed.
  • A seller account (an EOA) on Stable. For the default settlement path, the seller pays gas in USDT0; the Gas Waiver section shows the zero-gas variant.

1. Define the shared schema

Method.from() declares the intent and the schemas for the request (Challenge) and the credential payload. Both client and server import this definition.

2. Server: verify the credential

Method.toServer wires verify() into mppx. The function receives the deserialized credential (challenge + payload) and must throw on invalid proofs or return a Receipt.
verify() checks the signature but does not check nonce uniqueness or whether the authorization has already been spent. The chain enforces both at submission time: transferWithAuthorization reverts on a used nonce. The settle step turns those reverts into errors the server can surface to the client.

3. Settle: submit transferWithAuthorization

Settlement is intentionally separate from verify(). After verify() returns, you submit the authorization on-chain through whichever path fits your operational model. Three options, in order of recommendation.

Default: server submits directly

The seller’s EOA submits transferWithAuthorization to USDT0 with the signed authorization. The seller pays gas in USDT0 (Stable’s native gas token), so there is no separate gas-token balance to manage.

Alternative: settle through the Gas Waiver

Use Stable’s Gas Waiver to submit the inner transaction at gasPrice = 0. The seller still signs the wrapping transaction, but pays no gas. Requires a Waiver Server API key.
See Gas waiver protocol for how to build the signed inner transaction (gasPrice: 0, encoded transferWithAuthorization call) before posting.

Alternative: hand off to an x402 facilitator

If you already operate an x402 facilitator integration (Semantic Pay or Heurist), you can reuse it as a settlement target. POST a paymentPayload to /settle; the facilitator submits the on-chain call. The exact paymentPayload shape is x402-middleware-internal and not specified at the wire level. The simplest path is to use the facilitator’s own SDK to build the payload, or stick with the direct-submission path above. The facilitator does not need to speak MPP; it sees only the transferWithAuthorization fields.

4. Client: sign a credential

Method.toClient wires createCredential() into mppx. The client reads the Challenge, signs the EIP-712 authorization with the agent’s viem account, and serializes the credential.

5. Wire the server together

Use mppx’s Express middleware to issue Challenges, parse incoming Authorization headers, run verify(), call your settle function, and emit the Payment-Receipt header.

6. Run the flow end to end

Start the server, confirm the Challenge, run a client, and confirm settlement.
The next step settles a real USDT0 payment on Stable mainnet. Use a dedicated wallet and small amounts.

Confirm the Challenge

Send a paid request

Verify on Stablescan

Open https://stablescan.xyz/tx/0x8f3a1b2c... and confirm the transferWithAuthorization settled to your PAY_TO address.

What you just did

  • Paid in USDT0, denominated in dollars, with no gas-token balance to manage on the buyer side.
  • Used MPP’s WWW-Authenticate / Authorization / Payment-Receipt wire format on the client-server hop.
  • Settled with transferWithAuthorization on Stable in the same HTTP request lifecycle (~700 ms block time).

MPP concept

Read how MPP relates to x402 and what the other intents look like.

MPP sessions

Stream micropayments with off-chain vouchers when per-request settlement is too expensive.

Facilitators

Use Semantic Pay or Heurist as the settlement target instead of submitting directly.
Last modified on June 2, 2026