@paybond/kit 0.11.11 → 0.12.1

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.
Files changed (132) hide show
  1. package/completion-presets/catalog.json +10 -5
  2. package/completion-presets/catalog.sha256 +1 -1
  3. package/dist/agent/evidence.d.ts +10 -0
  4. package/dist/agent/evidence.js +16 -0
  5. package/dist/agent/index.d.ts +3 -2
  6. package/dist/agent/index.js +2 -1
  7. package/dist/agent/interceptor.d.ts +9 -0
  8. package/dist/agent/interceptor.js +181 -1
  9. package/dist/agent/receipt-client.d.ts +64 -0
  10. package/dist/agent/receipt-client.js +45 -0
  11. package/dist/agent/registry.js +1 -0
  12. package/dist/agent/run.d.ts +5 -0
  13. package/dist/agent/run.js +52 -0
  14. package/dist/agent/types.d.ts +70 -0
  15. package/dist/agent-mandate.d.ts +103 -0
  16. package/dist/agent-mandate.js +423 -0
  17. package/dist/agent-receipt-external-attestations.d.ts +56 -0
  18. package/dist/agent-receipt-external-attestations.js +185 -0
  19. package/dist/agent-receipt-pdf-export.d.ts +43 -0
  20. package/dist/agent-receipt-pdf-export.js +109 -0
  21. package/dist/agent-receipt-prevalidate.d.ts +7 -0
  22. package/dist/agent-receipt-prevalidate.js +63 -0
  23. package/dist/agent-receipt.d.ts +154 -0
  24. package/dist/agent-receipt.js +639 -0
  25. package/dist/audit/exports.d.ts +15 -1
  26. package/dist/audit/exports.js +55 -8
  27. package/dist/audit/index.d.ts +2 -2
  28. package/dist/audit/wire.d.ts +12 -0
  29. package/dist/audit/wire.js +13 -1
  30. package/dist/cli/command-spec.js +54 -3
  31. package/dist/cli/commands/dev.js +10 -3
  32. package/dist/cli/commands/discovery.js +17 -6
  33. package/dist/cli/commands/setup.js +19 -1
  34. package/dist/cli/commands/shopify.d.ts +53 -0
  35. package/dist/cli/commands/shopify.js +808 -0
  36. package/dist/cli/help.d.ts +1 -1
  37. package/dist/cli/help.js +15 -5
  38. package/dist/cli/router.js +15 -1
  39. package/dist/commerce-binding.d.ts +59 -0
  40. package/dist/commerce-binding.js +129 -0
  41. package/dist/completion-catalog-digest.d.ts +1 -1
  42. package/dist/completion-catalog-digest.js +1 -1
  43. package/dist/doctor-completion.d.ts +16 -0
  44. package/dist/doctor-completion.js +157 -1
  45. package/dist/index.d.ts +108 -7
  46. package/dist/index.js +221 -18
  47. package/dist/mcp-receipt-resource.d.ts +10 -0
  48. package/dist/mcp-receipt-resource.js +32 -0
  49. package/dist/mcp-server.d.ts +7 -0
  50. package/dist/mcp-server.js +81 -1
  51. package/dist/mpp-commercial.d.ts +19 -0
  52. package/dist/mpp-commercial.js +34 -0
  53. package/dist/mpp-funding.d.ts +71 -0
  54. package/dist/mpp-funding.js +192 -0
  55. package/dist/payment-transport.d.ts +30 -0
  56. package/dist/payment-transport.js +56 -0
  57. package/dist/policy/intent-spec.js +2 -0
  58. package/dist/policy/presets.d.ts +1 -1
  59. package/dist/policy/presets.js +1 -0
  60. package/dist/principal-intent.d.ts +1 -1
  61. package/dist/principal-intent.js +4 -1
  62. package/dist/project-init.d.ts +1 -1
  63. package/dist/project-init.js +1 -0
  64. package/dist/protocol-receipt.d.ts +129 -0
  65. package/dist/protocol-receipt.js +620 -0
  66. package/dist/shopify/checkout.d.ts +29 -0
  67. package/dist/shopify/checkout.js +79 -0
  68. package/dist/shopify/evidence.d.ts +13 -0
  69. package/dist/shopify/evidence.js +85 -0
  70. package/dist/shopify/index.d.ts +8 -0
  71. package/dist/shopify/index.js +6 -0
  72. package/dist/shopify/instrument.d.ts +61 -0
  73. package/dist/shopify/instrument.js +52 -0
  74. package/dist/shopify/order.d.ts +8 -0
  75. package/dist/shopify/order.js +110 -0
  76. package/dist/shopify/types.d.ts +82 -0
  77. package/dist/shopify/types.js +4 -0
  78. package/dist/solutions/catalog.d.ts +1 -1
  79. package/dist/solutions/catalog.js +1 -1
  80. package/dist/stripe-commerce/evidence.d.ts +13 -0
  81. package/dist/stripe-commerce/evidence.js +164 -0
  82. package/dist/stripe-commerce/index.d.ts +3 -0
  83. package/dist/stripe-commerce/index.js +2 -0
  84. package/dist/stripe-commerce/metadata.d.ts +11 -0
  85. package/dist/stripe-commerce/metadata.js +34 -0
  86. package/dist/stripe-commerce/types.d.ts +38 -0
  87. package/dist/stripe-commerce/types.js +1 -0
  88. package/dist/template-init.d.ts +1 -1
  89. package/dist/template-init.js +6 -1
  90. package/package.json +1 -1
  91. package/policy/presets/stripe-commerce.yaml +19 -0
  92. package/solutions/stripe-commerce.json +19 -0
  93. package/templates/manifest.json +82 -10
  94. package/templates/openai-shopping-agent/package-lock.json +13 -13
  95. package/templates/openai-shopping-agent/src/paybond.config.ts +1 -1
  96. package/templates/paybond-aws-operator/package-lock.json +7 -7
  97. package/templates/paybond-aws-operator/src/paybond.config.ts +1 -1
  98. package/templates/paybond-claude-agents-demo/package-lock.json +10 -10
  99. package/templates/paybond-claude-agents-demo/src/paybond.config.ts +1 -1
  100. package/templates/paybond-mastra-travel-agent/package-lock.json +33 -33
  101. package/templates/paybond-mastra-travel-agent/src/paybond.config.ts +1 -1
  102. package/templates/paybond-mcp-coding-agent/package-lock.json +7 -7
  103. package/templates/paybond-mcp-coding-agent/src/paybond.config.ts +1 -1
  104. package/templates/paybond-openai-agents-demo/package-lock.json +13 -13
  105. package/templates/paybond-openai-agents-demo/src/paybond.config.ts +1 -1
  106. package/templates/paybond-procurement-agent/package-lock.json +7 -7
  107. package/templates/paybond-procurement-agent/src/paybond.config.ts +1 -1
  108. package/templates/paybond-shopify-shopping-agent/.env.example +3 -0
  109. package/templates/paybond-shopify-shopping-agent/.github/workflows/smoke.yml +20 -0
  110. package/templates/paybond-shopify-shopping-agent/LICENSE +201 -0
  111. package/templates/paybond-shopify-shopping-agent/README.md +29 -0
  112. package/templates/paybond-shopify-shopping-agent/package-lock.json +223 -0
  113. package/templates/paybond-shopify-shopping-agent/package.json +20 -0
  114. package/templates/paybond-shopify-shopping-agent/paybond.policy.yaml +22 -0
  115. package/templates/paybond-shopify-shopping-agent/src/index.ts +54 -0
  116. package/templates/paybond-shopify-shopping-agent/src/paybond.config.ts +51 -0
  117. package/templates/paybond-shopify-shopping-agent/tsconfig.json +13 -0
  118. package/templates/paybond-stripe-agent-demo/.env.example +9 -0
  119. package/templates/paybond-stripe-agent-demo/.github/workflows/smoke.yml +20 -0
  120. package/templates/paybond-stripe-agent-demo/LICENSE +201 -0
  121. package/templates/paybond-stripe-agent-demo/README.md +50 -0
  122. package/templates/paybond-stripe-agent-demo/package-lock.json +223 -0
  123. package/templates/paybond-stripe-agent-demo/package.json +20 -0
  124. package/templates/paybond-stripe-agent-demo/paybond.policy.yaml +22 -0
  125. package/templates/paybond-stripe-agent-demo/src/charge-customer.ts +237 -0
  126. package/templates/paybond-stripe-agent-demo/src/index.ts +78 -0
  127. package/templates/paybond-stripe-agent-demo/src/paybond.config.ts +51 -0
  128. package/templates/paybond-stripe-agent-demo/tsconfig.json +13 -0
  129. package/templates/paybond-travel-agent/package-lock.json +13 -13
  130. package/templates/paybond-travel-agent/src/paybond.config.ts +1 -1
  131. package/templates/paybond-vercel-shopping-agent/package-lock.json +7 -7
  132. package/templates/paybond-vercel-shopping-agent/src/paybond.config.ts +1 -1
