s402 0.1.6 → 0.1.8

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/CHANGELOG.md CHANGED
@@ -5,6 +5,50 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.6] - 2026-02-19
9
+
10
+ ### Fixed
11
+
12
+ - **Security audit patches** (15 true positives, H-1 through M-6, L-2):
13
+ - H-1: `process()` wraps `resolveScheme`/`verify`/`settle` in try/catch — unhandled rejections no longer crash server middleware; returns `{success: false}` instead
14
+ - H-2: In-flight dedup `Set` on `process()` — concurrent identical payloads can no longer both reach `scheme.settle()`
15
+ - H-3: `Promise.race()` timeouts — 5s for verify, 15s for settle — prevents hanging RPC calls from exhausting the event loop
16
+ - M-1: `facilitatorUrl` in x402 compat now validated via `new URL()` — rejects `javascript:`, `file://`, and other non-http(s) schemes (SSRF guard)
17
+ - M-2: `isValidAmount` → `isValidU64Amount` on decode — rejects amounts above u64 max at the wire boundary
18
+ - M-5: Settle catch returns `SETTLEMENT_FAILED` (`retryable: true`) instead of `VERIFICATION_FAILED` (`retryable: false`) — agents can now retry on transient RPC failures
19
+ - M-6: `payTo` validation tightened from `startsWith('0x')` to full Sui address regex `/^0x[0-9a-fA-F]{64}$/` — rejects `'0x'` alone and non-hex chars
20
+ - L-2: `expiresAt` guard extended to reject `<= 0` — negative timestamps and zero are now invalid at decode time
21
+
22
+ ## [0.1.5] - 2026-02-19
23
+
24
+ ### Changed
25
+
26
+ - Author updated to SweeInc brand name
27
+ - Renamed `@sweepay/*` → `@sweefi/*` across all documentation
28
+
29
+ ## [0.1.4] - 2026-02-18
30
+
31
+ _Version bump for npm publish after license change._
32
+
33
+ ## [0.1.3] - 2026-02-18
34
+
35
+ ### Changed
36
+
37
+ - License changed from MIT to Apache-2.0
38
+ - Documentation consolidated (removed codebase-tour, added complete guide)
39
+ - Updated tagline to "HTTP 402 payment protocol"
40
+
41
+ ### Added
42
+
43
+ - CI and npm version badges to README
44
+
45
+ ## [0.1.2] - 2026-02-16
46
+
47
+ ### Added
48
+
49
+ - CI workflow (GitHub Actions) with tag-based npm releases
50
+ - Separate build job for Node 22
51
+
8
52
  ## [0.1.1] - 2026-02-16
9
53
 
10
54
  ### Fixed
@@ -33,4 +77,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
33
77
  - Property-based fuzz testing via fast-check
34
78
  - 207 tests, zero runtime dependencies
35
79
 
80
+ [0.1.6]: https://github.com/s402-protocol/core/compare/v0.1.5...v0.1.6
81
+ [0.1.5]: https://github.com/s402-protocol/core/compare/v0.1.4...v0.1.5
82
+ [0.1.4]: https://github.com/s402-protocol/core/compare/v0.1.3...v0.1.4
83
+ [0.1.3]: https://github.com/s402-protocol/core/compare/v0.1.2...v0.1.3
84
+ [0.1.2]: https://github.com/s402-protocol/core/compare/v0.1.1...v0.1.2
85
+ [0.1.1]: https://github.com/s402-protocol/core/compare/v0.1.0...v0.1.1
36
86
  [0.1.0]: https://github.com/s402-protocol/core/releases/tag/v0.1.0
package/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
 
6
6
  **Sui-native HTTP 402 protocol.** Atomic settlement via Sui's Programmable Transaction Blocks (PTBs). Includes an optional compat layer (`s402/compat`) for normalizing x402 input.
7
7
 
