Skip to content

Commit 6e5238c

Browse files
committed
docs: add MPP on Stellar documentation
Adds documentation for the Machine Payments Protocol (MPP) on Stellar, mirroring the existing x402 docs structure at docs/build/apps/x402/. Files added: - docs/build/apps/mpp/_category_.json — Docusaurus sidebar config - docs/build/apps/mpp/README.mdx — Overview: what is MPP, charge vs channel modes, install, examples - docs/build/apps/mpp/quickstart-guide.mdx — Step-by-step server + client tutorial with fee sponsorship - docs/build/apps/mpp/channel-guide.mdx — Off-chain payment channel guide (open, pay, close) MPP uses stellar-mpp-sdk for direct on-chain settlement (no external facilitator), supporting both one-time charge payments and high-frequency off-chain channel commitments.
1 parent 81b2d05 commit 6e5238c

4 files changed

Lines changed: 525 additions & 0 deletions

File tree

docs/build/apps/mpp/README.mdx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
title: MPP on Stellar
3+
sidebar_position: 75
4+
sidebar_label: MPP on Stellar
5+
description: "Use the Machine Payments Protocol (MPP) for per-request HTTP payments on Stellar, with support for AI agents, APIs, and off-chain payment channels."
6+
---
7+
8+
## What is MPP?
9+
10+
The [Machine Payments Protocol (MPP)](https://mpp.dev) is an open protocol that enables programmatic, per-request payments over HTTP, designed especially for AI agents and APIs. It extends the `402 Payment Required` HTTP status code into a machine-readable payment negotiation layer for both humans and autonomous agents.
11+
12+
On Stellar, MPP works with Soroban SAC (Stellar Asset Contract) token transfers so that clients can pay for API requests natively, without an external facilitator. This makes it ideal for micropayments, AI agent-driven APIs, and payment-enabled applications.
13+
14+
The `stellar-mpp-sdk` provides two payment modes:
15+
16+
- **Charge** — One-time on-chain SAC token transfers, settled per request
17+
- **Channel** — Off-chain payment commitments via a [one-way payment channel](https://github.com/stellar-experimental/one-way-channel) contract, with batch settlement on close
18+
19+
To build an MPP-enabled service or integrate payments into your app, see [Build Applications](../README.mdx) and the resources below.
20+
21+
## Demo
22+
23+
Try the [MPP Demo](https://github.com/stellar/stellar-mpp-sdk/tree/main/demo) locally. It runs a Node.js server that charges 0.01 USDC per request and a minimal browser UI for testing end-to-end payment flows on Stellar Testnet.
24+
25+
## Charge mode
26+
27+
Each request triggers a Soroban SAC `transfer` settled on-chain individually. This is the simplest mode — no channel setup or pre-funding required.
28+
29+
Two credential sub-modes are available:
30+
31+
- **Pull** (default) — The client prepares and signs the Soroban authorization entries; the server broadcasts the transaction. Supports an optional `feePayer` (sponsored) path where the server rebuilds the transaction with its own account as source, so the client never pays network fees.
32+
- **Push** — The client broadcasts the transaction itself and sends the transaction hash for server verification.
33+
34+
## Channel mode
35+
36+
Uses a [one-way payment channel](https://github.com/stellar-experimental/one-way-channel) contract. The funder deposits tokens into the channel once, then makes many off-chain payments by signing cumulative commitments — no per-payment on-chain transactions, ideal for high-frequency AI agent interactions. The server settles by closing the channel when convenient.
37+
38+
## Install
39+
40+
```bash
41+
npm install stellar-mpp-sdk mppx @stellar/stellar-sdk
42+
```
43+
44+
## Examples
45+
46+
- **stellar-mpp-sdk (GitHub)** — SDK source code, examples, and integration tests. [View on GitHub](https://github.com/stellar/stellar-mpp-sdk)
47+
- **Server example** — A minimal Node.js HTTP server charging 0.01 USDC per request using `stellar-mpp-sdk/server`. [View on GitHub](https://github.com/stellar/stellar-mpp-sdk/blob/main/examples/server.ts)
48+
- **Client example** — A Node.js client that automatically handles 402 responses using `stellar-mpp-sdk/client`. [View on GitHub](https://github.com/stellar/stellar-mpp-sdk/blob/main/examples/client.ts)
49+
50+
## Learn more
51+
52+
- [MPP Specification](https://mpp.dev) — Official MPP protocol specification
53+
- [stellar-mpp-sdk (npm)](https://www.npmjs.com/package/stellar-mpp-sdk) — npm package for MPP on Stellar
54+
- [mppx (npm)](https://www.npmjs.com/package/mppx) — Core MPP framework library
55+
- [one-way-channel contract](https://github.com/stellar-experimental/one-way-channel) — Soroban contract powering channel mode
56+
- [Signing Soroban invocations](../../guides/transactions/signing-soroban-invocations.mdx) — Auth-entry signing and transaction signing on Stellar
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"collapsed": false
3+
}
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
---
2+
title: MPP Channel Payment Guide
3+
description: Set up off-chain payment channels for high-frequency payments on Stellar using the Machine Payments Protocol (MPP).
4+
sidebar_position: 30
5+
---
6+
7+
This guide explains how to use MPP **channel mode** with `stellar-mpp-sdk`. Channel payments use a [one-way payment channel](https://github.com/stellar-experimental/one-way-channel) Soroban contract — the funder deposits tokens once, then makes many off-chain payments by signing cumulative commitments. No on-chain transaction is needed per payment, making this ideal for high-frequency AI agent interactions.
8+
9+
## How channel payments work
10+
11+
```
12+
Client (Funder) Server (Recipient)
13+
| |
14+
| [Deploy channel contract |
15+
| with commitment key + USDC |
16+
| deposit on-chain] |
17+
| |
18+
| GET /resource |
19+
|----------------------------->|
20+
| |
21+
| 402 Payment Required |
22+
| (channel: C..., amount: 0.1) |
23+
|<-----------------------------|
24+
| |
25+
| Sign commitment off-chain |
26+
| (cumulative: 0.1 XLM) |
27+
|----------------------------->|
28+
| |
29+
| Verify ed25519 sig, 200 OK |
30+
|<-----------------------------|
31+
| |
32+
| GET /resource |
33+
|----------------------------->|
34+
| |
35+
| 402 (cumulative: 0.1 XLM) |
36+
|<-----------------------------|
37+
| |
38+
| Sign commitment |
39+
| (cumulative: 0.2 XLM) |
40+
|----------------------------->|
41+
| |
42+
| Verify, 200 OK |
43+
|<-----------------------------|
44+
| |
45+
| [Server closes channel |
46+
| when convenient to settle] |
47+
```
48+
49+
Each commitment is cumulative. The server tracks the highest commitment it has seen; closing the channel batch-settles all payments in a single on-chain transaction.
50+
51+
## Prerequisites
52+
53+
Before using channel mode, you need a deployed [one-way-channel contract](https://github.com/stellar-experimental/one-way-channel) on Stellar Testnet or Mainnet. The contract is initialized with:
54+
55+
- A **commitment key** — an ed25519 keypair. The client signs commitments with the private key; the contract verifies with the public key.
56+
- A **token deposit** — the funder's initial XLM or USDC balance in the channel.
57+
58+
See the [one-way-channel repo](https://github.com/stellar-experimental/one-way-channel) for deployment instructions.
59+
60+
## Channel server
61+
62+
Create `channel-server.js`:
63+
64+
```js title="channel-server.js"
65+
import express from 'express'
66+
import { Mppx, Store } from 'mppx/server'
67+
import { stellar } from 'stellar-mpp-sdk/channel/server'
68+
import { StrKey } from '@stellar/stellar-sdk'
69+
70+
const PORT = 3001
71+
const CHANNEL_CONTRACT = process.env.CHANNEL_CONTRACT // C... (56 chars)
72+
const COMMITMENT_PUBKEY = process.env.COMMITMENT_PUBKEY // 64-char hex ed25519 public key
73+
74+
if (!CHANNEL_CONTRACT || !COMMITMENT_PUBKEY) {
75+
console.error('Set CHANNEL_CONTRACT and COMMITMENT_PUBKEY environment variables')
76+
process.exit(1)
77+
}
78+
79+
// Convert raw ed25519 public key (hex) to a Stellar G... address
80+
const commitmentPublicKeyG = StrKey.encodeEd25519PublicKey(
81+
Buffer.from(COMMITMENT_PUBKEY, 'hex'),
82+
)
83+
84+
const mppx = Mppx.create({
85+
secretKey: process.env.MPP_SECRET_KEY ?? 'my-channel-secret',
86+
methods: [
87+
stellar.channel({
88+
channel: CHANNEL_CONTRACT,
89+
commitmentKey: commitmentPublicKeyG,
90+
store: Store.memory(), // tracks cumulative amounts + replay protection
91+
network: 'testnet',
92+
}),
93+
],
94+
})
95+
96+
const app = express()
97+
98+
app.get('/my-service', async (req, res) => {
99+
const webReq = new Request(`http://localhost:${PORT}${req.url}`, {
100+
method: req.method,
101+
headers: Object.fromEntries(
102+
Object.entries(req.headers).filter(([, v]) => v != null),
103+
),
104+
})
105+
106+
const result = await mppx.channel({
107+
amount: '0.1', // 0.1 XLM per request (human-readable)
108+
description: 'API call',
109+
})(webReq)
110+
111+
if (result.status === 402) {
112+
const challenge = result.challenge
113+
challenge.headers.forEach((value, key) => res.setHeader(key, value))
114+
return res.status(402).send(await challenge.text())
115+
}
116+
117+
const response = result.withReceipt(
118+
Response.json({ secret: 'valuable content' }),
119+
)
120+
response.headers.forEach((value, key) => res.setHeader(key, value))
121+
return res.status(response.status).send(await response.text())
122+
})
123+
124+
app.listen(PORT, () => {
125+
console.log(`MPP channel server listening on http://localhost:${PORT}/my-service`)
126+
})
127+
```
128+
129+
Start the server:
130+
131+
```bash
132+
CHANNEL_CONTRACT=CABC... COMMITMENT_PUBKEY=<64-hex-chars> node channel-server.js
133+
```
134+
135+
## Channel client
136+
137+
Create `channel-client.js`:
138+
139+
```js title="channel-client.js"
140+
import { Keypair } from '@stellar/stellar-sdk'
141+
import { Mppx } from 'mppx/client'
142+
import { stellar } from 'stellar-mpp-sdk/channel/client'
143+
import { readFileSync } from 'node:fs'
144+
145+
// Load commitment secret key from .env
146+
const env = Object.fromEntries(
147+
readFileSync('.env', 'utf-8')
148+
.split('\n')
149+
.filter((l) => l.includes('='))
150+
.map((l) => l.split('=')),
151+
)
152+
const COMMITMENT_SECRET = env.COMMITMENT_SECRET?.trim() // 64-char hex ed25519 secret
153+
154+
if (!COMMITMENT_SECRET) {
155+
console.error('Add COMMITMENT_SECRET=<64-hex-chars> to .env')
156+
process.exit(1)
157+
}
158+
159+
// Convert raw hex ed25519 seed to a Stellar Keypair
160+
const commitmentKey = Keypair.fromRawEd25519Seed(
161+
Buffer.from(COMMITMENT_SECRET, 'hex'),
162+
)
163+
console.log(`Commitment public key: ${commitmentKey.publicKey()}`)
164+
165+
// Polyfill global fetch — 402 responses are handled automatically
166+
Mppx.create({
167+
methods: [
168+
stellar.channel({
169+
commitmentKey,
170+
onProgress(event) {
171+
switch (event.type) {
172+
case 'challenge':
173+
console.log(`Challenge: ${event.amount} XLM via channel ${event.channel.slice(0, 12)}...`)
174+
break
175+
case 'signed':
176+
console.log(`Commitment signed (cumulative: ${event.cumulativeAmount} stroops)`)
177+
break
178+
}
179+
},
180+
}),
181+
],
182+
})
183+
184+
// Requests are automatically paid with off-chain commitment signatures
185+
const res1 = await fetch('http://localhost:3001/my-service')
186+
console.log(`Request 1 (${res1.status}):`, await res1.json())
187+
188+
// Second request increments cumulative amount; still off-chain
189+
const res2 = await fetch('http://localhost:3001/my-service')
190+
console.log(`Request 2 (${res2.status}):`, await res2.json())
191+
```
192+
193+
Add the commitment secret key to `.env`:
194+
195+
```bash title=".env"
196+
COMMITMENT_SECRET=<64-hex-chars>
197+
```
198+
199+
Run the client:
200+
201+
```bash
202+
node channel-client.js
203+
```
204+
205+
Each request signs an increasing cumulative commitment off-chain. The server verifies the ed25519 signature against the on-chain `commitment_key` in the contract (via Soroban simulation, no transaction needed) and returns the protected content immediately.
206+
207+
## Closing the channel
208+
209+
When the server wants to settle, it closes the channel using the highest seen commitment:
210+
211+
```js
212+
import { close } from 'stellar-mpp-sdk/channel/server'
213+
import { Keypair } from '@stellar/stellar-sdk'
214+
215+
// Close the channel and settle all accumulated payments on-chain
216+
await close({
217+
channel: CHANNEL_CONTRACT,
218+
network: 'testnet',
219+
submitter: Keypair.fromSecret(process.env.SUBMITTER_SECRET), // pays tx fees
220+
})
221+
```
222+
223+
Closing submits a single on-chain transaction that transfers the cumulative committed amount from the channel to the recipient. The remainder is returned to the funder.
224+
225+
## Subpath exports
226+
227+
Channel mode uses separate subpath exports to avoid bundling unused code:
228+
229+
| Path | Purpose |
230+
|------|---------|
231+
| `stellar-mpp-sdk/channel/server` | `stellar`, `close`, `Mppx`, `Store` |
232+
| `stellar-mpp-sdk/channel/client` | `stellar`, `Mppx` |
233+
| `stellar-mpp-sdk/channel` | Channel method schema (Zod) |
234+
235+
## Additional documentation
236+
237+
- [one-way-channel contract](https://github.com/stellar-experimental/one-way-channel) — Soroban contract, deployment scripts, and parameters
238+
- [stellar-mpp-sdk on GitHub](https://github.com/stellar/stellar-mpp-sdk) — Full API reference and integration tests
239+
- [MPP Quickstart Guide](./quickstart-guide.mdx) — Charge mode (per-request on-chain settlement)
240+
- [MPP Specification](https://mpp.dev) — Protocol specification

0 commit comments

Comments
 (0)