payments
ContextVM Payments (CEP-8)
Use this skill when you want ContextVM servers to charge for specific capabilities and clients to pay automatically using the TypeScript SDK payments layer.
Payments are implemented as middleware around transports:
- Server-side middleware gates priced requests: it must not forward to the underlying MCP server until payment is verified.
- Client-side middleware listens for payment notifications, executes a handler, then continues the original request.
This skill documents how to use the SDK payments layer. For transport setup (signers, relays, encryption), see:
Quick start: charge for one tool (server + client)
Server: price a capability and attach payments
import type { PricedCapability } from '@contextvm/sdk/payments';
import { LnBolt11NwcPaymentProcessor, withServerPayments } from '@contextvm/sdk/payments';
const pricedCapabilities: PricedCapability[] = [
{
method: 'tools/call',
name: 'my-tool',
amount: 10,
currencyUnit: 'sats',
description: 'Example paid tool',
},
];
const processor = new LnBolt11NwcPaymentProcessor({
nwcConnectionString: process.env.NWC_SERVER_CONNECTION!,
});
const paidTransport = withServerPayments(baseTransport, {
processors: [processor],
pricedCapabilities,
});
Client: attach a handler and pay automatically
import { LnBolt11NwcPaymentHandler, withClientPayments } from '@contextvm/sdk/payments';
const handler = new LnBolt11NwcPaymentHandler({
nwcConnectionString: process.env.NWC_CLIENT_CONNECTION!,
});
const paidTransport = withClientPayments(baseTransport, {
handlers: [handler],
});
Core concepts (what to understand once)
Notifications and correlation
CEP-8 payments are expressed as correlated JSON-RPC notifications:
notifications/payment_requirednotifications/payment_acceptednotifications/payment_rejected(reject without charging)
They are correlated to the original request via an e tag (request event id). Your app should treat these as notifications, not responses.
PMI (Payment Method Identifier)
Each payment rail is identified by a PMI string (example: bitcoin-lightning-bolt11).
- Server processors advertise which PMIs they can accept.
- Client handlers advertise which PMIs they can pay.
- A payment only works if there is an intersection.
Amounts: advertise vs settle
pricedCapabilities[].amount + currencyUnit are what you advertise (discovery). The selected PMI determines how you settle.
If you use dynamic pricing via resolvePrice, the amount you return must match the unit expected by your chosen processor.
Server patterns
- Fixed pricing via
pricedCapabilities - Dynamic pricing via
resolvePrice - Reject without charging via
resolvePrice → { reject: true, message? } - Waive payment (prepaid/subscription) via
resolvePrice → { waive: true }
Read: references/server-setup.md
Client patterns
- Multiple handlers (multiple rails)
- Handling
payment_rejectedoutcomes - PMI advertisement via
pmitags (automatic when wrapping withwithClientPayments)
Read: references/client-setup.md
Built-in rails (provided by the SDK)
The SDK currently ships multiple built-in payment rails under the same PMI:
- PMI:
bitcoin-lightning-bolt11
Rails:
- Lightning over NWC (NIP-47)
- Server:
LnBolt11NwcPaymentProcessor - Client:
LnBolt11NwcPaymentHandler - Read:
references/lightning-nwc.md
- Server:
- Lightning via LNbits (REST API)
- Server:
LnBolt11LnbitsPaymentProcessor - Client:
LnBolt11LnbitsPaymentHandler - Read:
references/lightning-lnbits.md
- Server:
More rails may be added over time (new PMIs and/or additional implementations under existing PMIs). Prefer selecting rails based on PMI compatibility and operational needs.
Build your own rail (custom PMI)
Implement:
- a server-side
PaymentProcessor(issue + verify) - a client-side
PaymentHandler(pay)
Read: references/custom-rails.md
Troubleshooting pointers
- No
payment_required: verify request matchesmethod+nameinpricedCapabilities. - Payment succeeds but no
payment_accepted: verify server relay connectivity and processor verification settings. - Immediate rejection: handle
notifications/payment_rejectedand surface the optionalmessage.
For general relay/encryption/connection issues, see ../troubleshooting/SKILL.md.
Reference index
More from contextvm/cvmi
overview
Understand ContextVM protocol fundamentals, architecture, and core concepts. Use when users need to learn about ContextVM basics, how it bridges MCP with Nostr, protocol design principles, event kinds, or the relationship between MCP and Nostr in decentralized communication.
32concepts
Understand ContextVM core concepts, architecture decisions, and frequently asked questions. Use when users need clarification on what ContextVM is, why it uses Nostr, decentralization benefits, public vs private servers, network topology, or comparisons with traditional MCP.
32typescript-sdk
Use the @contextvm/sdk TypeScript SDK effectively. Reference for core interfaces, signers, relay handlers, transports, encryption, logging, and SDK patterns. Use when implementing SDK components, extending interfaces, configuring transports, or debugging SDK usage.
32server-dev
Build MCP servers that expose capabilities over the Nostr network using ContextVM. Use when creating new servers, converting existing MCP servers to ContextVM, configuring server transports, implementing access control, or setting up public server announcements.
31client-dev
Build MCP clients that connect to ContextVM servers over Nostr. Use when creating clients, discovering servers, connecting to remote servers, handling encrypted connections, or implementing the proxy pattern for existing MCP clients.
31troubleshooting
Diagnose and resolve ContextVM connection issues, relay problems, encryption failures, authentication errors, and common deployment problems. Use when users report errors, connection failures, timeout issues, or unexpected behavior with ContextVM clients, servers, gateways, or proxies.
17