8
+ s402 is the Sui-native implementation of HTTP 402 (Payment Required) — an open protocol that lets AI agents pay for API calls in a single HTTP request with no per-call on-chain transaction. Unlike Coinbase's x402 on Ethereum, s402 uses Sui's Programmable Transaction Blocks to reduce 1,000 payments to just 2 on-chain transactions via the Prepaid scheme, cutting per-call effective gas from $0.007 to $0.000014 and making micropayments economically viable for AI agents for the first time.
9
+
8
10
  ```bash
9
11
  npm install s402
10
12
  pnpm add s402
@@ -45,11 +47,14 @@ s402 <-- You are here. Protocol spec. Zero runtime deps.
45
47
  |-- Compat Optional x402 migration aid
46
48
  |-- Errors Typed error codes with recovery hints
47
49
  |
48
- @sweefi/sui <-- Sui-specific implementations (coming soon)
49
- @sweefi/sdk <-- High-level DX (coming soon)
50
+ @sweefi/sui <-- Sui adapter: 40 PTB builders + SuiPaymentAdapter + createS402Client
51
+ @sweefi/server <-- Chain-agnostic HTTP: s402Gate middleware + wrapFetchWithS402
52
+ @sweefi/ui-core <-- State machine + PaymentAdapter interface
53
+ @sweefi/vue <-- Vue 3 plugin + useSweefiPayment() composable
54
+ @sweefi/react <-- React context + useSweefiPayment() hook
50
55
  ```
51
56
 
52
- `s402` is **chain-agnostic protocol plumbing**. It defines _what_ gets sent over HTTP. The Sui-specific _how_ will live in `@sweefi/sui` (coming soon).
57
+ `s402` is **chain-agnostic protocol plumbing**. It defines _what_ gets sent over HTTP. The Sui-specific _how_ lives in [`@sweefi/sui`](https://www.npmjs.com/package/@sweefi/sui).
53
58
 
54
59
  ## Payment Schemes
55
60
 
@@ -158,7 +163,7 @@ const requirements: s402PaymentRequirements = {
158
163
  network: 'sui:mainnet',
159
164
  asset: '0x2::sui::SUI',
160
165
  amount: '1000000', // 0.001 SUI in MIST
161
- payTo: '0xrecipient...',
166
+ payTo: '0x0000000000000000000000000000000000000000000000000000000000000001',
162
167
  };
163
168
 
164
169
  response.status = 402;
@@ -258,7 +263,7 @@ import type {
258
263
  } from 's402';
259
264
  ```
260
265
 
261
- The reference Sui implementation of all five schemes will be available in `@sweefi/sui` (coming soon).
266
+ The reference Sui implementation of all five schemes is available in [`@sweefi/sui`](https://www.npmjs.com/package/@sweefi/sui).
262
267
 
263
268
  ## Wire Format
264
269
 
@@ -303,7 +308,7 @@ const requirements: s402PaymentRequirements = {
303
308
  network: 'sui:mainnet',
304
309
  asset: '0x2::sui::SUI',
305
310
  amount: '1000000',
306
- payTo: '0xrecipient...',
311
+ payTo: '0x0000000000000000000000000000000000000000000000000000000000000001',
307
312
  expiresAt: Date.now() + 5 * 60 * 1000, // 5-minute window
308
313
  };
309
314
  ```
package/SECURITY.md CHANGED
@@ -15,7 +15,7 @@ You will receive an acknowledgment within 48 hours. We aim to provide a fix or m
15
15
 
16
16
  This policy covers the `s402` npm package — the protocol types, HTTP encoding/decoding, scheme registry, and compat layer.
17
17
 
18
- Security issues in downstream packages (`@sweefi/sui`, `@sweefi/sdk`, etc.) should be reported to the same email.
18
+ Security issues in downstream packages (`@sweefi/sui`, `@sweefi/server`, `@sweefi/ui-core`, etc.) should be reported to the same email.
19
19
 
20
20
  ## What qualifies
21
21
 
package/dist/http.d.mts CHANGED
@@ -74,6 +74,31 @@ declare function validatePrepaidShape(value: unknown): void;
74
74
  declare function validateSubObjects(record: Record<string, unknown>): void;
75
75
  /** Validate that decoded payment requirements have the required shape. */
76
76
  declare function validateRequirementsShape(obj: unknown): void;
77
+ /** Content type for s402 JSON body transport */
78
+ declare const S402_CONTENT_TYPE: "application/s402+json";
79
+ /** Encode payment requirements as JSON string (for response body) */
80
+ declare function encodeRequirementsBody(requirements: s402PaymentRequirements): string;
81
+ /** Decode payment requirements from JSON string (from response body) */
82
+ declare function decodeRequirementsBody(body: string): s402PaymentRequirements;
83
+ /** Encode payment payload as JSON string (for request body) */
84
+ declare function encodePayloadBody(payload: s402PaymentPayload): string;
85
+ /** Decode payment payload from JSON string (from request body) */
86
+ declare function decodePayloadBody(body: string): s402PaymentPayload;
87
+ /** Encode settlement response as JSON string (for response body) */
88
+ declare function encodeSettleBody(response: s402SettleResponse): string;
89
+ /** Decode settlement response from JSON string (from response body) */
90
+ declare function decodeSettleBody(body: string): s402SettleResponse;
91
+ /**
92
+ * Detect transport mode from an incoming request.
93
+ *
94
+ * Checks Content-Type for body transport, then falls back to header detection.
95
+ * Returns 'body' if Content-Type is application/s402+json.
96
+ * Returns 'header' if x-payment header is present.
97
+ * Returns 'unknown' otherwise.
98
+ */
99
+ declare function detectTransport(request: {
100
+ headers: Headers;
101
+ }): 'header' | 'body' | 'unknown';
77
102
  /**
78
103
  * Detect whether a 402 response uses s402 or x402 protocol.
79
104
  *
@@ -88,4 +113,4 @@ declare function detectProtocol(headers: Headers): 's402' | 'x402' | 'unknown';
88
113
  */
89
114
  declare function extractRequirementsFromResponse(response: Response): s402PaymentRequirements | null;
90
115
  //#endregion
91
- export { decodePaymentPayload, decodePaymentRequired, decodeSettleResponse, detectProtocol, encodePaymentPayload, encodePaymentRequired, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, pickPayloadFields, pickRequirementsFields, pickSettleResponseFields, validateEscrowShape, validateMandateShape, validatePrepaidShape, validateRequirementsShape, validateStreamShape, validateSubObjects, validateUnlockShape };
116
+ export { S402_CONTENT_TYPE, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, pickPayloadFields, pickRequirementsFields, pickSettleResponseFields, validateEscrowShape, validateMandateShape, validatePrepaidShape, validateRequirementsShape, validateStreamShape, validateSubObjects, validateUnlockShape };
package/dist/http.mjs CHANGED
@@ -83,7 +83,9 @@ const S402_SUB_OBJECT_KEYS = {
83
83
  "ratePerCall",
84
84
  "maxCalls",
85
85
  "minDeposit",
86
- "withdrawalDelayMs"
86
+ "withdrawalDelayMs",
87
+ "providerPubkey",
88
+ "disputeWindowMs"
87
89
  ])
