@pactspec/stripe-billing
v0.1.0
Published
Stripe payment integration for PactSpec agents — metered billing, checkout, and usage reporting
Maintainers
Readme
@pactspec/stripe-billing
Stripe payment integration for PactSpec agents. Adds metered billing, checkout sessions, and usage reporting to any Express-style HTTP agent endpoint.
Zero runtime dependencies. All Stripe API calls use the built-in fetch() API with direct HTTP requests to https://api.stripe.com/v1/. The stripe npm package is not required (but can be used alongside this library if you prefer).
Installation
npm install @pactspec/stripe-billingQuick Start: Per-Invocation Billing
import express from 'express';
import { stripeBillingMiddleware } from '@pactspec/stripe-billing';
const app = express();
app.use('/api/agent', stripeBillingMiddleware({
stripeSecretKey: process.env.STRIPE_SECRET_KEY!,
pricing: { model: 'per-invocation', amount: 1, currency: 'usd' },
lookupCustomer: async (req) => req.headers['x-stripe-customer'] ?? null,
stripePriceId: 'price_abc123', // enables auto-generated checkout URLs in 402 responses
freeQuota: 100, // first 100 calls are free per customer
}));
app.post('/api/agent', (req, res) => {
res.json({ result: 'Hello from the agent' });
});When a request arrives without a valid Stripe customer or subscription, the middleware returns:
{
"error": "Payment required",
"message": "A valid Stripe subscription or checkout session is required to access this agent.",
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_...",
"sessionId": "cs_..."
}Setting Up Stripe Products and Prices
Before using this package, create a Stripe Product and Price in your Stripe Dashboard or via the API:
# Create a product
curl https://api.stripe.com/v1/products \
-u sk_test_YOUR_KEY: \
-d name="My Agent - Per Invocation"
# Create a metered price (for per-invocation billing)
curl https://api.stripe.com/v1/prices \
-u sk_test_YOUR_KEY: \
-d product=prod_XXX \
-d currency=usd \
-d unit_amount=1 \
-d "recurring[interval]=month" \
-d "recurring[usage_type]=metered"Use the resulting price_XXX ID as stripePriceId in the middleware options.
Customer Identification
The middleware identifies customers in this order:
X-Stripe-Customerheader — the Stripe customer ID directly (cus_...)Authorization: Bearer cs_...header — a Stripe Checkout Session ID; the middleware verifies it and extracts the customerlookupCustomercallback — your custom logic to map any request attribute to a Stripe customer ID
Metered Billing for Per-Token Pricing
For agents that charge per token, set model: 'per-token' and include a X-Token-Count response header:
app.use('/api/agent', stripeBillingMiddleware({
stripeSecretKey: process.env.STRIPE_SECRET_KEY!,
pricing: { model: 'per-token', amount: 1, currency: 'usd' },
lookupCustomer: async (req) => req.headers['x-stripe-customer'] ?? null,
onUsageReported: (record) => {
console.log(`Reported ${record.quantity} tokens for ${record.customerId}`);
},
}));
app.post('/api/agent', (req, res) => {
const tokens = 150;
res.setHeader('X-Token-Count', tokens);
res.json({ result: '...', tokenCount: tokens });
});The middleware reads the X-Token-Count header from the response after it finishes and reports that quantity to Stripe.
Free Quota Configuration
The freeQuota option lets each customer make N free invocations before billing kicks in. Usage is tracked in-memory per process.
stripeBillingMiddleware({
// ...
freeQuota: 50, // 50 free calls per customer
})Notes:
- Free quota resets when the process restarts (in-memory tracking)
- Free quota is checked before subscription verification, so free-tier users don't need a Stripe subscription
- For persistent quota tracking, use the
onUsageReportedcallback to store usage in your database
Checkout Flow for New Customers
Generate a Checkout Session programmatically:
import { createCheckoutSession, verifyCheckoutSession } from '@pactspec/stripe-billing';
// Create a session
const { url, sessionId } = await createCheckoutSession({
stripeSecretKey: process.env.STRIPE_SECRET_KEY!,
priceId: 'price_abc123',
successUrl: 'https://yourapp.com/success?session_id={CHECKOUT_SESSION_ID}',
cancelUrl: 'https://yourapp.com/cancel',
mode: 'subscription',
metadata: { agentId: 'urn:pactspec:acme:my-agent' },
});
// Redirect the user to `url`...
// Later, verify the session completed
const paid = await verifyCheckoutSession(sessionId, process.env.STRIPE_SECRET_KEY!);Usage Reporting (Direct)
For advanced use cases, report usage directly:
import { reportUsage, getUsageSummary } from '@pactspec/stripe-billing';
// Report 1 unit of usage
await reportUsage({
stripeSecretKey: process.env.STRIPE_SECRET_KEY!,
subscriptionItemId: 'si_abc123',
quantity: 1,
});
// Report token-based usage
await reportUsage({
stripeSecretKey: process.env.STRIPE_SECRET_KEY!,
subscriptionItemId: 'si_abc123',
quantity: 350, // 350 tokens
});
// Get usage summary
const summary = await getUsageSummary('cus_abc123', {
stripeSecretKey: process.env.STRIPE_SECRET_KEY!,
});
console.log(summary.items);PactSpec Spec Declaration
Reference Stripe billing in your PactSpec agent spec:
{
"skills": [{
"id": "my-skill",
"name": "My Skill",
"description": "A billable skill",
"inputSchema": { "type": "object" },
"outputSchema": { "type": "object" },
"pricing": {
"model": "per-invocation",
"amount": 1,
"currency": "USD",
"protocol": "stripe"
}
}]
}API Reference
stripeBillingMiddleware(options)
Returns an Express-compatible middleware function.
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| stripeSecretKey | string | Yes | Stripe secret key |
| pricing | BillingPricing | Yes | { model, amount, currency } |
| lookupCustomer | (req) => Promise<string \| null> | Yes | Maps request to Stripe customer ID |
| onUsageReported | (record: UsageRecord) => void | No | Callback after usage is reported |
| freeQuota | number | No | Free invocations per customer (default: 0) |
| stripePriceId | string | No | Price ID for auto-generated checkout URLs |
| checkoutSuccessUrl | string | No | Redirect URL after successful checkout |
| checkoutCancelUrl | string | No | Redirect URL if checkout is cancelled |
createCheckoutSession(options)
Creates a Stripe Checkout Session. Returns { url, sessionId }.
verifyCheckoutSession(sessionId, stripeSecretKey)
Returns true if the session is paid and complete.
reportUsage(options)
Reports usage to Stripe for a metered subscription item.
getUsageSummary(customerId, options)
Returns usage summary across all subscription items for a customer.
License
MIT
