@paybond/kit 0.12.0 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/evidence.d.ts +10 -0
- package/dist/agent/evidence.js +16 -0
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/interceptor.js +4 -0
- package/dist/agent/receipt-client.d.ts +16 -1
- package/dist/agent/run.d.ts +3 -0
- package/dist/agent/run.js +1 -1
- package/dist/agent-mandate.d.ts +103 -0
- package/dist/agent-mandate.js +423 -0
- package/dist/agent-receipt-external-attestations.d.ts +27 -0
- package/dist/agent-receipt-external-attestations.js +61 -0
- package/dist/agent-receipt-pdf-export.d.ts +43 -0
- package/dist/agent-receipt-pdf-export.js +109 -0
- package/dist/agent-receipt-prevalidate.d.ts +7 -0
- package/dist/agent-receipt-prevalidate.js +63 -0
- package/dist/agent-receipt.d.ts +21 -1
- package/dist/agent-receipt.js +63 -4
- package/dist/cli/command-spec.js +50 -1
- package/dist/cli/commands/dev.js +10 -3
- package/dist/cli/commands/setup.js +19 -1
- package/dist/cli/commands/shopify.d.ts +53 -0
- package/dist/cli/commands/shopify.js +808 -0
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +13 -3
- package/dist/cli/router.js +15 -1
- package/dist/commerce-binding.d.ts +59 -0
- package/dist/commerce-binding.js +129 -0
- package/dist/doctor-completion.d.ts +16 -0
- package/dist/doctor-completion.js +157 -1
- package/dist/index.d.ts +27 -2
- package/dist/index.js +43 -7
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +38 -11
- package/dist/policy/presets.d.ts +1 -1
- package/dist/policy/presets.js +1 -0
- package/dist/project-init.d.ts +1 -1
- package/dist/project-init.js +1 -0
- package/dist/protocol-receipt.d.ts +129 -0
- package/dist/protocol-receipt.js +620 -0
- package/dist/shopify/checkout.d.ts +29 -0
- package/dist/shopify/checkout.js +79 -0
- package/dist/shopify/evidence.d.ts +13 -0
- package/dist/shopify/evidence.js +85 -0
- package/dist/shopify/index.d.ts +8 -0
- package/dist/shopify/index.js +6 -0
- package/dist/shopify/instrument.d.ts +61 -0
- package/dist/shopify/instrument.js +52 -0
- package/dist/shopify/order.d.ts +8 -0
- package/dist/shopify/order.js +110 -0
- package/dist/shopify/types.d.ts +82 -0
- package/dist/shopify/types.js +4 -0
- package/dist/solutions/catalog.d.ts +1 -1
- package/dist/solutions/catalog.js +1 -1
- package/dist/stripe-commerce/evidence.d.ts +13 -0
- package/dist/stripe-commerce/evidence.js +164 -0
- package/dist/stripe-commerce/index.d.ts +3 -0
- package/dist/stripe-commerce/index.js +2 -0
- package/dist/stripe-commerce/metadata.d.ts +11 -0
- package/dist/stripe-commerce/metadata.js +34 -0
- package/dist/stripe-commerce/types.d.ts +38 -0
- package/dist/stripe-commerce/types.js +1 -0
- package/dist/template-init.d.ts +1 -1
- package/dist/template-init.js +6 -1
- package/package.json +1 -1
- package/policy/presets/stripe-commerce.yaml +19 -0
- package/solutions/stripe-commerce.json +19 -0
- package/templates/manifest.json +82 -10
- package/templates/openai-shopping-agent/package-lock.json +10 -10
- package/templates/openai-shopping-agent/src/paybond.config.ts +1 -1
- package/templates/paybond-aws-operator/package-lock.json +7 -7
- package/templates/paybond-aws-operator/src/paybond.config.ts +1 -1
- package/templates/paybond-claude-agents-demo/package-lock.json +7 -7
- package/templates/paybond-claude-agents-demo/src/paybond.config.ts +1 -1
- package/templates/paybond-mastra-travel-agent/package-lock.json +30 -30
- package/templates/paybond-mastra-travel-agent/src/paybond.config.ts +1 -1
- package/templates/paybond-mcp-coding-agent/package-lock.json +7 -7
- package/templates/paybond-mcp-coding-agent/src/paybond.config.ts +1 -1
- package/templates/paybond-openai-agents-demo/package-lock.json +10 -10
- package/templates/paybond-openai-agents-demo/src/paybond.config.ts +1 -1
- package/templates/paybond-procurement-agent/package-lock.json +7 -7
- package/templates/paybond-procurement-agent/src/paybond.config.ts +1 -1
- package/templates/paybond-shopify-shopping-agent/.env.example +3 -0
- package/templates/paybond-shopify-shopping-agent/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-shopify-shopping-agent/LICENSE +201 -0
- package/templates/paybond-shopify-shopping-agent/README.md +29 -0
- package/templates/paybond-shopify-shopping-agent/package-lock.json +223 -0
- package/templates/paybond-shopify-shopping-agent/package.json +20 -0
- package/templates/paybond-shopify-shopping-agent/paybond.policy.yaml +22 -0
- package/templates/paybond-shopify-shopping-agent/src/index.ts +54 -0
- package/templates/paybond-shopify-shopping-agent/src/paybond.config.ts +51 -0
- package/templates/paybond-shopify-shopping-agent/tsconfig.json +13 -0
- package/templates/paybond-stripe-agent-demo/.env.example +9 -0
- package/templates/paybond-stripe-agent-demo/.github/workflows/smoke.yml +20 -0
- package/templates/paybond-stripe-agent-demo/LICENSE +201 -0
- package/templates/paybond-stripe-agent-demo/README.md +50 -0
- package/templates/paybond-stripe-agent-demo/package-lock.json +223 -0
- package/templates/paybond-stripe-agent-demo/package.json +20 -0
- package/templates/paybond-stripe-agent-demo/paybond.policy.yaml +22 -0
- package/templates/paybond-stripe-agent-demo/src/charge-customer.ts +237 -0
- package/templates/paybond-stripe-agent-demo/src/index.ts +78 -0
- package/templates/paybond-stripe-agent-demo/src/paybond.config.ts +51 -0
- package/templates/paybond-stripe-agent-demo/tsconfig.json +13 -0
- package/templates/paybond-travel-agent/package-lock.json +13 -13
- package/templates/paybond-travel-agent/src/paybond.config.ts +1 -1
- package/templates/paybond-vercel-shopping-agent/package-lock.json +7 -7
- package/templates/paybond-vercel-shopping-agent/src/paybond.config.ts +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { encodeCommerceBindingToShopifyNoteAttributes, } from "../commerce-binding.js";
|
|
2
|
+
import { PAYBOND_UCP_AGENT_PROFILE_URL } from "./types.js";
|
|
3
|
+
function requireNonEmptyTrimmed(value, label) {
|
|
4
|
+
const trimmed = value.trim();
|
|
5
|
+
if (!trimmed) {
|
|
6
|
+
throw new Error(`shopify checkout: ${label} is required`);
|
|
7
|
+
}
|
|
8
|
+
return trimmed;
|
|
9
|
+
}
|
|
10
|
+
function normalizeVariantGid(variantId) {
|
|
11
|
+
const trimmed = variantId.trim();
|
|
12
|
+
if (!trimmed) {
|
|
13
|
+
throw new Error("shopify checkout: line item variantId is required");
|
|
14
|
+
}
|
|
15
|
+
if (trimmed.startsWith("gid://shopify/ProductVariant/")) {
|
|
16
|
+
return trimmed;
|
|
17
|
+
}
|
|
18
|
+
if (/^\d+$/.test(trimmed)) {
|
|
19
|
+
return `gid://shopify/ProductVariant/${trimmed}`;
|
|
20
|
+
}
|
|
21
|
+
return trimmed;
|
|
22
|
+
}
|
|
23
|
+
function assertPositiveQuantity(quantity) {
|
|
24
|
+
if (!Number.isInteger(quantity) || quantity <= 0) {
|
|
25
|
+
throw new Error("shopify checkout: line item quantity must be a positive integer");
|
|
26
|
+
}
|
|
27
|
+
return quantity;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Converts developer-friendly line items into UCP checkout `line_items`.
|
|
31
|
+
*/
|
|
32
|
+
export function toUcpCheckoutLineItems(lineItems) {
|
|
33
|
+
if (!Array.isArray(lineItems) || lineItems.length === 0) {
|
|
34
|
+
throw new Error("shopify checkout: at least one line item is required");
|
|
35
|
+
}
|
|
36
|
+
return lineItems.map((entry) => ({
|
|
37
|
+
item: { id: normalizeVariantGid(entry.variantId) },
|
|
38
|
+
quantity: assertPositiveQuantity(entry.quantity),
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Builds a UCP checkout create payload with canonical Paybond binding metadata in
|
|
43
|
+
* `note_attributes`.
|
|
44
|
+
*
|
|
45
|
+
* `tenantId` and `intentId` must be sourced from authenticated Paybond session context —
|
|
46
|
+
* never from unauthenticated client input.
|
|
47
|
+
*/
|
|
48
|
+
export function createCheckoutWithBinding(params) {
|
|
49
|
+
const tenantId = requireNonEmptyTrimmed(params.tenantId, "tenantId");
|
|
50
|
+
const intentId = requireNonEmptyTrimmed(params.intentId, "intentId");
|
|
51
|
+
const noteAttributes = encodeCommerceBindingToShopifyNoteAttributes({ tenantId, intentId }, params.existingNoteAttributes);
|
|
52
|
+
const payload = {
|
|
53
|
+
line_items: toUcpCheckoutLineItems(params.lineItems),
|
|
54
|
+
note_attributes: noteAttributes,
|
|
55
|
+
};
|
|
56
|
+
const cartId = params.cartId?.trim();
|
|
57
|
+
if (cartId) {
|
|
58
|
+
payload.cart_id = cartId;
|
|
59
|
+
}
|
|
60
|
+
const profileUrl = (params.agentProfileUrl ?? PAYBOND_UCP_AGENT_PROFILE_URL).trim();
|
|
61
|
+
payload.meta = { profile_url: profileUrl };
|
|
62
|
+
return payload;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Merges binding metadata into an existing UCP checkout mutation payload.
|
|
66
|
+
*
|
|
67
|
+
* Use for `update_checkout` and other checkout mutations so binding cannot be dropped.
|
|
68
|
+
*/
|
|
69
|
+
export function mergeBindingIntoCheckoutPayload(binding, checkoutPayload, agentProfileUrl = PAYBOND_UCP_AGENT_PROFILE_URL) {
|
|
70
|
+
const noteAttributes = encodeCommerceBindingToShopifyNoteAttributes(binding, checkoutPayload.note_attributes);
|
|
71
|
+
return {
|
|
72
|
+
...checkoutPayload,
|
|
73
|
+
note_attributes: noteAttributes,
|
|
74
|
+
meta: {
|
|
75
|
+
...(checkoutPayload.meta ?? {}),
|
|
76
|
+
profile_url: (checkoutPayload.meta?.profile_url ?? agentProfileUrl).trim(),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MapShopifyToolResultToEvidenceOptions, ShopifyCheckoutToolResult } from "./types.js";
|
|
2
|
+
export declare const SHOPIFY_COMMERCE_MAPPER_VERSION = "shopify_commerce_v1";
|
|
3
|
+
/**
|
|
4
|
+
* Rejects Shopify order webhook envelopes — those fund Harbor intents and must not
|
|
5
|
+
* be submitted as tool-completion evidence.
|
|
6
|
+
*/
|
|
7
|
+
export declare function assertNotShopifyFundingWebhook(input: Record<string, unknown>): void;
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes Shopify checkout tool results into completion-catalog evidence fields.
|
|
10
|
+
*
|
|
11
|
+
* Rejects webhook-shaped funding payloads before mapping.
|
|
12
|
+
*/
|
|
13
|
+
export declare function mapShopifyToolResultToEvidence(toolResult: Record<string, unknown>, options: MapShopifyToolResultToEvidenceOptions): Pick<ShopifyCheckoutToolResult, "status" | "cost_cents">;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export const SHOPIFY_COMMERCE_MAPPER_VERSION = "shopify_commerce_v1";
|
|
2
|
+
const SHOPIFY_FUNDING_TOPICS = new Set([
|
|
3
|
+
"orders/create",
|
|
4
|
+
"orders/paid",
|
|
5
|
+
"orders/updated",
|
|
6
|
+
"orders/cancelled",
|
|
7
|
+
"orders/fulfilled",
|
|
8
|
+
"refunds/create",
|
|
9
|
+
]);
|
|
10
|
+
function readObject(value) {
|
|
11
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
function readString(record, ...keys) {
|
|
17
|
+
for (const key of keys) {
|
|
18
|
+
const value = record[key];
|
|
19
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
20
|
+
return value.trim();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
function readNumber(record, ...keys) {
|
|
26
|
+
for (const key of keys) {
|
|
27
|
+
const value = record[key];
|
|
28
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Rejects Shopify order webhook envelopes — those fund Harbor intents and must not
|
|
36
|
+
* be submitted as tool-completion evidence.
|
|
37
|
+
*/
|
|
38
|
+
export function assertNotShopifyFundingWebhook(input) {
|
|
39
|
+
const topic = readString(input, "topic", "X-Shopify-Topic");
|
|
40
|
+
if (topic && SHOPIFY_FUNDING_TOPICS.has(topic)) {
|
|
41
|
+
throw new Error(`${topic} webhooks are funding signals, not tool-completion evidence`);
|
|
42
|
+
}
|
|
43
|
+
if (readString(input, "X-Shopify-Shop-Domain") && readString(input, "X-Shopify-Webhook-Id")) {
|
|
44
|
+
throw new Error("Shopify webhook envelopes are funding signals, not tool-completion evidence");
|
|
45
|
+
}
|
|
46
|
+
const adminGraphqlId = readString(input, "admin_graphql_api_id");
|
|
47
|
+
const orderNumber = readString(input, "order_number");
|
|
48
|
+
if (adminGraphqlId?.includes("gid://shopify/Order/") && orderNumber && !("status" in input)) {
|
|
49
|
+
throw new Error("Shopify order webhook payloads are funding signals, not tool-completion evidence");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function resolveCostCents(record) {
|
|
53
|
+
const cost = readNumber(record, "cost_cents", "costCents", "amount_cents", "amountCents") ??
|
|
54
|
+
readNumber(record, "total_price_cents", "totalPriceCents");
|
|
55
|
+
if (cost === undefined) {
|
|
56
|
+
throw new Error("Shopify tool result missing cost_cents");
|
|
57
|
+
}
|
|
58
|
+
if (!Number.isInteger(cost) || cost < 0) {
|
|
59
|
+
throw new Error("Shopify tool result cost_cents must be a non-negative integer");
|
|
60
|
+
}
|
|
61
|
+
return cost;
|
|
62
|
+
}
|
|
63
|
+
function mapCostAndCompletionEvidence(record) {
|
|
64
|
+
const status = readString(record, "status");
|
|
65
|
+
if (!status) {
|
|
66
|
+
throw new Error("Shopify tool result missing status");
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
status: status,
|
|
70
|
+
cost_cents: resolveCostCents(record),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Normalizes Shopify checkout tool results into completion-catalog evidence fields.
|
|
75
|
+
*
|
|
76
|
+
* Rejects webhook-shaped funding payloads before mapping.
|
|
77
|
+
*/
|
|
78
|
+
export function mapShopifyToolResultToEvidence(toolResult, options) {
|
|
79
|
+
assertNotShopifyFundingWebhook(toolResult);
|
|
80
|
+
const preset = options.preset;
|
|
81
|
+
if (preset === "cost_and_completion") {
|
|
82
|
+
return mapCostAndCompletionEvidence(toolResult);
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`mapShopifyToolResultToEvidence: unsupported preset ${preset}`);
|
|
85
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { PAYBOND_UCP_AGENT_PROFILE_URL, PAYBOND_SHOPIFY_UCP_VERSION } from "./types.js";
|
|
2
|
+
export { SHOPIFY_COMMERCE_MAPPER_VERSION } from "./evidence.js";
|
|
3
|
+
export { createCheckoutWithBinding, mergeBindingIntoCheckoutPayload, toUcpCheckoutLineItems, } from "./checkout.js";
|
|
4
|
+
export { assertNotShopifyFundingWebhook, mapShopifyToolResultToEvidence, } from "./evidence.js";
|
|
5
|
+
export { getOrder } from "./order.js";
|
|
6
|
+
export { createGuardedShopifyCheckoutHandler, instrumentShopifyCheckout, } from "./instrument.js";
|
|
7
|
+
export type { CreateCheckoutWithBindingParams, GetShopifyOrderParams, MapShopifyToolResultToEvidenceOptions, ShopifyCheckoutCreatePayload, ShopifyCheckoutExecuteInput, ShopifyCheckoutLineItemInput, ShopifyCheckoutToolArgs, ShopifyCheckoutToolResult, ShopifyCommerceEvidencePreset, ShopifyOrderSummary, ShopifyUcpCheckoutLineItem, ShopifyUcpFetch, } from "./types.js";
|
|
8
|
+
export type { CreateGuardedShopifyCheckoutHandlerOptions, InstrumentShopifyCheckoutInput, ShopifyCheckoutExecutor, ShopifyCheckoutSessionBinding, } from "./instrument.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { PAYBOND_UCP_AGENT_PROFILE_URL, PAYBOND_SHOPIFY_UCP_VERSION } from "./types.js";
|
|
2
|
+
export { SHOPIFY_COMMERCE_MAPPER_VERSION } from "./evidence.js";
|
|
3
|
+
export { createCheckoutWithBinding, mergeBindingIntoCheckoutPayload, toUcpCheckoutLineItems, } from "./checkout.js";
|
|
4
|
+
export { assertNotShopifyFundingWebhook, mapShopifyToolResultToEvidence, } from "./evidence.js";
|
|
5
|
+
export { getOrder } from "./order.js";
|
|
6
|
+
export { createGuardedShopifyCheckoutHandler, instrumentShopifyCheckout, } from "./instrument.js";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Paybond } from "../index.js";
|
|
2
|
+
import type { PaybondPolicyLoadSource } from "../policy/load.js";
|
|
3
|
+
import type { PaybondInstrumentInput } from "../agent/instrument.js";
|
|
4
|
+
import type { ShopifyCheckoutExecuteInput, ShopifyCheckoutToolArgs, ShopifyCheckoutToolResult } from "./types.js";
|
|
5
|
+
/** Paybond session binding for Shopify checkout — never sourced from client input. */
|
|
6
|
+
export type ShopifyCheckoutSessionBinding = {
|
|
7
|
+
tenantId: string;
|
|
8
|
+
intentId: string;
|
|
9
|
+
};
|
|
10
|
+
export type ShopifyCheckoutExecutor = (input: ShopifyCheckoutExecuteInput) => Promise<ShopifyCheckoutToolResult>;
|
|
11
|
+
export type CreateGuardedShopifyCheckoutHandlerOptions = {
|
|
12
|
+
binding: () => ShopifyCheckoutSessionBinding;
|
|
13
|
+
executeCheckout: ShopifyCheckoutExecutor;
|
|
14
|
+
agentProfileUrl?: string;
|
|
15
|
+
};
|
|
16
|
+
export type InstrumentShopifyCheckoutInput = Omit<PaybondInstrumentInput<{
|
|
17
|
+
"commerce.checkout": (args: ShopifyCheckoutToolArgs) => Promise<ShopifyCheckoutToolResult>;
|
|
18
|
+
}>, "tools"> & {
|
|
19
|
+
policy: PaybondPolicyLoadSource;
|
|
20
|
+
executeCheckout: ShopifyCheckoutExecutor;
|
|
21
|
+
agentProfileUrl?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Mutable binding populated after sandbox bind or production attach.
|
|
24
|
+
* When omitted, a private ref is created for sandbox quickstarts.
|
|
25
|
+
*/
|
|
26
|
+
bindingRef?: ShopifyCheckoutSessionBinding;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Wraps a checkout executor so binding metadata is injected on every call.
|
|
30
|
+
*
|
|
31
|
+
* `tenantId` and `intentId` are read from the Paybond session binding ref — never from tool args.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createGuardedShopifyCheckoutHandler(options: CreateGuardedShopifyCheckoutHandlerOptions): (args: ShopifyCheckoutToolArgs) => Promise<ShopifyCheckoutToolResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Instruments `commerce.checkout` with Paybond middleware and guaranteed binding injection.
|
|
36
|
+
*
|
|
37
|
+
* Returns the standard {@link PaybondInstrumentRuntime} surface from `paybond.instrument()`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function instrumentShopifyCheckout(paybond: Paybond, input: InstrumentShopifyCheckoutInput): Promise<{
|
|
40
|
+
bindingRef: ShopifyCheckoutSessionBinding;
|
|
41
|
+
tools: {
|
|
42
|
+
"commerce.checkout": (args: ShopifyCheckoutToolArgs) => Promise<ShopifyCheckoutToolResult>;
|
|
43
|
+
};
|
|
44
|
+
policy: import("../index.js").PaybondPolicy;
|
|
45
|
+
binding: Extract<import("../index.js").PaybondInstrumentBinding, {
|
|
46
|
+
phase: "deferred";
|
|
47
|
+
} | {
|
|
48
|
+
phase: "lazy";
|
|
49
|
+
}>;
|
|
50
|
+
} | {
|
|
51
|
+
bindingRef: ShopifyCheckoutSessionBinding;
|
|
52
|
+
tools: {
|
|
53
|
+
"commerce.checkout": (args: ShopifyCheckoutToolArgs) => Promise<ShopifyCheckoutToolResult>;
|
|
54
|
+
};
|
|
55
|
+
run: import("../index.js").PaybondAgentRun;
|
|
56
|
+
policy: import("../index.js").PaybondPolicy;
|
|
57
|
+
hooks: import("../index.js").PaybondAgentHooks;
|
|
58
|
+
binding: Extract<import("../index.js").PaybondInstrumentBinding, {
|
|
59
|
+
phase: "bound";
|
|
60
|
+
}>;
|
|
61
|
+
}>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createCheckoutWithBinding } from "./checkout.js";
|
|
2
|
+
import { PAYBOND_UCP_AGENT_PROFILE_URL } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a checkout executor so binding metadata is injected on every call.
|
|
5
|
+
*
|
|
6
|
+
* `tenantId` and `intentId` are read from the Paybond session binding ref — never from tool args.
|
|
7
|
+
*/
|
|
8
|
+
export function createGuardedShopifyCheckoutHandler(options) {
|
|
9
|
+
const profileUrl = (options.agentProfileUrl ?? PAYBOND_UCP_AGENT_PROFILE_URL).trim();
|
|
10
|
+
return async (args) => {
|
|
11
|
+
const { tenantId, intentId } = options.binding();
|
|
12
|
+
if (!tenantId.trim() || !intentId.trim()) {
|
|
13
|
+
throw new Error("Paybond session binding is required before commerce.checkout");
|
|
14
|
+
}
|
|
15
|
+
const checkoutPayload = createCheckoutWithBinding({
|
|
16
|
+
tenantId,
|
|
17
|
+
intentId,
|
|
18
|
+
lineItems: args.lineItems,
|
|
19
|
+
existingNoteAttributes: args.noteAttributes,
|
|
20
|
+
cartId: args.cartId,
|
|
21
|
+
agentProfileUrl: profileUrl,
|
|
22
|
+
});
|
|
23
|
+
return options.executeCheckout({
|
|
24
|
+
...args,
|
|
25
|
+
tenantId,
|
|
26
|
+
intentId,
|
|
27
|
+
checkoutPayload,
|
|
28
|
+
agentProfileUrl: profileUrl,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Instruments `commerce.checkout` with Paybond middleware and guaranteed binding injection.
|
|
34
|
+
*
|
|
35
|
+
* Returns the standard {@link PaybondInstrumentRuntime} surface from `paybond.instrument()`.
|
|
36
|
+
*/
|
|
37
|
+
export async function instrumentShopifyCheckout(paybond, input) {
|
|
38
|
+
const bindingRef = input.bindingRef ?? { tenantId: "", intentId: "" };
|
|
39
|
+
const checkoutHandler = createGuardedShopifyCheckoutHandler({
|
|
40
|
+
binding: () => bindingRef,
|
|
41
|
+
executeCheckout: input.executeCheckout,
|
|
42
|
+
agentProfileUrl: input.agentProfileUrl,
|
|
43
|
+
});
|
|
44
|
+
const { bindingRef: _ignored, executeCheckout: _execute, ...instrumentInput } = input;
|
|
45
|
+
const instrumented = await paybond.instrument({
|
|
46
|
+
...instrumentInput,
|
|
47
|
+
tools: {
|
|
48
|
+
"commerce.checkout": checkoutHandler,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
return { ...instrumented, bindingRef };
|
|
52
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GetShopifyOrderParams, ShopifyOrderSummary } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fetches an order via Shopify UCP Order MCP for reconciliation.
|
|
4
|
+
*
|
|
5
|
+
* Agents must not pass Shopify offline access tokens. This helper uses the UCP transport
|
|
6
|
+
* at `{shop}/api/ucp/mcp` with the Paybond agent profile URL.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getOrder(params: GetShopifyOrderParams): Promise<ShopifyOrderSummary>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { decodeCommerceBindingFromShopifyNoteAttributes } from "../commerce-binding.js";
|
|
2
|
+
import { PAYBOND_SHOPIFY_UCP_VERSION, PAYBOND_UCP_AGENT_PROFILE_URL } from "./types.js";
|
|
3
|
+
function requireShopDomain(shopDomain) {
|
|
4
|
+
const trimmed = shopDomain.trim();
|
|
5
|
+
if (!trimmed) {
|
|
6
|
+
throw new Error("shopify order: shopDomain is required");
|
|
7
|
+
}
|
|
8
|
+
return trimmed.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
9
|
+
}
|
|
10
|
+
function normalizeOrderGid(orderId) {
|
|
11
|
+
const trimmed = orderId.trim();
|
|
12
|
+
if (!trimmed) {
|
|
13
|
+
throw new Error("shopify order: orderId is required");
|
|
14
|
+
}
|
|
15
|
+
if (trimmed.startsWith("gid://shopify/Order/")) {
|
|
16
|
+
return trimmed;
|
|
17
|
+
}
|
|
18
|
+
if (/^\d+$/.test(trimmed)) {
|
|
19
|
+
return `gid://shopify/Order/${trimmed}`;
|
|
20
|
+
}
|
|
21
|
+
return trimmed;
|
|
22
|
+
}
|
|
23
|
+
function shopOrigin(shopDomain) {
|
|
24
|
+
const host = requireShopDomain(shopDomain);
|
|
25
|
+
return host.includes(".") ? `https://${host}` : `https://${host}.myshopify.com`;
|
|
26
|
+
}
|
|
27
|
+
function defaultFetchUcp(url, init) {
|
|
28
|
+
return fetch(url, init);
|
|
29
|
+
}
|
|
30
|
+
function readNoteAttributes(value) {
|
|
31
|
+
if (!Array.isArray(value)) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const attrs = [];
|
|
35
|
+
for (const entry of value) {
|
|
36
|
+
if (!entry || typeof entry !== "object") {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const name = entry.name;
|
|
40
|
+
const attrValue = entry.value;
|
|
41
|
+
if (typeof name === "string" && typeof attrValue === "string") {
|
|
42
|
+
attrs.push({ name, value: attrValue });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return attrs;
|
|
46
|
+
}
|
|
47
|
+
function orderSummaryFromPayload(shop, orderId, payload) {
|
|
48
|
+
const noteAttributes = readNoteAttributes(payload.note_attributes);
|
|
49
|
+
const binding = decodeCommerceBindingFromShopifyNoteAttributes(noteAttributes);
|
|
50
|
+
return {
|
|
51
|
+
order_id: readString(payload, "id", "order_id", "admin_graphql_api_id") ?? orderId,
|
|
52
|
+
shop,
|
|
53
|
+
financial_status: readString(payload, "financial_status"),
|
|
54
|
+
note_attributes: noteAttributes,
|
|
55
|
+
binding: {
|
|
56
|
+
tenant_id: binding?.tenantId ?? null,
|
|
57
|
+
paybond_intent_id: binding?.intentId ?? null,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function readString(record, ...keys) {
|
|
62
|
+
for (const key of keys) {
|
|
63
|
+
const value = record[key];
|
|
64
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
65
|
+
return value.trim();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Fetches an order via Shopify UCP Order MCP for reconciliation.
|
|
72
|
+
*
|
|
73
|
+
* Agents must not pass Shopify offline access tokens. This helper uses the UCP transport
|
|
74
|
+
* at `{shop}/api/ucp/mcp` with the Paybond agent profile URL.
|
|
75
|
+
*/
|
|
76
|
+
export async function getOrder(params) {
|
|
77
|
+
const shop = requireShopDomain(params.shopDomain);
|
|
78
|
+
const orderId = normalizeOrderGid(params.orderId);
|
|
79
|
+
const profileUrl = (params.agentProfileUrl ?? PAYBOND_UCP_AGENT_PROFILE_URL).trim();
|
|
80
|
+
const fetchUcp = params.fetchUcp ?? defaultFetchUcp;
|
|
81
|
+
const response = await fetchUcp(`${shopOrigin(shop)}/api/ucp/mcp`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"content-type": "application/json",
|
|
85
|
+
"UCP-Agent": profileUrl,
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
jsonrpc: "2.0",
|
|
89
|
+
id: "paybond-kit-get-order",
|
|
90
|
+
method: "order/get",
|
|
91
|
+
params: {
|
|
92
|
+
order_id: orderId,
|
|
93
|
+
meta: {
|
|
94
|
+
profile_url: profileUrl,
|
|
95
|
+
ucp_version: PAYBOND_SHOPIFY_UCP_VERSION,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
const body = (await response.json());
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
const message = readString(body, "message", "error") ?? `UCP order/get failed (${response.status})`;
|
|
103
|
+
throw new Error(message);
|
|
104
|
+
}
|
|
105
|
+
const result = body.result;
|
|
106
|
+
if (!result || typeof result !== "object") {
|
|
107
|
+
throw new Error("shopify order: UCP response missing result");
|
|
108
|
+
}
|
|
109
|
+
return orderSummaryFromPayload(shop, orderId, result);
|
|
110
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ShopifyNoteAttribute } from "../commerce-binding.js";
|
|
2
|
+
/** Canonical Paybond UCP agent profile URL for Shopify partner registration. */
|
|
3
|
+
export declare const PAYBOND_UCP_AGENT_PROFILE_URL = "https://paybond.ai/.well-known/ucp/profile.json";
|
|
4
|
+
/** Default UCP protocol version supported by Paybond Kit Shopify helpers. */
|
|
5
|
+
export declare const PAYBOND_SHOPIFY_UCP_VERSION = "2026-04-08";
|
|
6
|
+
/** Shopify commerce evidence presets supported by {@link mapShopifyToolResultToEvidence}. */
|
|
7
|
+
export type ShopifyCommerceEvidencePreset = "cost_and_completion";
|
|
8
|
+
export type MapShopifyToolResultToEvidenceOptions = {
|
|
9
|
+
preset: ShopifyCommerceEvidencePreset;
|
|
10
|
+
};
|
|
11
|
+
/** Line item input for {@link createCheckoutWithBinding}. */
|
|
12
|
+
export type ShopifyCheckoutLineItemInput = {
|
|
13
|
+
variantId: string;
|
|
14
|
+
quantity: number;
|
|
15
|
+
};
|
|
16
|
+
/** UCP checkout line item shape (Shopify Checkout MCP). */
|
|
17
|
+
export type ShopifyUcpCheckoutLineItem = {
|
|
18
|
+
item: {
|
|
19
|
+
id: string;
|
|
20
|
+
};
|
|
21
|
+
quantity: number;
|
|
22
|
+
};
|
|
23
|
+
/** Inputs for {@link createCheckoutWithBinding}. Binding ids must come from Paybond session context. */
|
|
24
|
+
export type CreateCheckoutWithBindingParams = {
|
|
25
|
+
intentId: string;
|
|
26
|
+
tenantId: string;
|
|
27
|
+
lineItems: readonly ShopifyCheckoutLineItemInput[];
|
|
28
|
+
existingNoteAttributes?: readonly ShopifyNoteAttribute[];
|
|
29
|
+
cartId?: string;
|
|
30
|
+
agentProfileUrl?: string;
|
|
31
|
+
};
|
|
32
|
+
/** UCP `create_checkout` payload with binding metadata injected. */
|
|
33
|
+
export type ShopifyCheckoutCreatePayload = {
|
|
34
|
+
line_items: ShopifyUcpCheckoutLineItem[];
|
|
35
|
+
note_attributes: ShopifyNoteAttribute[];
|
|
36
|
+
cart_id?: string;
|
|
37
|
+
meta?: {
|
|
38
|
+
profile_url: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/** Checkout tool completion envelope for `commerce.checkout` evidence mapping. */
|
|
42
|
+
export type ShopifyCheckoutToolResult = {
|
|
43
|
+
status: "completed" | "requires_escalation" | "failed";
|
|
44
|
+
cost_cents: number;
|
|
45
|
+
order_id?: string;
|
|
46
|
+
shop: string;
|
|
47
|
+
continue_url?: string;
|
|
48
|
+
};
|
|
49
|
+
/** Arguments accepted by guarded `commerce.checkout` tool handlers. */
|
|
50
|
+
export type ShopifyCheckoutToolArgs = {
|
|
51
|
+
shopDomain: string;
|
|
52
|
+
lineItems: readonly ShopifyCheckoutLineItemInput[];
|
|
53
|
+
amountCents: number;
|
|
54
|
+
cartId?: string;
|
|
55
|
+
noteAttributes?: readonly ShopifyNoteAttribute[];
|
|
56
|
+
};
|
|
57
|
+
/** Arguments passed to the checkout executor after binding injection. */
|
|
58
|
+
export type ShopifyCheckoutExecuteInput = ShopifyCheckoutToolArgs & {
|
|
59
|
+
tenantId: string;
|
|
60
|
+
intentId: string;
|
|
61
|
+
checkoutPayload: ShopifyCheckoutCreatePayload;
|
|
62
|
+
agentProfileUrl: string;
|
|
63
|
+
};
|
|
64
|
+
export type ShopifyUcpFetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
65
|
+
/** Inputs for {@link getOrder}. Agents must not pass Shopify offline access tokens. */
|
|
66
|
+
export type GetShopifyOrderParams = {
|
|
67
|
+
shopDomain: string;
|
|
68
|
+
orderId: string;
|
|
69
|
+
agentProfileUrl?: string;
|
|
70
|
+
fetchUcp?: ShopifyUcpFetch;
|
|
71
|
+
};
|
|
72
|
+
/** Normalized order view for reconciliation helpers. */
|
|
73
|
+
export type ShopifyOrderSummary = {
|
|
74
|
+
order_id: string;
|
|
75
|
+
shop: string;
|
|
76
|
+
financial_status?: string;
|
|
77
|
+
note_attributes: ShopifyNoteAttribute[];
|
|
78
|
+
binding: {
|
|
79
|
+
tenant_id: string | null;
|
|
80
|
+
paybond_intent_id: string | null;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Canonical Paybond UCP agent profile URL for Shopify partner registration. */
|
|
2
|
+
export const PAYBOND_UCP_AGENT_PROFILE_URL = "https://paybond.ai/.well-known/ucp/profile.json";
|
|
3
|
+
/** Default UCP protocol version supported by Paybond Kit Shopify helpers. */
|
|
4
|
+
export const PAYBOND_SHOPIFY_UCP_VERSION = "2026-04-08";
|
|
@@ -21,7 +21,7 @@ export type SolutionSmokeDefaults = {
|
|
|
21
21
|
evidencePreset: string;
|
|
22
22
|
resultBody: Record<string, unknown>;
|
|
23
23
|
};
|
|
24
|
-
declare const KNOWN_SOLUTION_IDS: readonly ["travel", "shopping", "saas", "aws"];
|
|
24
|
+
declare const KNOWN_SOLUTION_IDS: readonly ["travel", "shopping", "saas", "aws", "stripe-commerce"];
|
|
25
25
|
export type SolutionId = (typeof KNOWN_SOLUTION_IDS)[number];
|
|
26
26
|
/** True when `value` is a bundled solution id (travel, shopping, saas, aws). */
|
|
27
27
|
export declare function isKnownSolutionId(value: string): value is SolutionId;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
const KNOWN_SOLUTION_IDS = ["travel", "shopping", "saas", "aws"];
|
|
4
|
+
const KNOWN_SOLUTION_IDS = ["travel", "shopping", "saas", "aws", "stripe-commerce"];
|
|
5
5
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
function solutionCandidatePaths(solutionId) {
|
|
7
7
|
const fileName = `${solutionId}.json`;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CostAndCompletionEvidence, MapStripeToolResultToEvidenceOptions, StripeChargeVendorEvidence } from "./types.js";
|
|
2
|
+
export declare const STRIPE_COMMERCE_MAPPER_VERSION = "stripe_commerce_v1";
|
|
3
|
+
/**
|
|
4
|
+
* Rejects Stripe funding webhook envelopes — those fund Harbor intents and must not
|
|
5
|
+
* be submitted as tool-completion evidence.
|
|
6
|
+
*/
|
|
7
|
+
export declare function assertNotStripeFundingWebhook(input: Record<string, unknown>): void;
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes Stripe SDK tool results into completion-catalog evidence fields.
|
|
10
|
+
*
|
|
11
|
+
* Rejects webhook-shaped funding payloads before mapping.
|
|
12
|
+
*/
|
|
13
|
+
export declare function mapStripeToolResultToEvidence(toolResult: Record<string, unknown>, options: MapStripeToolResultToEvidenceOptions): StripeChargeVendorEvidence | CostAndCompletionEvidence;
|