88
90
  };
89
91
  /** Strip unknown keys from a sub-object, returning a clean copy. */
@@ -318,6 +320,8 @@ function validatePrepaidShape(value) {
318
320
  assertString(obj, "withdrawalDelayMs", "prepaid");
319
321
  if (typeof obj.withdrawalDelayMs === "string" && !isValidAmount(obj.withdrawalDelayMs)) throw new s402Error("INVALID_PAYLOAD", `prepaid.withdrawalDelayMs must be a non-negative integer string (milliseconds), got "${obj.withdrawalDelayMs}"`);
320
322
  assertOptionalString(obj, "maxCalls", "prepaid");
323
+ assertOptionalString(obj, "providerPubkey", "prepaid");
324
+ assertOptionalString(obj, "disputeWindowMs", "prepaid");
321
325
  }
322
326
  /**
323
327
  * Validate all optional sub-objects on a requirements record.
@@ -345,6 +349,8 @@ function validateRequirementsShape(obj) {
345
349
  if (typeof record.payTo !== "string") missing.push("payTo (string)");
346
350
  else if (!/^0x[0-9a-fA-F]{64}$/.test(record.payTo)) throw new s402Error("INVALID_PAYLOAD", `payTo must be a 32-byte Sui address (0x + 64 hex chars), got "${record.payTo.substring(0, 20)}..."`);
347
351
  if (missing.length > 0) throw new s402Error("INVALID_PAYLOAD", `Malformed payment requirements: missing ${missing.join(", ")}`);
352
+ if (/[\x00-\x1f\x7f]/.test(record.network)) throw new s402Error("INVALID_PAYLOAD", "network contains control characters");
353
+ if (/[\x00-\x1f\x7f]/.test(record.asset)) throw new s402Error("INVALID_PAYLOAD", "asset contains control characters");
348
354
  if (Array.isArray(record.accepts) && record.accepts.length === 0) throw new s402Error("INVALID_PAYLOAD", "accepts array must contain at least one scheme");
349
355
  const accepts = record.accepts;
350
356
  for (const scheme of accepts) if (typeof scheme !== "string") throw new s402Error("INVALID_PAYLOAD", `Invalid entry in accepts array: expected string, got ${typeof scheme}`);
@@ -354,6 +360,13 @@ function validateRequirementsShape(obj) {
354
360
  if (record.expiresAt !== void 0) {
355
361
  if (typeof record.expiresAt !== "number" || !Number.isFinite(record.expiresAt) || record.expiresAt <= 0) throw new s402Error("INVALID_PAYLOAD", `expiresAt must be a positive finite number (Unix timestamp ms), got ${record.expiresAt}`);
356
362
  }
363
+ if (record.protocolFeeAddress !== void 0) {
364
+ if (typeof record.protocolFeeAddress !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(record.protocolFeeAddress)) throw new s402Error("INVALID_PAYLOAD", `protocolFeeAddress must be a 32-byte Sui address (0x + 64 hex chars), got "${String(record.protocolFeeAddress).substring(0, 20)}..."`);
365
+ }
366
+ if (record.facilitatorUrl !== void 0) {
367
+ if (typeof record.facilitatorUrl !== "string") throw new s402Error("INVALID_PAYLOAD", `facilitatorUrl must be a string, got ${typeof record.facilitatorUrl}`);
368
+ if (/[\x00-\x1f\x7f]/.test(record.facilitatorUrl)) throw new s402Error("INVALID_PAYLOAD", "facilitatorUrl contains control characters (potential header injection)");
369
+ }
357
370
  validateSubObjects(record);
358
371
  }
359
372
  /** Validate that a decoded payment payload has the required shape. */
