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.