paybondpaybond
Sign in

Multi-provider commerce checkout

Route commerce.checkout across Shopify, Stripe, and Zinc adapters while Paybond stays the authorize → prove → release → receipt control layer.

Paybond Kit is a trust and control layer (authorize → prove → release → receipt). It is not a buy-anything HTTP proxy and does not replace merchant storefronts or cart APIs.

Use the multi-provider commerce surface when one agent tool (commerce.checkout) may settle through more than one merchant adapter. Prefer the Shopify-only helper when you only ever check out on Shopify.

When to use which helper

HelperUse when
instrumentShopifyCheckout / @paybond/kit/shopify / paybond_kit.shopifySingle-provider Shopify UCP checkout with binding injection
instrumentCommerceCheckout / @paybond/kit/commerce / paybond_kit.commerceOne tool routes across Shopify, Stripe, and/or Zinc adapters

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

What not to do

  • Do not build a Gateway “buy-proxy” that accepts arbitrary retailer URLs and purchases on behalf of tenants.
  • Do not pass tenantId / tenant_id or intentId / intent_id in tool arguments — those come from authenticated Paybond bind/attach.
  • Do not accept free-form checkout amounts from the LLM when a catalog price is available — resolve amounts server-side.

Scaffold

# TypeScript
paybond init --template commerce-checkout-agent

# Python (same product surface; Python Kit defaults to this twin)
paybond init --template commerce-checkout-agent --language python

Creates an agent with Shopify and Stripe mock executors, Zinc sandbox, paybond.policy.yaml (commerce.checkout + cost_and_completion), and a smoke path (npm run smoke for TypeScript, or uv sync / pip install -e . plus paybond agent sandbox smoke … for the Python twin — pure Python, no Node).

TypeScript

import { Paybond } from "@paybond/kit";
import {
  createShopifyCommerceProvider,
  createStripeCommerceProvider,
  createZincCommerceProvider,
  instrumentCommerceCheckout,
} from "@paybond/kit/commerce";

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

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

// Offline mock for e2e — live Zinc HTTP is optional via httpClient.
const zinc = createZincCommerceProvider({ mode: "sandbox" });

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

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

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

Or wire the router yourself:

import { createCommerceCheckoutRouter } from "@paybond/kit/commerce";

const checkout = createCommerceCheckoutRouter({
  providers: { shopify, stripe, zinc },
  defaultProvider: "shopify",
  binding: () => bindingRef,
});

await paybond.instrument({
  policy: "shopping",
  tools: { "commerce.checkout": checkout },
});

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

Python

import os

from paybond_kit import Paybond
from paybond_kit.commerce import (
    create_shopify_commerce_provider,
    create_stripe_commerce_provider,
    create_zinc_commerce_provider,
    instrument_commerce_checkout,
)

async def run() -> None:
    async def execute_shopify(input_payload):
        # Call UCP / storefront with input_payload["checkout_payload"] (binding stamped).
        return {
            "status": "completed",
            "cost_cents": input_payload["amount_cents"],
            "order_id": "gid://shopify/Order/123",
            "shop": input_payload["shop_domain"],
        }

    async def execute_stripe(input_payload):
        # Create/confirm PaymentIntent with input_payload["metadata"].
        return {
            "status": "completed",
            "cost_cents": input_payload["amount_cents"],
            "payment_intent_id": "pi_test",
        }

    shopify = create_shopify_commerce_provider(execute_checkout=execute_shopify)
    stripe = create_stripe_commerce_provider(execute_checkout=execute_stripe)
    zinc = create_zinc_commerce_provider(mode="sandbox")

    paybond = await Paybond.open(api_key=os.environ["PAYBOND_API_KEY"])
    instrumented, binding_ref = await instrument_commerce_checkout(
        paybond,
        policy="shopping",
        providers={"shopify": shopify, "stripe": stripe, "zinc": zinc},
        default_provider="shopify",
    )

    binding_ref["tenant_id"] = tenant_id
    binding_ref["intent_id"] = intent_id
    await instrumented.bind(intent_id=intent_id, capability_token=capability_token)

Router-only wiring:

from paybond_kit.commerce import create_commerce_checkout_router

checkout = create_commerce_checkout_router(
    providers={"shopify": shopify, "stripe": stripe, "zinc": zinc},
    default_provider="shopify",
    binding=lambda: binding_ref,
)

await paybond.instrument(
    policy="shopping",
    tools={"commerce.checkout": checkout},
)

Policy snippet

version: 1
name: commerce-checkout-agent-v1
default_deny: true

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

intent:
  allowed_tools:
    - commerce.checkout
  budget:
    currency: usd
    max_spend_usd: 100

Scaffold with paybond policy init --preset shopping --out paybond.policy.yaml.

Per-provider notes

ProviderBinding injectionSetup
Shopifynote_attributes via createCheckoutWithBinding / create_checkout_with_bindingPass executeCheckout that calls UCP/storefront with the stamped payload
StripePaymentIntent metadata (tenant_id, paybond_intent_id, optional rail)Pass executeCheckout that creates/confirms with input.metadata
ZincSession ids on the adapter request onlyDefault mode: "sandbox" (offline). Live: mode: "live" + pluggable httpClient / http_client

Zinc

  • Sandbox (default): deterministic mock orders, no network — use for instrument and smoke tests. Sandbox cost_cents is product-derived (Σ price_cents × quantity); agent amount_cents / amountCents must match and is never trusted alone. Optional max_price_cents caps the derived cost.
  • Live: pass mode: "live" (exact) and a pluggable HTTP client. Kit does not embed Zinc credentials or a Gateway buy-proxy.