@@ -377,6 +390,66 @@ function validateSettleShape(obj) {
377
390
  if (obj == null || typeof obj !== "object") throw new s402Error("INVALID_PAYLOAD", "Settle response is not an object");
378
391
  if (typeof obj.success !== "boolean") throw new s402Error("INVALID_PAYLOAD", "Malformed settle response: missing or invalid \"success\" (boolean)");
379
392
  }
393
+ /** Content type for s402 JSON body transport */
394
+ const S402_CONTENT_TYPE = "application/s402+json";
395
+ /** Encode payment requirements as JSON string (for response body) */
396
+ function encodeRequirementsBody(requirements) {
397
+ return JSON.stringify(requirements);
398
+ }
399
+ /** Decode payment requirements from JSON string (from response body) */
400
+ function decodeRequirementsBody(body) {
401
+ let parsed;
402
+ try {
403
+ parsed = JSON.parse(body);
404
+ } catch (e) {
405
+ throw new s402Error("INVALID_PAYLOAD", `Failed to parse s402 requirements body: ${e instanceof Error ? e.message : "invalid JSON"}`);
406
+ }
407
+ validateRequirementsShape(parsed);
408
+ return pickRequirementsFields(parsed);
409
+ }
410
+ /** Encode payment payload as JSON string (for request body) */
411
+ function encodePayloadBody(payload) {
412
+ return JSON.stringify(payload);
413
+ }
414
+ /** Decode payment payload from JSON string (from request body) */
415
+ function decodePayloadBody(body) {
416
+ let parsed;
417
+ try {
418
+ parsed = JSON.parse(body);
419
+ } catch (e) {
420
+ throw new s402Error("INVALID_PAYLOAD", `Failed to parse s402 payload body: ${e instanceof Error ? e.message : "invalid JSON"}`);
421
+ }
422
+ validatePayloadShape(parsed);
423
+ return pickPayloadFields(parsed);
424
+ }
425
+ /** Encode settlement response as JSON string (for response body) */
426
+ function encodeSettleBody(response) {
427
+ return JSON.stringify(response);
428
+ }
429
+ /** Decode settlement response from JSON string (from response body) */
430
+ function decodeSettleBody(body) {
431
+ let parsed;
432
+ try {
433
+ parsed = JSON.parse(body);
434
+ } catch (e) {
435
+ throw new s402Error("INVALID_PAYLOAD", `Failed to parse s402 settle body: ${e instanceof Error ? e.message : "invalid JSON"}`);
436
+ }
437
+ validateSettleShape(parsed);
438
+ return pickSettleResponseFields(parsed);
439
+ }
440
+ /**
441
+ * Detect transport mode from an incoming request.
442
+ *
443
+ * Checks Content-Type for body transport, then falls back to header detection.
444
+ * Returns 'body' if Content-Type is application/s402+json.
445
+ * Returns 'header' if x-payment header is present.
446
+ * Returns 'unknown' otherwise.
447
+ */
448
+ function detectTransport(request) {
449
+ if (request.headers.get("content-type")?.includes(S402_CONTENT_TYPE)) return "body";
450
+ if (request.headers.get(S402_HEADERS.PAYMENT)) return "header";
451
+ return "unknown";
452
+ }
380
453
  /**
381
454
  * Detect whether a 402 response uses s402 or x402 protocol.
382
455
  *
@@ -412,4 +485,4 @@ function extractRequirementsFromResponse(response) {
412
485
  }
413
486
 
414
487
  //#endregion
415
- export { decodePaymentPayload, decodePaymentRequired, decodeSettleResponse, detectProtocol, encodePaymentPayload, encodePaymentRequired, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, pickPayloadFields, pickRequirementsFields, pickSettleResponseFields, validateEscrowShape, validateMandateShape, validatePrepaidShape, validateRequirementsShape, validateStreamShape, validateSubObjects, validateUnlockShape };
488
+ export { S402_CONTENT_TYPE, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, pickPayloadFields, pickRequirementsFields, pickSettleResponseFields, validateEscrowShape, validateMandateShape, validatePrepaidShape, validateRequirementsShape, validateStreamShape, validateSubObjects, validateUnlockShape };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createS402Error, s402Error, s402ErrorCode, s402ErrorCodeType, s402ErrorInfo } from "./errors.mjs";
2
2
  import { S402_HEADERS, S402_VERSION, s402Discovery, s402EscrowExtra, s402EscrowPayload, s402ExactPayload, s402Mandate, s402MandateRequirements, s402PaymentPayload, s402PaymentPayloadBase, s402PaymentRequirements, s402PaymentSession, s402PrepaidExtra, s402PrepaidPayload, s402RegistryQuery, s402Scheme, s402ServiceEntry, s402SettleResponse, s402SettlementMode, s402StreamExtra, s402StreamPayload, s402UnlockExtra, s402UnlockPayload, s402VerifyResponse } from "./types.mjs";
3
- import { decodePaymentPayload, decodePaymentRequired, decodeSettleResponse, detectProtocol, encodePaymentPayload, encodePaymentRequired, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, validateRequirementsShape } from "./http.mjs";
3
+ import { S402_CONTENT_TYPE, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, validateRequirementsShape } from "./http.mjs";
4
4
 
5
5
  //#region src/scheme.d.ts
6
6
  /** Implemented by each scheme on the client side */
