@peac/mappings-ucp 0.12.5 → 0.12.7

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 (2) hide show
  1. package/README.md +61 -138
  2. package/package.json +13 -9
package/README.md CHANGED
@@ -1,13 +1,6 @@
1
1
  # @peac/mappings-ucp
2
2
 
3
- Universal Commerce Protocol (UCP) mapping to PEAC receipts and dispute evidence.
4
-
5
- ## Features
6
-
7
- - **Webhook signature verification** - Detached JWS (RFC 7797) with ES256/ES384/ES512
8
- - **Raw-first, JCS fallback** - Handles UCP's ambiguous canonicalization spec
9
- - **UCP order to PEAC receipt mapping** - Amounts in minor units (cents)
10
- - **Dispute evidence generation** - Hardened YAML schema for @peac/audit bundles
3
+ Universal Commerce Protocol (UCP) order mapping to PEAC signed receipts, webhook signature verification, and dispute evidence generation.
11
4
 
12
5
  ## Installation
13
6
 
@@ -15,163 +8,93 @@ Universal Commerce Protocol (UCP) mapping to PEAC receipts and dispute evidence.
15
8
  pnpm add @peac/mappings-ucp
16
9
  ```
17
10
 
18
- ## Usage
19
-
20
- ### Verify Webhook Signature
21
-
22
- ```typescript
23
- import { verifyUcpWebhookSignature } from '@peac/mappings-ucp';
11
+ ## What It Does
24
12
 
25
- const result = await verifyUcpWebhookSignature({
26
- signature_header: req.headers['request-signature'],
27
- body_bytes: rawBody, // Uint8Array
28
- profile_url: 'https://business.example.com/.well-known/ucp',
29
- });
13
+ `@peac/mappings-ucp` maps UCP order data to PEAC receipt claims, verifies UCP webhook signatures using detached JWS (RFC 7797), and generates structured dispute evidence bundles. Order state is kept distinct from payment state: the `payment_state_source` field marks whether payment status was explicitly provided or derived from order fulfillment, so downstream consumers can distinguish observed payment evidence from inferred status.
30
14
 
31
- if (result.valid) {
32
- console.log(`Verified using ${result.mode_used} mode`);
33
- console.log(`Key: ${result.key?.kid}`);
34
- } else {
35
- console.error(`Verification failed: ${result.error_code}`);
36
- console.log('Attempts:', result.attempts);
37
- }
38
- ```
15
+ ## How Do I Use It?
39
16
 
40
- ### Map UCP Order to PEAC Receipt
17
+ ### Map a UCP order to receipt claims
41
18
 
42
19
  ```typescript
43
20
  import { mapUcpOrderToReceipt } from '@peac/mappings-ucp';
44
- import { issue } from '@peac/protocol';
45
21
 
46
22
  const claims = mapUcpOrderToReceipt({
47
- order: webhookBody.order,
48
- issuer: 'https://platform.example.com',
49
- subject: 'buyer:123',
23
+ order: ucpOrder,
24
+ issuer: 'https://merchant.example.com',
25
+ subject: 'agent:shopper-bot-123',
50
26
  currency: 'USD',
51
27
  });
52
28
 
53
29
  // Sign with @peac/protocol
54
- const { jws } = await issue({
55
- iss: 'https://platform.example.com',
56
- kind: 'evidence',
57
- type: 'org.peacprotocol/payment',
58
- pillars: ['commerce'],
59
- extensions: {
60
- 'org.peacprotocol/commerce': {
61
- payment_rail: 'ucp',
62
- amount_minor: String(claims.amount),
63
- currency: claims.currency,
64
- reference: claims.reference,
65
- },
66
- },
67
- privateKey,
68
- kid,
69
- });
30
+ const receipt = await issue(claims, privateKey, kid);
70
31
  ```
71
32
 
72
- ### Create Dispute Evidence
33
+ ### Verify a UCP webhook signature
73
34
 
74
35
  ```typescript
75
- import { createUcpDisputeEvidence } from '@peac/mappings-ucp';
76
- import { createDisputeBundle } from '@peac/audit';
36
+ import { verifyUcpWebhookSignature } from '@peac/mappings-ucp';
77
37
 
