Your agent should not create a checkout or complete an order without a bounded spend boundary and an audit trail. Paybond sits at the tool execution boundary — Harbor verifies the operation and amount before your handler runs, then you attach evidence after the checkout completes.
Paybond guards tools, not Shopify webhooks
Shopify order webhooks (orders/create, orders/paid) are funding signals and binding checks. They are separate from tool-completion evidence submitted after you hold a capability_token. See How intent funding works.
Try it (no Shopify credentials)
Smoke the contract with a sandbox intent and a Shopify-shaped tool result:
terminal
paybond login
paybond agent sandbox smoke \
--operation commerce.checkout \
--requested-spend-cents 4500 \
--evidence-preset cost_and_completion \
--result-body '{"status":"completed","cost_cents":4500,"order_id":"gid://shopify/Order/123","shop":"paybond-agent-commerce-dev.myshopify.com"}' \
--format tableThis smoke path validates:
- the operation name (
commerce.checkout) - the spend boundary (
requested_spend_cents) - evidence shape (
cost_and_completion)
Non-negotiable: binding metadata
Every agent-initiated checkout must write binding metadata into Shopify so the async webhook path can resolve tenant scope:
paybond_intent_idtenant_id
For UCP Checkout MCP, write these into note_attributes (or cart attributes that propagate to orders). Agents must not hold Shopify offline access tokens (Admin API credentials). Those are tenant-owned and stored in Paybond settlement configuration.
Wire middleware (agent-agnostic)
Use Paybond Kit middleware around your Shopify checkout tool. The tool must:
- create checkout/order (UCP/MCP or other Shopify surface)
- inject binding metadata
- return a completion payload suitable for evidence
TypeScript sketch:
import { Paybond } from "@paybond/kit";
type CheckoutArgs = Readonly<{
tenantId: string;
intentId: string;
amountCents: number;
shopDomain: string;
lineItems: ReadonlyArray<{ variantId: string; quantity: number }>;
}>;
type CheckoutResult = Readonly<{
status: "completed" | "requires_escalation" | "failed";
cost_cents: number;
order_id?: string;
shop: string;
continue_url?: string;
}>;
async function checkoutWithUcp(args: CheckoutArgs): Promise<CheckoutResult> {
// Important: bind to Shopify metadata for webhook preflight.
const noteAttributes = [
{ name: "paybond_intent_id", value: args.intentId },
{ name: "tenant_id", value: args.tenantId },
];
// Pseudocode: call UCP Checkout MCP / storefront flow here.
// Return a completion envelope compatible with cost_and_completion evidence.
return {
status: "completed",
cost_cents: args.amountCents,
order_id: "gid://shopify/Order/123",
shop: args.shopDomain,
};
}
const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! });
const instrumented = await paybond.instrument({
policy: "./paybond.policy.yaml",
tools: {
"commerce.checkout": checkoutWithUcp,
},
});
Production checklist
- Configure settlement rails in the console — Configure Shopify settlement. For conditional settlement, the merchant must enable manual payment capture in Shopify Admin.
- Create and fund an intent — Fund intents by rail.
- Bind middleware per request with
instrumented.bind({ intentId, capabilityToken })(or a lazy context provider). - Never pass tenant ids from unauthenticated input — tenant scope comes from the Paybond API key.
- Inject
paybond_intent_idandtenant_idinto Shopify checkout metadata on every call.
Related guides
- Paybond with Shopify agentic commerce — partner brief and integration surfaces
- Configure Shopify settlement — tenant-admin prerequisites (manual capture + shop link)
- Settlement with payment providers — provider touchpoints, webhooks, capture timing