@@ -0,0 +1,620 @@
1
+ /**
2
+ * Local verification for protocol-v2 authorization and settlement receipts.
3
+ *
4
+ * Canonical JSON matches the Go gateway `protocolAuthorizationReceiptCanonicalV1` /
5
+ * `protocolSettlementReceiptCanonicalV1` marshalers (fixed struct field order, signing
6
+ * fields stripped, RFC3339Nano UTC timestamps, HTML-safe `\u` escapes), not Kit's generic
7
+ * JCS-style `normalizeJson`. Ported from `go/gateway/internal/protocolv2/receipt.go`.
8
+ */
9
+ import { getPublicKey, sign, verify as ed25519Verify } from "@noble/ed25519";
10
+ import { AGENT_AUTHORIZATION_KIND_PRINCIPAL, AGENT_AUTHORIZATION_KIND_TENANT, CURRENCY_RE, HEX64_RE, HUMAN_PRESENCE_MODE_HUMAN_NOT_PRESENT, HUMAN_PRESENCE_MODE_HUMAN_PRESENT, MAX_TENANT_ID_LEN, SCOPE_TOKEN_RE, TENANT_ID_RE, formatRfc3339NanoUtc, goJsonEscape, hexToBytes, readNumber, readObject, readString, readStringArray, roundUtcToSeconds, sha256Hex, } from "./agent-mandate.js";
11
+ import { ensureEd25519Sha512Sync } from "./ed25519-sync.js";
12
+ export const PROTOCOL_RECEIPT_SCHEMA_VERSION = 1;
13
+ export const PROTOCOL_RECEIPT_VERSION_V1 = "1";
14
+ export const PROTOCOL_RECEIPT_SIGNING_ALGORITHM_ED25519_SHA256 = "ed25519-sha256-json-v1";
15
+ export const PROTOCOL_AUTHORIZATION_RECEIPT_KIND_V1 = "paybond.protocol_authorization_receipt_v1";
16
+ export const PROTOCOL_SETTLEMENT_RECEIPT_KIND_V1 = "paybond.protocol_settlement_receipt_v1";
17
+ export const PROTOCOL_RECEIPT_STATUS_AUTHORIZED = "authorized";
18
+ /** Default protocol source (AP2-aligned) for imported mandates and exported receipts. */
19
+ export const PROTOCOL_SOURCE_AP2 = "ap2";
20
+ /** Stripe Agentic Commerce Protocol (ACP) source identifier for partner receipts. */
21
+ export const PROTOCOL_SOURCE_ACP = "acp";
22
+ /** Stripe Unified Commerce Protocol (UCP) source identifier for partner receipts. */
23
+ export const PROTOCOL_SOURCE_UCP = "ucp";
24
+ const PROTOCOL_SETTLEMENT_TERMINAL_STATES = new Set([
25
+ "released",
26
+ "refunded",
27
+ "resolved_split",
28
+ "escalated_external",
29
+ ]);
30
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
31
+ function readOptionalBool(value) {
32
+ return typeof value === "boolean" ? value : undefined;
33
+ }
34
+ function coerceWire(input) {
35
+ return readObject(input) ?? {};
36
+ }
37
+ function normalizeTenantId(raw, label) {
38
+ const trimmed = raw.trim();
39
+ if (!trimmed) {
40
+ throw new Error(`${label}: tenant: id is missing`);
41
+ }
42
+ if (trimmed.length > MAX_TENANT_ID_LEN) {
43
+ throw new Error(`${label}: tenant: id exceeds max length (${MAX_TENANT_ID_LEN})`);
44
+ }
45
+ if (!TENANT_ID_RE.test(trimmed)) {
46
+ throw new Error(`${label}: tenant: id must match [a-z0-9][a-z0-9._-]* (lowercase alphanumeric, dots, underscores, hyphens)`);
47
+ }
48
+ return trimmed;
49
+ }
50
+ function normalizeScopeSet(raw, field) {
51
+ const seen = new Set();
52
+ const out = [];
53
+ for (const item of raw) {
54
+ const value = item.trim().toLowerCase();
55
+ if (value === "") {
56
+ throw new Error(`${field} contains an empty value`);
57
+ }
58
+ if (!SCOPE_TOKEN_RE.test(value)) {
59
+ throw new Error(`${field} value ${JSON.stringify(value)} is not canonical`);
60
+ }
61
+ if (seen.has(value)) {
62
+ continue;
63
+ }
64
+ seen.add(value);
65
+ out.push(value);
66
+ }
67
+ out.sort();
68
+ return out;
69
+ }
70
+ function parseTimestamp(value, errMsg) {
71
+ if (value instanceof Date) {
72
+ if (Number.isNaN(value.getTime())) {
73
+ throw new Error(errMsg);
74
+ }
75
+ return value;
76
+ }
77
+ if (typeof value === "string") {
78
+ const trimmed = value.trim();
79
+ if (!trimmed) {
80
+ throw new Error(errMsg);
81
+ }
82
+ const parsed = new Date(trimmed);
83
+ if (Number.isNaN(parsed.getTime())) {
84
+ throw new Error(errMsg);
85
+ }
86
+ return parsed;
87
+ }
88
+ throw new Error(errMsg);
89
+ }
90
+ function normalizeTimestamp(value, errMsg) {
91
+ return formatRfc3339NanoUtc(roundUtcToSeconds(parseTimestamp(value, errMsg)));
92
+ }
93
+ function normalizeTransportBinding(raw, label) {
94
+ let sourceProtocol = readString(raw.source_protocol).trim().toLowerCase();
95
+ if (sourceProtocol === "") {
96
+ sourceProtocol = PROTOCOL_SOURCE_AP2;
97
+ }
98
+ if (!SCOPE_TOKEN_RE.test(sourceProtocol)) {
99
+ throw new Error(`${label}: source_protocol ${JSON.stringify(sourceProtocol)} is not canonical`);
100
+ }
101
+ const partnerPlatform = readString(raw.partner_platform).trim();
102
+ const externalAuthorizationId = readString(raw.external_authorization_id).trim();
103
+ const requestId = readString(raw.request_id).trim();
104
+ for (const [field, value] of [
105
+ ["partner_platform", partnerPlatform],
106
+ ["external_authorization_id", externalAuthorizationId],
107
+ ["request_id", requestId],
108
+ ]) {
109
+ if (value.length > 256) {
110
+ throw new Error(`${label}: ${field} must be 256 bytes or fewer`);
111
+ }
112
+ }
113
+ return {
114
+ source_protocol: sourceProtocol,
115
+ partner_platform: partnerPlatform,
116
+ external_authorization_id: externalAuthorizationId,
117
+ request_id: requestId,
118
+ };
119
+ }
120
+ function canonicalTransportBinding(binding) {
121
+ const out = { source_protocol: binding.source_protocol };
122
+ if (binding.partner_platform) {
123
+ out.partner_platform = binding.partner_platform;
124
+ }
125
+ if (binding.external_authorization_id) {
126
+ out.external_authorization_id = binding.external_authorization_id;
127
+ }
128
+ if (binding.request_id) {
129
+ out.request_id = binding.request_id;
130
+ }
131
+ return out;
132
+ }
133
+ /**
134
+ * Validates and canonicalizes an authorization receipt for hashing and signing.
135
+ *
136
+ * Signing fields are lower/trim-normalized but not recomputed here.
137
+ *
138
+ * @throws When structure or field values are invalid.
139
+ */
140
+ export function normalizeProtocolAuthorizationReceiptV1(input) {
141
+ const raw = coerceWire(input);
142
+ const label = "protocol authorization receipt";
143
+ let schemaVersion = readNumber(raw.schema_version);
144
+ if (schemaVersion === 0 || schemaVersion === PROTOCOL_RECEIPT_SCHEMA_VERSION) {
145
+ schemaVersion = PROTOCOL_RECEIPT_SCHEMA_VERSION;
146
+ }
147
+ else {
148
+ throw new Error(`${label}: unsupported schema_version ${schemaVersion}`);
149
+ }
150
+ let kind = readString(raw.kind).trim();
151
+ if (kind === "" || kind === PROTOCOL_AUTHORIZATION_RECEIPT_KIND_V1) {
152
+ kind = PROTOCOL_AUTHORIZATION_RECEIPT_KIND_V1;
153
+ }
154
+ else {
155
+ throw new Error(`${label}: unsupported kind ${JSON.stringify(kind)}`);
156
+ }
157
+ let receiptVersion = readString(raw.receipt_version).trim();
158
+ if (receiptVersion === "" || receiptVersion === PROTOCOL_RECEIPT_VERSION_V1) {
159
+ receiptVersion = PROTOCOL_RECEIPT_VERSION_V1;
160
+ }
161
+ else {
162
+ throw new Error(`${label}: unsupported receipt_version ${JSON.stringify(receiptVersion)}`);
163
+ }
164
+ const receiptId = readString(raw.receipt_id).trim().toLowerCase();
165
+ if (receiptId === "") {
166
+ throw new Error(`${label}: receipt_id is required`);
167
+ }
168
+ if (!SCOPE_TOKEN_RE.test(receiptId)) {
169
+ throw new Error(`${label}: receipt_id ${JSON.stringify(receiptId)} is not canonical`);
170
+ }
171
+ const intentIdTrimmed = readString(raw.intent_id).trim();
172
+ if (!UUID_RE.test(intentIdTrimmed)) {
173
+ throw new Error(`${label}: intent_id must be a canonical UUID`);
174
+ }
175
+ const intentId = intentIdTrimmed.toLowerCase();
176
+ const issuedAt = normalizeTimestamp(raw.issued_at, `${label}: issued_at is required`);
177
+ let status = readString(raw.status).trim().toLowerCase();
178
+ if (status === "" || status === PROTOCOL_RECEIPT_STATUS_AUTHORIZED) {
179
+ status = PROTOCOL_RECEIPT_STATUS_AUTHORIZED;
180
+ }
181
+ else {
182
+ throw new Error(`${label}: unsupported status ${JSON.stringify(status)}`);
183
+ }
184
+ const tenantId = normalizeTenantId(readString(raw.tenant_id), `${label}: tenant_id`);
185
+ const verifierId = readString(raw.verifier_id).trim().toLowerCase();
186
+ if (verifierId === "") {
187
+ throw new Error(`${label}: verifier_id is required`);
188
+ }
189
+ if (!SCOPE_TOKEN_RE.test(verifierId)) {
190
+ throw new Error(`${label}: verifier_id ${JSON.stringify(verifierId)} is not canonical`);
191
+ }
192
+ const transportBinding = normalizeTransportBinding(readObject(raw.transport_binding) ?? {}, `${label}: transport_binding`);
193
+ const mandateDigest = readString(raw.mandate_digest_sha256_hex).trim().toLowerCase();
194
+ if (!HEX64_RE.test(mandateDigest)) {
195
+ throw new Error(`${label}: mandate_digest_sha256_hex must be a lowercase 64-byte hex SHA-256 digest`);
196
+ }
197
+ const importedMandatePubKey = readString(raw.imported_mandate_signing_public_key_ed25519_hex)
198
+ .trim()
199
+ .toLowerCase();
200
+ let importedPubKeyBytes;
201
+ try {
202
+ importedPubKeyBytes = hexToBytes(importedMandatePubKey);
203
+ }
204
+ catch {
205
+ throw new Error(`${label}: imported_mandate_signing_public_key_ed25519_hex is invalid`);
206
+ }
207
+ if (importedPubKeyBytes.length !== 32) {
208
+ throw new Error(`${label}: imported_mandate_signing_public_key_ed25519_hex is invalid`);
209
+ }
210
+ const authorizationRaw = readObject(raw.authorization) ?? {};
211
+ const authorizationKind = readString(authorizationRaw.kind).trim().toLowerCase();
212
+ if (authorizationKind !== AGENT_AUTHORIZATION_KIND_PRINCIPAL &&
213
+ authorizationKind !== AGENT_AUTHORIZATION_KIND_TENANT) {
214
+ throw new Error(`${label}: authorization.kind must be ${JSON.stringify(AGENT_AUTHORIZATION_KIND_PRINCIPAL)} or ${JSON.stringify(AGENT_AUTHORIZATION_KIND_TENANT)}`);
215
+ }
216
+ const authorizationTenantId = normalizeTenantId(readString(authorizationRaw.tenant_id), `${label}: authorization.tenant_id`);
217
+ if (authorizationTenantId !== tenantId) {
218
+ throw new Error(`${label}: authorization.tenant_id must match tenant_id`);
219
+ }
220
+ const authorization = {
221
+ kind: authorizationKind,
222
+ tenant_id: authorizationTenantId,
223
+ principal_subject: readString(authorizationRaw.principal_subject).trim(),
224
+ principal_type: readString(authorizationRaw.principal_type).trim().toLowerCase(),
225
+ };
226
+ const agentRaw = readObject(raw.agent) ?? {};
227
+ const agent = {
228
+ subject: readString(agentRaw.subject).trim(),
229
+ issuer: readString(agentRaw.issuer).trim(),
230
+ key_id: readString(agentRaw.key_id).trim(),
231
+ display_name: readString(agentRaw.display_name).trim(),
232
+ };
233
+ if (agent.subject === "") {
234
+ throw new Error(`${label}: agent.subject is required`);
235
+ }
236
+ const allowedActions = normalizeScopeSet(readStringArray(raw.allowed_actions), `${label}: allowed_actions`);
237
+ const allowedTools = normalizeScopeSet(readStringArray(raw.allowed_tools), `${label}: allowed_tools`);
238
+ if (allowedActions.length === 0 && allowedTools.length === 0) {
239
+ throw new Error(`${label}: at least one allowed action or allowed tool is required`);
240
+ }
241
+ const spendCeilingRaw = readObject(raw.spend_ceiling) ?? {};
242
+ const amountMinor = readNumber(spendCeilingRaw.amount_minor);
243
+ if (amountMinor <= 0) {
244
+ throw new Error(`${label}: spend_ceiling.amount_minor must be greater than zero`);
245
+ }
246
+ const currency = readString(spendCeilingRaw.currency).trim().toLowerCase();
247
+ if (!CURRENCY_RE.test(currency)) {
248
+ throw new Error(`${label}: spend_ceiling.currency ${JSON.stringify(currency)} is not canonical`);
249
+ }
250
+ const spendCeiling = { amount_minor: amountMinor, currency };
251
+ const settlementRaw = readObject(raw.settlement) ?? {};
252
+ const defaultRail = readString(settlementRaw.default_rail).trim().toLowerCase();
253
+ if (defaultRail === "") {
254
+ throw new Error(`${label}: settlement.default_rail is required`);
255
+ }
256
+ const allowedRails = normalizeScopeSet(readStringArray(settlementRaw.allowed_rails), `${label}: settlement.allowed_rails`);
257
+ const settlement = {
258
+ default_rail: defaultRail,
259
+ allowed_rails: allowedRails,
260
+ };
261
+ const constraintRaw = readObject(raw.constraint) ?? {};
262
+ const constraintKind = readString(constraintRaw.kind).trim().toLowerCase();
263
+ if (constraintKind === "") {
264
+ throw new Error(`${label}: constraint.kind is required`);
265
+ }
266
+ const constraint = {
267
+ kind: constraintKind,
268
+ id: readString(constraintRaw.id).trim(),
269
+ version: readString(constraintRaw.version).trim(),
270
+ digest_sha256_hex: readString(constraintRaw.digest_sha256_hex).trim().toLowerCase(),
271
+ uri: readString(constraintRaw.uri).trim(),
272
+ };
273
+ const expiresAt = normalizeTimestamp(raw.expires_at, `${label}: expires_at is required`);
274
+ const nonce = readString(raw.nonce).trim();
275
+ if (nonce === "") {
276
+ throw new Error(`${label}: nonce is required`);
277
+ }
278
+ const humanPresenceMode = readString(raw.human_presence_mode).trim().toLowerCase();
279
+ if (humanPresenceMode !== HUMAN_PRESENCE_MODE_HUMAN_PRESENT &&
280
+ humanPresenceMode !== HUMAN_PRESENCE_MODE_HUMAN_NOT_PRESENT) {
281
+ throw new Error(`${label}: human_presence_mode must be ${JSON.stringify(HUMAN_PRESENCE_MODE_HUMAN_PRESENT)} or ${JSON.stringify(HUMAN_PRESENCE_MODE_HUMAN_NOT_PRESENT)}`);
282
+ }
283
+ let signingAlgorithm = readString(raw.signing_algorithm).trim().toLowerCase();
284
+ if (signingAlgorithm === "") {
285
+ signingAlgorithm = PROTOCOL_RECEIPT_SIGNING_ALGORITHM_ED25519_SHA256;
286
+ }
287
+ return {
288
+ schema_version: schemaVersion,
289
+ kind,
290
+ receipt_version: receiptVersion,
291
+ receipt_id: receiptId,
292
+ issued_at: issuedAt,
293
+ status,
294
+ intent_id: intentId,
295
+ tenant_id: tenantId,
296
+ verifier_id: verifierId,
297
+ transport_binding: transportBinding,
298
+ mandate_digest_sha256_hex: mandateDigest,
299
+ imported_mandate_signing_public_key_ed25519_hex: importedMandatePubKey,
300
+ authorization,
301
+ agent,
302
+ allowed_actions: allowedActions,
303
+ allowed_tools: allowedTools,
304
+ spend_ceiling: spendCeiling,
305
+ settlement,
306
+ constraint,
307
+ expires_at: expiresAt,
308
+ nonce,
309
+ human_presence_mode: humanPresenceMode,
310
+ signing_algorithm: signingAlgorithm,
311
+ message_digest_sha256_hex: readString(raw.message_digest_sha256_hex).trim().toLowerCase(),
312
+ signing_public_key_ed25519_hex: readString(raw.signing_public_key_ed25519_hex).trim().toLowerCase(),
313
+ ed25519_signature_hex: readString(raw.ed25519_signature_hex).trim().toLowerCase(),
314
+ };
315
+ }
316
+ /**
317
+ * Validates and canonicalizes a settlement receipt for hashing and signing.
318
+ *
319
+ * @throws When structure or field values are invalid.
320
+ */
321
+ export function normalizeProtocolSettlementReceiptV1(input) {
322
+ const raw = coerceWire(input);
323
+ const label = "protocol settlement receipt";
324
+ let schemaVersion = readNumber(raw.schema_version);
325
+ if (schemaVersion === 0 || schemaVersion === PROTOCOL_RECEIPT_SCHEMA_VERSION) {
326
+ schemaVersion = PROTOCOL_RECEIPT_SCHEMA_VERSION;
327
+ }
328
+ else {
329
+ throw new Error(`${label}: unsupported schema_version ${schemaVersion}`);
330
+ }
331
+ let kind = readString(raw.kind).trim();
332
+ if (kind === "" || kind === PROTOCOL_SETTLEMENT_RECEIPT_KIND_V1) {
333
+ kind = PROTOCOL_SETTLEMENT_RECEIPT_KIND_V1;
334
+ }
335
+ else {
336
+ throw new Error(`${label}: unsupported kind ${JSON.stringify(kind)}`);
337
+ }
338
+ let receiptVersion = readString(raw.receipt_version).trim();
339
+ if (receiptVersion === "" || receiptVersion === PROTOCOL_RECEIPT_VERSION_V1) {
340
+ receiptVersion = PROTOCOL_RECEIPT_VERSION_V1;
341
+ }
342
+ else {
343
+ throw new Error(`${label}: unsupported receipt_version ${JSON.stringify(receiptVersion)}`);
344
+ }
345
+ const receiptId = readString(raw.receipt_id).trim().toLowerCase();
346
+ if (receiptId === "") {
347
+ throw new Error(`${label}: receipt_id is required`);
348
+ }
349
+ if (!SCOPE_TOKEN_RE.test(receiptId)) {
350
+ throw new Error(`${label}: receipt_id ${JSON.stringify(receiptId)} is not canonical`);
351
+ }
352
+ const intentIdTrimmed = readString(raw.intent_id).trim();
353
+ if (!UUID_RE.test(intentIdTrimmed)) {
354
+ throw new Error(`${label}: intent_id must be a canonical UUID`);
355
+ }
356
+ const intentId = intentIdTrimmed.toLowerCase();
357
+ if (receiptId !== intentId) {
358
+ throw new Error(`${label}: receipt_id must equal intent_id for Harbor-backed receipts`);
359
+ }
360
+ const issuedAt = normalizeTimestamp(raw.issued_at, `${label}: issued_at is required`);
361
+ const tenantId = normalizeTenantId(readString(raw.tenant_id), `${label}: tenant_id`);
362
+ const verifierId = readString(raw.verifier_id).trim().toLowerCase();
363
+ if (verifierId === "") {
364
+ throw new Error(`${label}: verifier_id is required`);
365
+ }
366
+ if (!SCOPE_TOKEN_RE.test(verifierId)) {
367
+ throw new Error(`${label}: verifier_id ${JSON.stringify(verifierId)} is not canonical`);
368
+ }
369
+ const transportBinding = normalizeTransportBinding(readObject(raw.transport_binding) ?? {}, `${label}: transport_binding`);
370
+ const authorizationReceiptId = readString(raw.authorization_receipt_id).trim().toLowerCase();
371
+ if (authorizationReceiptId === "") {
372
+ throw new Error(`${label}: authorization_receipt_id is required`);
373
+ }
374
+ if (!SCOPE_TOKEN_RE.test(authorizationReceiptId)) {
375
+ throw new Error(`${label}: authorization_receipt_id ${JSON.stringify(authorizationReceiptId)} is not canonical`);
376
+ }
377
+ const mandateDigest = readString(raw.mandate_digest_sha256_hex).trim().toLowerCase();
378
+ if (!HEX64_RE.test(mandateDigest)) {
379
+ throw new Error(`${label}: mandate_digest_sha256_hex must be a lowercase 64-byte hex SHA-256 digest`);
380
+ }
381
+ const harborState = readString(raw.harbor_state).trim().toLowerCase();
382
+ if (!PROTOCOL_SETTLEMENT_TERMINAL_STATES.has(harborState)) {
383
+ throw new Error(`${label}: harbor_state must be released, refunded, resolved_split, or escalated_external`);
384
+ }
385
+ const predicatePassed = readOptionalBool(raw.predicate_passed);
386
+ const settlementRail = readString(raw.settlement_rail).trim().toLowerCase();
387
+ if (settlementRail === "") {
388
+ throw new Error(`${label}: settlement_rail is required`);
389
+ }
390
+ const settlementMode = readString(raw.settlement_mode).trim();
391
+ if (settlementMode === "") {
392
+ throw new Error(`${label}: settlement_mode is required`);
393
+ }
394
+ const principalDid = readString(raw.principal_did).trim();
395
+ const payeeDid = readString(raw.payee_did).trim();
396
+ const currency = readString(raw.currency).trim().toLowerCase();
397
+ if (currency === "") {
398
+ throw new Error(`${label}: currency is required`);
399
+ }
400
+ const amountCents = readNumber(raw.amount_cents);
401
+ if (amountCents <= 0) {
402
+ throw new Error(`${label}: amount_cents must be greater than zero`);
403
+ }
404
+ const terminalObservedAt = normalizeTimestamp(raw.terminal_observed_at, `${label}: terminal_observed_at is required`);
405
+ let signingAlgorithm = readString(raw.signing_algorithm).trim().toLowerCase();
406
+ if (signingAlgorithm === "") {
407
+ signingAlgorithm = PROTOCOL_RECEIPT_SIGNING_ALGORITHM_ED25519_SHA256;
408
+ }
409
+ const normalized = {
410
+ schema_version: schemaVersion,
411
+ kind,
412
+ receipt_version: receiptVersion,
413
+ receipt_id: receiptId,
414
+ issued_at: issuedAt,
415
+ intent_id: intentId,
416
+ tenant_id: tenantId,
417
+ verifier_id: verifierId,
418
+ transport_binding: transportBinding,
419
+ authorization_receipt_id: authorizationReceiptId,
420
+ mandate_digest_sha256_hex: mandateDigest,
421
+ harbor_state: harborState,
422
+ settlement_rail: settlementRail,
423
+ settlement_mode: settlementMode,
424
+ principal_did: principalDid,
425
+ payee_did: payeeDid,
426
+ currency,
427
+ amount_cents: amountCents,
428
+ terminal_observed_at: terminalObservedAt,
429
+ signing_algorithm: signingAlgorithm,
430
+ message_digest_sha256_hex: readString(raw.message_digest_sha256_hex).trim().toLowerCase(),
431
+ signing_public_key_ed25519_hex: readString(raw.signing_public_key_ed25519_hex).trim().toLowerCase(),
432
+ ed25519_signature_hex: readString(raw.ed25519_signature_hex).trim().toLowerCase(),
433
+ };
434
+ if (predicatePassed !== undefined) {
435
+ normalized.predicate_passed = predicatePassed;
436
+ }
437
+ return normalized;
438
+ }
439
+ function marshalCanonicalAuthorizationReceipt(receipt) {
440
+ const payload = {
441
+ schema_version: receipt.schema_version,
442
+ kind: receipt.kind,
443
+ receipt_version: receipt.receipt_version,
444
+ receipt_id: receipt.receipt_id,
445
+ issued_at: receipt.issued_at,
446
+ status: receipt.status,
447
+ intent_id: receipt.intent_id,
448
+ tenant_id: receipt.tenant_id,
449
+ verifier_id: receipt.verifier_id,
450
+ transport_binding: canonicalTransportBinding(receipt.transport_binding),
451
+ mandate_digest_sha256_hex: receipt.mandate_digest_sha256_hex,
452
+ imported_mandate_signing_public_key_ed25519_hex: receipt.imported_mandate_signing_public_key_ed25519_hex,
453
+ authorization: {
454
+ kind: receipt.authorization.kind,
455
+ tenant_id: receipt.authorization.tenant_id,
456
+ principal_subject: receipt.authorization.principal_subject ?? "",
457
+ principal_type: receipt.authorization.principal_type ?? "",
458
+ },
459
+ agent: {
460
+ subject: receipt.agent.subject,
461
+ issuer: receipt.agent.issuer ?? "",
462
+ key_id: receipt.agent.key_id ?? "",
463
+ display_name: receipt.agent.display_name ?? "",
464
+ },
465
+ allowed_actions: receipt.allowed_actions.length > 0 ? [...receipt.allowed_actions] : null,
466
+ allowed_tools: receipt.allowed_tools.length > 0 ? [...receipt.allowed_tools] : null,
467
+ spend_ceiling: {
468
+ amount_minor: receipt.spend_ceiling.amount_minor,
469
+ currency: receipt.spend_ceiling.currency,
470
+ },
471
+ settlement: {
472
+ default_rail: receipt.settlement.default_rail,
473
+ allowed_rails: [...receipt.settlement.allowed_rails],
474
+ },
475
+ constraint: {
476
+ kind: receipt.constraint.kind,
477
+ id: receipt.constraint.id ?? "",
478
+ version: receipt.constraint.version ?? "",
479
+ digest_sha256_hex: receipt.constraint.digest_sha256_hex ?? "",
480
+ uri: receipt.constraint.uri ?? "",
481
+ },
482
+ expires_at: receipt.expires_at,
483
+ nonce: receipt.nonce,
484
+ human_presence_mode: receipt.human_presence_mode,
485
+ };
486
+ return new TextEncoder().encode(goJsonEscape(JSON.stringify(payload)));
487
+ }
488
+ function marshalCanonicalSettlementReceipt(receipt) {
489
+ const payload = {
490
+ schema_version: receipt.schema_version,
491
+ kind: receipt.kind,
492
+ receipt_version: receipt.receipt_version,
493
+ receipt_id: receipt.receipt_id,
494
+ issued_at: receipt.issued_at,
495
+ intent_id: receipt.intent_id,
496
+ tenant_id: receipt.tenant_id,
497
+ verifier_id: receipt.verifier_id,
498
+ transport_binding: canonicalTransportBinding(receipt.transport_binding),
499
+ authorization_receipt_id: receipt.authorization_receipt_id,
500
+ mandate_digest_sha256_hex: receipt.mandate_digest_sha256_hex,
501
+ harbor_state: receipt.harbor_state,
502
+ };
503
+ if (receipt.predicate_passed !== undefined) {
504
+ payload.predicate_passed = receipt.predicate_passed;
505
+ }
506
+ payload.settlement_rail = receipt.settlement_rail;
507
+ payload.settlement_mode = receipt.settlement_mode;
508
+ payload.principal_did = receipt.principal_did;
509
+ payload.payee_did = receipt.payee_did;
510
+ payload.currency = receipt.currency;
511
+ payload.amount_cents = receipt.amount_cents;
512
+ payload.terminal_observed_at = receipt.terminal_observed_at;
513
+ return new TextEncoder().encode(goJsonEscape(JSON.stringify(payload)));
514
+ }
515
+ function verifySignedDigest(kind, signingAlgorithm, messageDigestHex, publicKeyHex, signatureHex, expectedDigestHex) {
516
+ if (signingAlgorithm !== PROTOCOL_RECEIPT_SIGNING_ALGORITHM_ED25519_SHA256) {
517
+ throw new Error(`${kind}: signing_algorithm must be ${JSON.stringify(PROTOCOL_RECEIPT_SIGNING_ALGORITHM_ED25519_SHA256)}`);
518
+ }
519
+ if (!HEX64_RE.test(messageDigestHex)) {
520
+ throw new Error(`${kind}: message_digest_sha256_hex must be a lowercase 64-byte hex SHA-256 digest`);
521
+ }
522
+ if (messageDigestHex !== expectedDigestHex) {
523
+ throw new Error(`${kind}: message digest mismatch`);
524
+ }
525
+ let publicKey;
526
+ let signature;
527
+ try {
528
+ publicKey = hexToBytes(publicKeyHex);
529
+ }
530
+ catch {
531
+ throw new Error(`${kind}: invalid signing_public_key_ed25519_hex`);
532
+ }
533
+ if (publicKey.length !== 32) {
534
+ throw new Error(`${kind}: invalid signing_public_key_ed25519_hex`);
535
+ }
536
+ try {
537
+ signature = hexToBytes(signatureHex);
538
+ }
539
+ catch {
540
+ throw new Error(`${kind}: invalid ed25519_signature_hex`);
541
+ }
542
+ if (signature.length !== 64) {
543
+ throw new Error(`${kind}: invalid ed25519_signature_hex`);
544
+ }
545
+ if (!ed25519Verify(signature, hexToBytes(expectedDigestHex), publicKey)) {
546
+ throw new Error(`${kind}: ed25519 signature verification failed`);
547
+ }
548
+ }
549
+ /**
550
+ * Validates, canonicalizes, and signs an authorization receipt with
551
+ * Ed25519-over-SHA-256(canonical JSON).
552
+ *
553
+ * @param signingSeed - 32-byte Ed25519 seed (noble private key).
554
+ */
555
+ export function signProtocolAuthorizationReceiptV1(signingSeed, input) {
556
+ ensureEd25519Sha512Sync();
557
+ if (signingSeed.length !== 32) {
558
+ throw new Error("protocol authorization receipt: signing key must be an ed25519 private key");
559
+ }
560
+ const normalized = normalizeProtocolAuthorizationReceiptV1(input);
561
+ const digest = sha256Hex(marshalCanonicalAuthorizationReceipt(normalized));
562
+ const signature = sign(hexToBytes(digest), signingSeed);
563
+ const publicKey = getPublicKey(signingSeed);
564
+ return {
565
+ ...normalized,
566
+ signing_algorithm: PROTOCOL_RECEIPT_SIGNING_ALGORITHM_ED25519_SHA256,
567
+ message_digest_sha256_hex: digest,
568
+ signing_public_key_ed25519_hex: Buffer.from(publicKey).toString("hex"),
569
+ ed25519_signature_hex: Buffer.from(signature).toString("hex"),
570
+ };
571
+ }
572
+ /**
573
+ * Validates, canonicalizes, and signs a settlement receipt with
574
+ * Ed25519-over-SHA-256(canonical JSON).
575
+ *
576
+ * @param signingSeed - 32-byte Ed25519 seed (noble private key).
577
+ */
578
+ export function signProtocolSettlementReceiptV1(signingSeed, input) {
579
+ ensureEd25519Sha512Sync();
580
+ if (signingSeed.length !== 32) {
581
+ throw new Error("protocol settlement receipt: signing key must be an ed25519 private key");
582
+ }
583
+ const normalized = normalizeProtocolSettlementReceiptV1(input);
584
+ const digest = sha256Hex(marshalCanonicalSettlementReceipt(normalized));
585
+ const signature = sign(hexToBytes(digest), signingSeed);
586
+ const publicKey = getPublicKey(signingSeed);
587
+ return {
588
+ ...normalized,
589
+ signing_algorithm: PROTOCOL_RECEIPT_SIGNING_ALGORITHM_ED25519_SHA256,
590
+ message_digest_sha256_hex: digest,
591
+ signing_public_key_ed25519_hex: Buffer.from(publicKey).toString("hex"),
592
+ ed25519_signature_hex: Buffer.from(signature).toString("hex"),
593
+ };
594
+ }
595
+ /**
596
+ * Checks structure, canonical digest recompute, and detached Ed25519 signature.
597
+ *
598
+ * @returns The normalized authorization receipt on success.
599
+ * @throws When structure, digest, or signature verification fails.
600
+ */
601
+ export function verifyProtocolAuthorizationReceiptV1(input) {
602
+ ensureEd25519Sha512Sync();
603
+ const normalized = normalizeProtocolAuthorizationReceiptV1(input);
604
+ const digest = sha256Hex(marshalCanonicalAuthorizationReceipt(normalized));
605
+ verifySignedDigest("protocol authorization receipt", normalized.signing_algorithm, normalized.message_digest_sha256_hex, normalized.signing_public_key_ed25519_hex, normalized.ed25519_signature_hex, digest);
606
+ return normalized;
607
+ }
608
+ /**
609
+ * Checks structure, canonical digest recompute, and detached Ed25519 signature.
610
+ *
611
+ * @returns The normalized settlement receipt on success.
612
+ * @throws When structure, digest, or signature verification fails.
613
+ */
614
+ export function verifyProtocolSettlementReceiptV1(input) {
615
+ ensureEd25519Sha512Sync();
616
+ const normalized = normalizeProtocolSettlementReceiptV1(input);
617
+ const digest = sha256Hex(marshalCanonicalSettlementReceipt(normalized));
618
+ verifySignedDigest("protocol settlement receipt", normalized.signing_algorithm, normalized.message_digest_sha256_hex, normalized.signing_public_key_ed25519_hex, normalized.ed25519_signature_hex, digest);
619
+ return normalized;
620
+ }
@@ -0,0 +1,29 @@
1
+ import { type ShopifyNoteAttribute } from "../commerce-binding.js";
2
+ import type { CreateCheckoutWithBindingParams, ShopifyCheckoutCreatePayload, ShopifyCheckoutLineItemInput, ShopifyUcpCheckoutLineItem } from "./types.js";
3
+ /**
4
+ * Converts developer-friendly line items into UCP checkout `line_items`.
5
+ */
6
+ export declare function toUcpCheckoutLineItems(lineItems: readonly ShopifyCheckoutLineItemInput[]): ShopifyUcpCheckoutLineItem[];
7
+ /**
8
+ * Builds a UCP checkout create payload with canonical Paybond binding metadata in
9
+ * `note_attributes`.
10
+ *
11
+ * `tenantId` and `intentId` must be sourced from authenticated Paybond session context —
12
+ * never from unauthenticated client input.
13
+ */
14
+ export declare function createCheckoutWithBinding(params: CreateCheckoutWithBindingParams): ShopifyCheckoutCreatePayload;
15
+ /**
16
+ * Merges binding metadata into an existing UCP checkout mutation payload.
17
+ *
18
+ * Use for `update_checkout` and other checkout mutations so binding cannot be dropped.
19
+ */
20
+ export declare function mergeBindingIntoCheckoutPayload(binding: {
21
+ tenantId: string;
22
+ intentId: string;
23
+ }, checkoutPayload: {
24
+ note_attributes?: readonly ShopifyNoteAttribute[];
25
+ meta?: {
26
+ profile_url?: string;
27
+ };
28
+ [key: string]: unknown;
29
+ }, agentProfileUrl?: string): Record<string, unknown>;