Package Exports
- @solana/mpp
- @solana/mpp/client
- @solana/mpp/server
Readme
@solana/mpp
Solana payment method for the Machine Payments Protocol.
MPP is an open protocol proposal that lets any HTTP API accept payments using the 402 Payment Required flow.
[!IMPORTANT] This repository is under active development. The Solana MPP spec is not yet finalized — APIs and wire formats are subject to change.
Install
pnpm add @solana/mppPeer dependencies:
pnpm add @solana/kit mppx
# Optional — for Swig session authorization:
pnpm add @swig-wallet/kitFeatures
Charge (one-time payments)
- Native SOL and SPL token transfers (USDC, PYUSD, Token-2022, etc.)
- Two settlement modes: pull (
type="transaction", default) and push (type="signature") - Fee sponsorship: server pays transaction fees on behalf of clients
- Replay protection via consumed transaction signatures
Session (metered / streaming payments)
- Voucher-based payment channels with monotonic cumulative amounts
- Multiple authorization modes:
unbounded,regular_budget,swig_session - Auto-open, auto-topup, and close lifecycle
- Swig smart wallet integration for on-chain spend limits
General
- Works with ConnectorKit,
@solana/kitkeypair signers, and Solana Keychain remote signers - Server pre-fetches
recentBlockhashto save client an RPC round-trip - Transaction simulation before broadcast to prevent wasted fees
Architecture
sdk/src/
├── Methods.ts # Shared charge + session schemas
├── constants.ts # Token programs, USDC mints, RPC URLs
├── server/
│ ├── Charge.ts # Server: challenge, verify, broadcast
│ └── Session.ts # Server: session channel management
├── client/
│ ├── Charge.ts # Client: build tx, sign, send
│ └── Session.ts # Client: session lifecycle
└── session/
├── Types.ts # Session types and interfaces
├── Voucher.ts # Voucher signing and verification
├── ChannelStore.ts # Persistent channel state
└── authorizers/ # Pluggable authorization strategies
├── UnboundedAuthorizer.ts
├── BudgetAuthorizer.ts
└── SwigSessionAuthorizer.tsExports:
@solana/mpp— shared schemas, session types, and authorizers@solana/mpp/server— server-side charge + session,Mppx,Store@solana/mpp/client— client-side charge + session,Mppx
Quick Start
Charge (one-time payment)
Server:
import { Mppx, solana } from '@solana/mpp/server'
const mppx = Mppx.create({
secretKey: process.env.MPP_SECRET_KEY,
methods: [
solana.charge({
recipient: 'RecipientPubkey...',
splToken: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
decimals: 6,
}),
],
})
const result = await mppx.charge({
amount: '1000000', // 1 USDC
currency: 'USDC',
})(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))Client:
import { Mppx, solana } from '@solana/mpp/client'
const mppx = Mppx.create({
methods: [solana.charge({ signer })], // any TransactionSigner
})
const response = await mppx.fetch('https://api.example.com/paid-endpoint')Session (metered payments)
Server:
import { Mppx, solana } from '@solana/mpp/server'
const mppx = Mppx.create({
secretKey: process.env.MPP_SECRET_KEY,
methods: [
solana.session({
recipient: 'RecipientPubkey...',
asset: { kind: 'sol', decimals: 9 },
channelProgram: 'ChannelProgramId...',
pricing: { unit: 'request', amountPerUnit: '10', meter: 'api_calls' },
sessionDefaults: { suggestedDeposit: '1000', ttlSeconds: 60 },
}),
],
})Client:
import { Mppx, solana } from '@solana/mpp/client'
import { UnboundedAuthorizer } from '@solana/mpp'
const mppx = Mppx.create({
methods: [
solana.session({
signer,
authorizer: new UnboundedAuthorizer({ signer, buildOpenTx, buildTopupTx }),
}),
],
})
const response = await mppx.fetch('https://api.example.com/metered-endpoint')Fee Sponsorship (charge)
The server can pay transaction fees on behalf of clients:
// Server — pass a TransactionPartialSigner to cover fees
solana.charge({
recipient: '...',
signer: feePayerSigner, // KeyPairSigner, Keychain SolanaSigner, etc.
})
// Client — no changes needed, fee payer is handled automaticallyHow It Works
Charge Flow
- Client requests a resource
- Server returns 402 Payment Required with a challenge (
recipient,amount,currency,recentBlockhash) - Client builds and signs a Solana transfer transaction
- Server simulates, broadcasts, confirms on-chain, and verifies the transfer
- Server returns the resource with a
Payment-Receiptheader
With fee sponsorship, the client partially signs (transfer authority only) and the server co-signs as fee payer before broadcasting.
Session Flow
- First request: server returns 402, client opens a channel (deposit + voucher)
- Subsequent requests: client sends updated vouchers with monotonic cumulative amounts
- Server deducts from the channel balance per its pricing config
- When balance runs low: client tops up the channel
- On close: final voucher settles the channel
Demo
An interactive playground with a React frontend and Express backend, running against Surfpool.
- Charge flow demo:
http://localhost:5173/charges - Session flow demo:
http://localhost:5173/sessions
surfpool start
pnpm demo:install
pnpm demo:server
pnpm demo:appSee demo/README.md for full details.
Development
pnpm install
just fmt # Format and lint
just build # Typecheck
just test # Unit tests (charge + session, no network)
just test-integration # Integration tests (requires Surfpool)
just test-all # All tests
just pre-commit # fmt + typecheck + unit testsSpec
This SDK implements the Solana Charge Intent for the HTTP Payment Authentication Scheme.
Session method docs and implementation notes:
License
MIT