Your agent should not call Stripe to charge a card or capture a PaymentIntent without a bounded spend boundary and an audit trail. Paybond sits at the tool execution boundary — not inside Stripe's SDK — so Harbor verifies the operation and amount before your handler runs, then auto-submits evidence when the charge succeeds.
Paybond guards tools, not Stripe webhooks
Use Paybond middleware on the tool that creates or captures payments. Keep Stripe webhook handlers for funding signals separate from tool-completion evidence. See How intent funding works.
Try it (no Stripe credentials)
terminal
paybond login
paybond agent sandbox smoke \
--operation payments.charge_customer \
--requested-spend-cents 2500 \
--evidence-preset cost_and_completion \
--result-body '{"status":"completed","cost_cents":2500,"payment_intent_id":"pi_smoke"}' \
--format tableScaffold a Stripe-aware policy
Presets are starting points — fork a local file for your charge tools:
terminal
paybond policy init --preset saas --out paybond.policy.yamlEdit paybond.policy.yaml to match your Stripe tool names:
version: 1
name: stripe-agent-v1
default_deny: true
tools:
payments.charge_customer:
side_effecting: true
max_spend_cents: 50000
evidence_preset: cost_and_completion
payments.list_invoices:
side_effecting: false
intent:
allowed_tools:
- payments.charge_customer
budget:
currency: usd
max_spend_usd: 500
Validate before deploy:
terminal
paybond policy validate-tools --file paybond.policy.yaml --local-onlyWire middleware (agent-agnostic)
Paybond's default path works with any orchestrator that exposes { name, execute } tools — including a custom Stripe wrapper:
paybond-session.ts
import Stripe from "stripe";
import { Paybond } from "@paybond/kit";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! });
async function chargeCustomer(args: {
customerId: string;
amountCents: number;
currency: string;
}) {
const pi = await stripe.paymentIntents.create({
amount: args.amountCents,
currency: args.currency,
customer: args.customerId,
confirm: true,
automatic_payment_methods: { enabled: true },
});
return { status: pi.status, cost_cents: args.amountCents, payment_intent_id: pi.id };
}
const instrumented = await paybond.instrument({
policy: "./paybond.policy.yaml",
tools: {
"payments.charge_customer": chargeCustomer,
"payments.list_invoices": listInvoices,
},
});
// Per session (production): const runtime = await instrumented.bind({ intentId, capabilityToken });
// Sandbox quickstart: paybond.agent({ policy: "./paybond.policy.yaml", tools: { ... } })Production checklist
- Configure settlement rails in the console — Configure settlement rails.
- Create and fund an intent — Fund intents by rail.
- Bind middleware per request with
instrumented.bind({ intentId, capabilityToken })or a lazycontextprovider. - Never pass tenant ids from unauthenticated client input — tenant scope comes from the Paybond API key.
Related guides
- Agent middleware — run binding, registry, auto-evidence
- Agent policy-as-code — GitOps validation and presets
- Agent-agnostic spend controls — default
{ name, execute }wiring
Developer reference: /docs/kit/agent-middleware.