@@ -192,4 +192,4 @@ declare class s402ResourceServer {
192
192
  process(payload: s402PaymentPayload, requirements: s402PaymentRequirements): Promise<s402SettleResponse>;
193
193
  }
194
194
  //#endregion
195
- export { S402_HEADERS, S402_VERSION, createS402Error, decodePaymentPayload, decodePaymentRequired, decodeSettleResponse, detectProtocol, encodePaymentPayload, encodePaymentRequired, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, s402Client, type s402ClientScheme, type s402DirectScheme, type s402Discovery, s402Error, s402ErrorCode, type s402ErrorCodeType, type s402ErrorInfo, type s402EscrowExtra, type s402EscrowPayload, type s402ExactPayload, s402Facilitator, type s402FacilitatorScheme, type s402Mandate, type s402MandateRequirements, type s402PaymentPayload, type s402PaymentPayloadBase, type s402PaymentRequirements, type s402PaymentSession, type s402PrepaidExtra, type s402PrepaidPayload, type s402RegistryQuery, s402ResourceServer, type s402RouteConfig, type s402Scheme, type s402ServerScheme, type s402ServiceEntry, type s402SettleResponse, type s402SettlementMode, type s402StreamExtra, type s402StreamPayload, type s402UnlockExtra, type s402UnlockPayload, type s402VerifyResponse, validateRequirementsShape };
195
+ export { S402_CONTENT_TYPE, S402_HEADERS, S402_VERSION, createS402Error, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, s402Client, type s402ClientScheme, type s402DirectScheme, type s402Discovery, s402Error, s402ErrorCode, type s402ErrorCodeType, type s402ErrorInfo, type s402EscrowExtra, type s402EscrowPayload, type s402ExactPayload, s402Facilitator, type s402FacilitatorScheme, type s402Mandate, type s402MandateRequirements, type s402PaymentPayload, type s402PaymentPayloadBase, type s402PaymentRequirements, type s402PaymentSession, type s402PrepaidExtra, type s402PrepaidPayload, type s402RegistryQuery, s402ResourceServer, type s402RouteConfig, type s402Scheme, type s402ServerScheme, type s402ServiceEntry, type s402SettleResponse, type s402SettlementMode, type s402StreamExtra, type s402StreamPayload, type s402UnlockExtra, type s402UnlockPayload, type s402VerifyResponse, validateRequirementsShape };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { S402_HEADERS, S402_VERSION } from "./types.mjs";
2
2
  import { createS402Error, s402Error, s402ErrorCode } from "./errors.mjs";
