paybondpaybond
Sign in

Recipe · Spend controls

Multi-provider commerce checkout

Route one commerce.checkout tool across Shopify, Stripe, and Zinc adapters with Paybond spend limits, session binding, and signed completion evidence.

  1. 1Wire
  2. 2Authorize
  3. 3Smoke

You'll build

One guarded checkout tool with per-provider adapters and cost_and_completion evidence

Before you start

  • paybond login
  • commerce.checkout
  • ~5 min

In short: when an agent may check out through more than one merchant adapter, use Kit’s multi-provider commerce router. Paybond stays the authorize → prove → release → receipt layer — not a buy-anything HTTP proxy.

Shopify-only vs multi-provider

HelperUse when
instrumentShopifyCheckout / paybond_kit.shopifyYou only ever check out on Shopify
instrumentCommerceCheckout / paybond_kit.commerceOne commerce.checkout tool routes across Shopify, Stripe, and/or Zinc

Both inject tenant_id and paybond_intent_id from the Paybond session binding — never from unauthenticated tool args.

Scaffold

Terminal
Terminal commandSwipe to inspect long lines
# TypeScript
paybond init --template commerce-checkout-agent
cp .env.example .env.local
paybond login
npm install
npm run smoke

# Python
paybond init --template commerce-checkout-agent --language python
cp .env.example .env.local
paybond login
uv sync   # or: pip install -e .
paybond agent sandbox smoke --policy-file paybond.policy.yaml --operation commerce.checkout --requested-spend-cents 4500 --evidence-preset cost_and_completion --result-body '{"status":"completed","cost_cents":4500,"provider":"zinc","order_id":"zinc_sandbox_ord_amazon_B00SMOKE"}' --format json
# Or demo main: python app.py

Wire middleware

Prefer instrumentCommerceCheckout so session binding is injected on every call. Tenant and intent IDs come from the Paybond session — never from tool args.

paybond-session.ts

TS
Code exampleSwipe to inspect long lines
import { Paybond } from "@paybond/kit";
import {
  createShopifyCommerceProvider,
  createStripeCommerceProvider,
  createZincCommerceProvider,
  instrumentCommerceCheckout,
} from "@paybond/kit/commerce";

const shopify = createShopifyCommerceProvider({
  executeCheckout: async (input) => {
    // UCP / storefront with input.checkoutPayload (note_attributes already bound).
    return {
      status: "completed",
      cost_cents: input.amountCents,
      order_id: "gid://shopify/Order/123",
      shop: input.shopDomain,
    };
  },
});

const stripe = createStripeCommerceProvider({
  executeCheckout: async (input) => {
    // PaymentIntent create/confirm with input.metadata (tenant + intent bound).
    return {
      status: "completed",
      cost_cents: input.amountCents,
      payment_intent_id: "pi_test",
    };
  },
});

const zinc = createZincCommerceProvider({ mode: "sandbox" });

const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! });

const instrumented = await instrumentCommerceCheckout(paybond, {
  policy: "./paybond.policy.yaml",
  providers: { shopify, stripe, zinc },
  defaultProvider: "shopify",
});

instrumented.bindingRef.tenantId = tenantId;
instrumented.bindingRef.intentId = intentId;
await instrumented.bind({ intentId, capabilityToken });

Tool args select the adapter with provider (defaults to defaultProvider). Provider payloads live under shopify, stripe, or zinc.

Policy

tools:
  commerce.checkout:
    side_effecting: true
    max_spend_cents: 10000
    evidence_preset: cost_and_completion

paybond policy init --preset shopping scaffolds the same shape.

Per-provider binding

ProviderWhat gets stampedYour job
ShopifyCheckout note_attributes (tenant_id, paybond_intent_id)Call UCP/storefront with the stamped payload
StripePaymentIntent metadata (+ optional settlement rail)Create/confirm with the stamped metadata
ZincSession ids on the adapter requestUse sandbox offline, or pass a live httpClient