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.
pnpm add @solana/mppCharge (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
- Split payments: send one charge to multiple recipients in a single transaction
- Replay protection via consumed transaction signatures
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
- Optional
tokenProgramhint; clients resolve the mint owner and fail closed if discovery fails
mpp-sdk/
├── typescript/ # TypeScript SDK
│ └── packages/mpp/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
├── rust/ # Rust SDK (coming soon)
│ └── src/lib.rs
└── demo/ # Interactive playground
Exports:
@solana/mpp— shared schemas, session types, and authorizers only@solana/mpp/server— server-side charge + session,Mppx,Store@solana/mpp/client— client-side charge + session,Mppx
Server:
import { Mppx, solana } from '@solana/mpp/server'
const mppx = Mppx.create({
secretKey: process.env.MPP_SECRET_KEY,
methods: [
solana.charge({
recipient: 'RecipientPubkey...',
currency: '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')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 automatically- Client requests a resource
- Server returns 402 Payment Required with a challenge (
recipient,amount,currency, optionaltokenProgram, optionalrecentBlockhash) - 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.
Use splits when one charge should pay multiple recipients in the same asset.
The top-level amount is the total paid. The primary recipient receives
amount - sum(splits), and each split recipient receives its own amount.
import { Mppx, solana } from '@solana/mpp/server'
const mppx = Mppx.create({
secretKey: process.env.MPP_SECRET_KEY,
methods: [
solana.charge({
recipient: 'SellerPubkey...',
currency: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
decimals: 6,
splits: [
{ recipient: 'PlatformPubkey...', amount: '50000', memo: 'platform fee' },
{ recipient: 'ReferrerPubkey...', amount: '20000', memo: 'referral fee' },
],
}),
],
})
const result = await mppx.charge({
amount: '1000000', // total: 1.00 USDC
currency: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
})(request)In this example:
- seller receives
930000 - platform receives
50000 - referrer receives
20000
The same splits shape works for native SOL charges.
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.
# TypeScript
cd typescript && pnpm install
just ts-fmt # Format and lint
just ts-build # Build
just ts-test # Unit tests (charge + session, no network)
just ts-test-integration # Integration tests (requires Surfpool)
# Rust
cd rust && cargo build
# Everything
just build # Build both
just test # Test both
just pre-commit # Full pre-commit checksThis SDK implements the Solana Charge Intent for the HTTP Payment Authentication Scheme.
Session method docs and implementation notes are intentionally kept out of this README for now. See docs/methods/sessions.md.
MIT
