paybondpaybond
Sign in

Protect Shopify payments from agents

Guard Shopify UCP/Checkout tools with Paybond middleware — bind intents to note_attributes, treat order webhooks as funding gates, and submit evidence before capture.

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
Terminal commandSwipe to inspect long lines
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 table

This 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_id
  • tenant_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

  1. Configure settlement rails in the console — Configure Shopify settlement. For conditional settlement, the merchant must enable manual payment capture in Shopify Admin.
  2. Create and fund an intent — Fund intents by rail.
  3. Bind middleware per request with instrumented.bind({ intentId, capabilityToken }) (or a lazy context provider).
  4. Never pass tenant ids from unauthenticated input — tenant scope comes from the Paybond API key.
  5. Inject paybond_intent_id and tenant_id into Shopify checkout metadata on every call.