3
- import { decodePaymentPayload, decodePaymentRequired, decodeSettleResponse, detectProtocol, encodePaymentPayload, encodePaymentRequired, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, validateRequirementsShape } from "./http.mjs";
3
+ import { S402_CONTENT_TYPE, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, validateRequirementsShape } from "./http.mjs";
4
4
 
5
5
  //#region src/client.ts
6
6
  var s402Client = class {
@@ -317,4 +317,4 @@ var s402Facilitator = class {
317
317
  };
318
318
 
319
319
  //#endregion
320
- export { S402_HEADERS, S402_VERSION, createS402Error, decodePaymentPayload, decodePaymentRequired, decodeSettleResponse, detectProtocol, encodePaymentPayload, encodePaymentRequired, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, s402Client, s402Error, s402ErrorCode, s402Facilitator, s402ResourceServer, validateRequirementsShape };
320
+ export { S402_CONTENT_TYPE, S402_HEADERS, S402_VERSION, createS402Error, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, s402Client, s402Error, s402ErrorCode, s402Facilitator, s402ResourceServer, validateRequirementsShape };
package/dist/types.d.mts CHANGED
@@ -28,9 +28,27 @@ interface s402PaymentRequirements {
28
28
  facilitatorUrl?: string;
29
29
  /** AP2 mandate requirements (if agent spending authorization is needed) */
30
30
  mandate?: s402MandateRequirements;
31
- /** Protocol fee in basis points (0-10000). 0 = no fee. */
31
+ /**
32
+ * Protocol fee in basis points (0-10000). **Advisory only.**
33
+ *
34
+ * This field is a transparency hint for the client's UI — it lets the payer
35
+ * see the total cost before committing. It is NOT the source of truth for
36
+ * settlement math. The authoritative fee rate is owned by the Facilitator
37
+ * (configured in its ProtocolState or equivalent on-chain object) and
38
+ * enforced at the smart contract level.
39
+ *
40
+ * Resource Servers SHOULD omit this field and let the Facilitator provide
41
+ * it via its `/.well-known/s402-facilitator` endpoint. If included, it MUST
42
+ * match the Facilitator's configured rate — a mismatch is a warning sign.
43
+ *
44
+ * Trust model: Facilitator owns the fee. Resource Server cannot override it.
45
+ */
32
46
  protocolFeeBps?: number;
33
- /** Address that receives the protocol fee. Defaults to payTo if omitted. */
47
+ /**
48
+ * Address that receives the protocol fee.
49
+ * Advisory only — authoritative value is in Facilitator's on-chain config.
50
+ * Defaults to the Facilitator's own address if omitted.
51
+ */
34
52
  protocolFeeAddress?: string;
35
53
  /** Whether the server requires an on-chain receipt NFT */
36
54
  receiptRequired?: boolean;
@@ -107,6 +125,19 @@ interface s402PrepaidExtra {
107
125
  minDeposit: string;
108
126
  /** Withdrawal delay in ms. Agent must wait this long after last claim. Min 60s, max 7d. */
109
127
  withdrawalDelayMs: string;
128
+ /**
129
+ * Provider's Ed25519 public key (hex string, 32 bytes).
130
+ * When present, enables v0.2 signed receipt mode — claims enter a pending
131
+ * state and can be disputed with cryptographic fraud proofs.
132
+ * @since v0.2
133
+ */
134
+ providerPubkey?: string;
135
+ /**
136
+ * Dispute window in milliseconds. Min 60s (60000), max 24h (86400000).
137
+ * Only relevant when providerPubkey is set.
138
+ * @since v0.2
139
+ */
140
+ disputeWindowMs?: string;
110
141
  }
111
142
  /** Mandate requirements in a 402 response — tells client what mandate is needed */
112
143
  interface s402MandateRequirements {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "s402",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "s402 — Sui-native HTTP 402 wire format. Types, HTTP encoding, and scheme registry for five payment schemes. Wire-compatible with x402. Zero runtime dependencies.",
6
6
  "license": "Apache-2.0",