skalor-merchant
v0.1.0
Published
Merchant middleware for Skalor — accept AI agent payments with one line of code
Maintainers
Readme
@skalor/merchant
Accept AI agent payments with one line of code.
Drop-in middleware for Express, Fastify, and Next.js that turns any API endpoint into a paid endpoint. AI agents pay via Skalor A2A settlement — no checkout pages, no subscriptions, no accounts.
npm install @skalor/merchantQuick Start
Express
import express from "express"
import { skalorPay } from "@skalor/merchant/express"
const app = express()
app.get("/api/weather", skalorPay({
jwtSecret: process.env.SKALOR_JWT_SECRET,
price: 0.05,
receiverAgentId: "my-weather-agent",
description: "Hourly weather dataset",
}), (req, res) => {
// req.skalorReceipt has the full verified payment receipt
res.json({ forecast: "72°F, sunny" })
})Fastify
import Fastify from "fastify"
import { skalorPay } from "@skalor/merchant/fastify"
const app = Fastify()
app.get("/api/weather", {
preHandler: skalorPay({
jwtSecret: process.env.SKALOR_JWT_SECRET,
price: 0.05,
receiverAgentId: "my-weather-agent",
}),
}, async (request) => {
return { forecast: "72°F, sunny" }
})Next.js (App Router)
// app/api/weather/route.ts
import { withSkalorPay } from "@skalor/merchant/nextjs"
export const GET = withSkalorPay({
jwtSecret: process.env.SKALOR_JWT_SECRET,
price: 0.05,
receiverAgentId: "my-weather-agent",
description: "Hourly weather dataset",
}, async (request, { receipt }) => {
return Response.json({ forecast: "72°F, sunny" })
})How It Works
Agent Your API Skalor
│ │ │
│─── GET /api/weather ──────▶│ │
│ │ │
│◀── 402 Payment Required ──│ │
│ X-Skalor-Amount: 0.05 │ │
│ X-Skalor-Settlement-Url │ │
│ │ │
│─── POST /skalor-pay ──────────────────────────────────▶│
│ │ Settle A2A│
│◀── JWT Receipt ────────────────────────────────────────│
│ │ │
│─── GET /api/weather ──────▶│ │
│ Authorization: Bearer <receipt> │
│ │── verify JWT ──▶ │
│◀── { forecast: "72°F" } ──│ │- Agent calls your endpoint — no token
- Middleware returns HTTP 402 with price + settlement URL
- Agent's SDK pays via
/skalor-pay→ gets signed JWT receipt - Agent retries with
Authorization: Bearer <receipt> - Middleware verifies the receipt → your handler runs
- You see the payment in your Skalor Dashboard
Configuration
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| jwtSecret | string | ✅ | — | HS256 secret for verifying receipts |
| price | number \| (req) => number | ✅ | — | USD price (static or dynamic) |
| receiverAgentId | string | — | — | Your agent ID in Skalor |
| merchantId | string | — | — | Your merchant UUID |
| description | string | — | — | What the agent is paying for |
| currency | string | — | "USDC" | Currency code |
| network | string | — | "tempo" | Settlement network |
| settlementUrl | string | — | Skalor default | Override settlement endpoint |
| onPaymentVerified | (receipt) => void | — | — | Callback after verification |
Dynamic Pricing
skalorPay({
jwtSecret: process.env.SKALOR_JWT_SECRET,
price: (req) => {
// Price based on query parameters, model size, etc.
return req.query.premium ? 0.10 : 0.03
},
receiverAgentId: "my-agent",
})Webhook Verification
Verify incoming Skalor webhooks:
import { verifyWebhookSignature } from "@skalor/merchant"
app.post("/webhooks/skalor", express.raw({ type: "application/json" }), (req, res) => {
const isValid = verifyWebhookSignature(
req.body,
req.headers["x-skalor-signature"],
process.env.SKALOR_WEBHOOK_SECRET,
)
if (!isValid) return res.status(401).send("Invalid signature")
const event = JSON.parse(req.body)
console.log("Payment received:", event)
res.sendStatus(200)
})Standalone Receipt Verification
Use the verifier directly without middleware:
import { verifyReceipt } from "@skalor/merchant"
const receipt = verifyReceipt(jwtToken, {
jwtSecret: process.env.SKALOR_JWT_SECRET,
price: 0.05,
})
console.log(receipt.payer) // buyer agent ID
console.log(receipt.amount) // 0.05
console.log(receipt.tx_hash) // on-chain transaction hash
console.log(receipt.status) // "SETTLED"402 Response Format
When an agent hits a paywalled endpoint without a valid receipt:
Headers:
HTTP/1.1 402 Payment Required
X-Skalor-Payment-Required: true
X-Skalor-Amount: 0.05
X-Skalor-Currency: USDC
X-Skalor-Network: tempo
X-Skalor-Settlement-Url: https://...supabase.co/functions/v1/skalor-pay
X-Skalor-Receiver-Agent-Id: my-weather-agentBody:
{
"error": "payment_required",
"message": "This endpoint requires payment via Skalor A2A settlement",
"payment": {
"amount": 0.05,
"currency": "USDC",
"network": "tempo",
"settlement_url": "https://...supabase.co/functions/v1/skalor-pay",
"receiver_agent_id": "my-weather-agent"
}
}License
MIT — SKALOR