78
- // Create evidence from webhook
79
- const evidence = await createUcpDisputeEvidence({
38
+ const result = await verifyUcpWebhookSignature({
80
39
  signature_header: req.headers['request-signature'],
81
40
  body_bytes: rawBody,
82
- method: 'POST',
83
- path: '/webhooks/ucp/orders',
84
- received_at: new Date().toISOString(),
85
41
  profile_url: 'https://business.example.com/.well-known/ucp',
86
- profile_fetched_at: new Date().toISOString(),
87
42
  });
88
43
 
89
- // Create dispute bundle with evidence
90
- const bundle = await createDisputeBundle({
91
- dispute_ref: 'dispute_123',
92
- created_by: 'platform:example.com',
93
- receipts: [receiptJws],
94
- keys: jwks,
95
- policy: evidence.evidence_yaml, // UCP evidence stored here
96
- });
44
+ if (result.valid) {
45
+ // Signature verified; proceed with order mapping
46
+ }
47
+ ```
48
+
49
+ ### Extract line item summaries and order statistics
50
+
51
+ ```typescript
52
+ import { extractLineItemSummary, calculateOrderStats } from '@peac/mappings-ucp';
53
+
54
+ const summary = extractLineItemSummary(ucpOrder);
55
+ const stats = calculateOrderStats(ucpOrder);
56
+
57
+ console.log(stats.total_items); // number of line items
58
+ console.log(stats.fulfilled_items); // items marked fulfilled
97
59
  ```
98
60
 
99
- ## Verification Strategy
100
-
101
- UCP's webhook spec says "detached JWT over the request body" but doesn't specify canonicalization (unlike AP2 which requires JCS). This package uses:
102
-
103
- 1. **Try raw body bytes first** - Most likely what implementers expect
104
- 2. **Fallback to JCS-canonicalized body** - If raw fails and body is valid JSON
105
- 3. **Record all attempts** - For debugging and dispute evidence
106
-
107
- Both b64=true (standard) and b64=false (RFC 7797 unencoded payload) are supported.
108
-
109
- ## Security and Correctness Notes
110
-
111
- This verifier implements strict JOSE semantics for audit-grade correctness:
112
-
113
- - **RFC 7797 b64=false**: Unencoded payloads are passed as raw bytes to the verification library (not ASCII-decoded strings). This ensures binary payloads and UTF-8 content verify correctly.
114
- - **JOSE crit semantics**: If the `crit` header is present, ALL entries must be understood by this implementation. Unknown critical parameters cause immediate rejection with a clear error.
115
- - **Strict header typing**: `crit` must be an array of strings (no objects, numbers, or duplicates). `b64` must be a boolean (not string `"false"` or number `0`).
116
- - **Single profile fetch**: The verifier returns both the parsed profile and raw JSON, eliminating race conditions and enabling deterministic evidence hashing.
117
- - **Deterministic evidence**: YAML output uses UTF-8 encoding, LF line endings, and exactly one trailing newline for byte-stable hashing across platforms.
118
- - **JWS signature format**: Demo/test code uses IEEE P1363 ECDSA signatures (raw R||S) as required by JWS, not DER encoding.
119
-
120
- The verification result includes `profile` and `profile_raw` fields, allowing callers to capture evidence without re-fetching.
121
-
122
- ## Evidence Schema
123
-
124
- The evidence YAML uses a hardened schema that cannot be misinterpreted as executable policy:
125
-
126
- ```yaml
127
- peac_bundle_metadata_version: 'org.peacprotocol.ucp/0.1'
128
- kind: 'evidence_attachment'
129
- scope: 'ucp_webhook'
130
-
131
- request:
132
- method: 'POST'
133
- path: '/webhooks/ucp/orders'
134
- received_at: '2026-01-13T12:00:00Z'
135
-
136
- payload:
137
- raw_sha256_hex: 'abc123...'
138
- raw_bytes_b64url: 'eyJ...' # Optional, for bodies <= 256KB
139
- jcs_sha256_hex: 'def456...' # If JSON parseable
140
- json_parseable: true
141
-
142
- signature:
143
- header_value: 'eyJhbGc...' # Full Request-Signature header
144
- kid: 'business-key-001'
145
- alg: 'ES256'
146
- b64: null
147
- verified: true
148
- verification_mode_used: 'raw'
149
- verification_attempts:
150
- - mode: 'raw'
151
- success: true
152
-
153
- profile:
154
- url: 'https://business.example.com/.well-known/ucp'
155
- fetched_at: '2026-01-13T11:59:30Z'
156
- profile_jcs_sha256_hex: 'ghi789...'
157
- key_jwk:
158
- kty: 'EC'
159
- crv: 'P-256'
160
- kid: 'business-key-001'
161
- x: '...'
162
- y: '...'
61
+ ### Attach and extract evidence carriers on webhook payloads
62
+
63
+ ```typescript
64
+ import {
65
+ UcpCarrierAdapter,
66
+ attachCarrierToWebhookPayload,
67
+ extractCarrierFromWebhookPayload,
68
+ } from '@peac/mappings-ucp';
69
+
70
+ // Attach a signed receipt carrier to a UCP webhook payload
71
+ attachCarrierToWebhookPayload(webhookPayload, carrier);
72
+
73
+ // Extract carrier from an incoming webhook payload
74
+ const result = extractCarrierFromWebhookPayload(webhookPayload);
75
+ if (result) {
76
+ console.log(result.receipts[0].receipt_ref);
77
+ }
78
+
79
+ // Or use the adapter interface
80
+ const adapter = new UcpCarrierAdapter();
81
+ const extracted = adapter.extract(webhookPayload);
163
82
  ```
164
83
 
165
- ## Error Codes
84
+ ## Integrates With
85
+
86
+ - `@peac/kernel` (Layer 0): Evidence carrier types and constants
87
+ - `@peac/schema` (Layer 1): Receipt schemas and carrier validation
88
+ - `@peac/protocol` (Layer 3): Sign mapped claims into receipts with `issue()`
89
+
90
+ ## For Agent Developers
91
+
92
+ If you are building an AI agent that interacts with UCP-based commerce platforms:
166
93
 
167
- | Code | HTTP | Description |
168
- | ------------------------------------- | ---- | -------------------------------- |
169
- | E_UCP_SIGNATURE_MISSING | 400 | Request-Signature header missing |
170
- | E_UCP_SIGNATURE_MALFORMED | 400 | Invalid detached JWS format |
171
- | E_UCP_SIGNATURE_ALGORITHM_UNSUPPORTED | 400 | Algorithm not ES256/ES384/ES512 |
172
- | E_UCP_KEY_NOT_FOUND | 401 | Key ID not in profile |
173
- | E_UCP_SIGNATURE_INVALID | 401 | Signature verification failed |
174
- | E_UCP_PROFILE_FETCH_FAILED | 502 | Failed to fetch UCP profile |
94
+ - Use `mapUcpOrderToReceipt()` to produce signed evidence of order observations
95
+ - Use `verifyUcpWebhookSignature()` to validate incoming webhook authenticity before mapping
96
+ - Order status reflects fulfillment state; payment status requires explicit `payment_state` when the upstream source provides it
97
+ - See the [llms.txt](https://github.com/peacprotocol/peac/blob/main/llms.txt) for a concise protocol overview
175
98
 
176
99
  ## License
177
100
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peac/mappings-ucp",
3
- "version": "0.12.5",
3
+ "version": "0.12.7",
4
4
  "description": "Google Universal Commerce Protocol (UCP) mapping to PEAC receipts and dispute evidence",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,19 +15,23 @@
15
15
  "dist"
16
16
  ],
17
17
  "keywords": [
18
+ "peac",
19
+ "peacprotocol",
20
+ "interaction-records",
21
+ "signed-records",
22
+ "receipts",
23
+ "originary",
18
24
  "ucp",
19
- "google",
20
- "commerce",
21
- "webhooks",
22
- "ap2",
23
- "peac"
25
+ "universal-commerce",
26
+ "order-management",
27
+ "payment-evidence"
24
28
  ],
25
29
  "author": "PEAC Protocol Contributors",
26
30
  "license": "Apache-2.0",
27
31
  "bugs": {
28
32
  "url": "https://github.com/peacprotocol/peac/issues"
29
33
  },
30
- "homepage": "https://www.peacprotocol.org",
34
+ "homepage": "https://github.com/peacprotocol/peac#readme",
31
35
  "repository": {
32
36
  "type": "git",
33
37
  "url": "git+https://github.com/peacprotocol/peac.git",
@@ -38,8 +42,8 @@
38
42
  },
39
43
  "dependencies": {
40
44
  "jose": "^5.2.0",
41
- "@peac/kernel": "0.12.5",
42
- "@peac/schema": "0.12.5"
45
+ "@peac/kernel": "0.12.7",
46
+ "@peac/schema": "0.12.7"
43
47
  },
44
48
  "devDependencies": {
45
49
  "typescript": "^5.3.0",