@marcohefti/request-network-api-contracts 0.7.0 → 0.7.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.
package/README.md CHANGED
@@ -49,6 +49,11 @@ npm run verify
49
49
  `sync:openapi` fetches and normalizes the production Request and Auth APIs, applies only documented compatibility patches,
50
50
  and records raw/normalized hashes. Client code generation remains the responsibility of each client repository.
51
51
 
52
+ Webhook fee items preserve their required `type`, `amount`, and `currency` fields while accepting additional
53
+ provider-supplied fields. This follows an authenticated `payment.confirmed` payload that included an unmodeled fee
54
+ extension. A nullable fee `amount` is a compatibility inference from the related Request API fee schema, not a field
55
+ independently confirmed by that webhook payload.
56
+
52
57
  ### Local schema drift patching
53
58
 
54
59
  See `docs/OPENAPI-0.31.0-AUDIT.md` for the operation inventory and the evidence behind nullable-union, fee-drift,
package/docs/UPDATES.md CHANGED
@@ -12,6 +12,16 @@ change so SDK maintainers know when to regenerate code.
12
12
  - **Notes:** Regeneration impact, required SDK updates, new fixtures
13
13
 
14
14
  ## History
15
+ - **Date:** 2026-09-08
16
+ - **Spec Version:** contracts 0.7.1
17
+ - **Change Summary:** Opened `WebhookFeeItem` to provider-supplied extensions and made its string `amount` nullable;
18
+ the required `type`, `amount`, and `currency` fields remain unchanged. Added a synthetic null-amount/extension
19
+ regression fixture and guards that preserve the behavior through webhook regeneration.
20
+ - **Source:** Authenticated `payment.confirmed` delivery (fee extension) and related Request API fee schema
21
+ (nullable amount).
22
+ - **Notes:** The extra fee field is confirmed by the webhook delivery. Nullable webhook `amount` is an API-schema
23
+ compatibility inference, not an independently observed webhook field. Regenerate TS/PHP clients against 0.7.1.
24
+
15
25
  - **Date:** 2026-09-03
16
26
  - **Spec Version:** Request API 0.31.0; Auth API 0.14.0; contracts 0.7.0
17
27
  - **Change Summary:** Added the separate Auth contract, synchronized all 82 Request operations, removed two obsolete
@@ -0,0 +1,15 @@
1
+ {
2
+ "event": "payment.confirmed",
3
+ "requestId": "synthetic-payment-confirmed-fee-extension",
4
+ "timestamp": "2026-09-08T00:00:00Z",
5
+ "fees": [
6
+ {
7
+ "type": "network",
8
+ "amount": null,
9
+ "currency": "ETH",
10
+ "syntheticExtension": {
11
+ "fixture": "contract-regression"
12
+ }
13
+ }
14
+ ]
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcohefti/request-network-api-contracts",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "private": false,
5
5
  "description": "Shared contracts for Request Network API clients (OpenAPI spec, webhook fixtures).",
6
6
  "license": "MIT",
@@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
 
8
8
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
9
9
  const openApiDirectory = resolve(root, "specs/openapi");
10
- const contractVersion = "0.7.0";
10
+ const contractVersion = "0.7.1";
11
11
 
12
12
  const sources = [
13
13
  {
@@ -64,6 +64,16 @@ async function main() {
64
64
  payerEoaAddress: { type: "string", nullable: true, description: "Connected payer EOA when available." },
65
65
  });
66
66
 
67
+ const webhookFeeItem = schema.components.schemas.WebhookFeeItem;
68
+ webhookFeeItem.additionalProperties = true;
69
+ webhookFeeItem.properties.amount = {
70
+ ...webhookFeeItem.properties.amount,
71
+ type: "string",
72
+ nullable: true,
73
+ description: "Human-readable amount as a string when supplied; nullable for fee records without a settled amount.",
74
+ };
75
+ webhookFeeItem.required = ["type", "amount", "currency"];
76
+
67
77
  Object.assign(schema.components.schemas, {
68
78
  ClientIdLinkedEvent: eventSchema("client_id.linked", {
69
79
  clientId: { type: "string" },
@@ -111,6 +121,7 @@ async function main() {
111
121
 
112
122
  Object.assign(schema.components.examples, {
113
123
  payment_confirmed: { summary: "Official current settlement example", value: JSON.parse(await readFile(resolve(root, "fixtures/webhooks/payment-confirmed.json"), "utf8")) },
124
+ payment_confirmed_fee_extension: { summary: "Synthetic fee-extension compatibility regression", value: JSON.parse(await readFile(resolve(root, "fixtures/webhooks/payment-confirmed-fee-extension.json"), "utf8")) },
114
125
  payment_failed: { summary: "Official current failure example", value: JSON.parse(await readFile(resolve(root, "fixtures/webhooks/payment-failed.json"), "utf8")) },
115
126
  client_id_linked: { summary: "Official hosted onboarding example", value: JSON.parse(await readFile(resolve(root, "fixtures/webhooks/client-id-linked.json"), "utf8")) },
116
127
  kyt_screening_completed: { summary: "Official definitive KYT example", value: JSON.parse(await readFile(resolve(root, "fixtures/webhooks/kyt-screening-completed.json"), "utf8")) },
package/scripts/verify.js CHANGED
@@ -13,6 +13,7 @@ const files = [
13
13
  path.join(root, 'specs', 'openapi', 'manifest.json'),
14
14
  path.join(root, 'specs', 'webhooks', 'request-network-webhooks.json'),
15
15
  path.join(root, 'specs', 'webhooks', 'manifest.json'),
16
+ path.join(root, 'fixtures', 'webhooks', 'payment-confirmed-fee-extension.json'),
16
17
  ];
17
18
 
18
19
  for (const file of files) {
@@ -75,6 +76,20 @@ try {
75
76
  assert(requestSpec['x-contract-patches'].includes('secure-payment-response-shapes'), 'secure-payment response patch is not declared');
76
77
  assert(authSpec['x-contract-patches'].includes('platform-webhook-client-id-auth'), 'Auth webhook security patch is not declared');
77
78
 
79
+ const webhookFeeItem = webhookSpec.components.schemas.WebhookFeeItem;
80
+ assert(webhookSpec.components.schemas.WebhookBase.additionalProperties === true, 'WebhookBase must remain open for event-level extensions');
81
+ assert(webhookFeeItem.additionalProperties === true, 'WebhookFeeItem must allow fee-provider extensions');
82
+ assert(JSON.stringify(webhookFeeItem.required) === JSON.stringify(['type', 'amount', 'currency']), 'WebhookFeeItem must require type, amount, and currency');
83
+ assert(webhookFeeItem.properties.type.type === 'string', 'WebhookFeeItem.type must remain a string');
84
+ assert(webhookFeeItem.properties.amount.type === 'string' && webhookFeeItem.properties.amount.nullable === true, 'WebhookFeeItem.amount must be a nullable string');
85
+ assert(webhookFeeItem.properties.currency.type === 'string', 'WebhookFeeItem.currency must remain a string');
86
+
87
+ const feeExtensionFixture = readJson('fixtures/webhooks/payment-confirmed-fee-extension.json');
88
+ const syntheticFee = feeExtensionFixture.fees?.[0];
89
+ assert(feeExtensionFixture.event === 'payment.confirmed', 'fee-extension fixture must be a payment.confirmed event');
90
+ assert(syntheticFee?.amount === null, 'fee-extension fixture must cover a null fee amount');
91
+ assert(Object.hasOwn(syntheticFee ?? {}, 'syntheticExtension'), 'fee-extension fixture must cover an unknown fee extension');
92
+
78
93
  const createResponse = requestSpec.paths['/v2/secure-payments'].post.responses['201'].content['application/json'].schema;
79
94
  for (const key of ['requestIds', 'securePaymentUrl', 'token']) {
80
95
  assert(createResponse.required.includes(key), `secure-payment response does not require ${key}`);
@@ -1,5 +1,5 @@
1
1
  {
2
- "contractsVersion": "0.7.0",
2
+ "contractsVersion": "0.7.1",
3
3
  "fetchedAt": "2026-09-03T09:13:49.531Z",
4
4
  "defaultEnvironment": "production",
5
5
  "supportedRuntimeHosts": [
@@ -494,7 +494,7 @@
494
494
  "schemas": {
495
495
  "WebhookFeeItem": {
496
496
  "type": "object",
497
- "additionalProperties": false,
497
+ "additionalProperties": true,
498
498
  "properties": {
499
499
  "type": {
500
500
  "type": "string",
@@ -502,7 +502,8 @@
502
502
  },
503
503
  "amount": {
504
504
  "type": "string",
505
- "description": "Human-readable amount as a string."
505
+ "description": "Human-readable amount as a string when supplied; nullable for fee records without a settled amount.",
506
+ "nullable": true
506
507
  },
507
508
  "currency": {
508
509
  "type": "string",
@@ -1318,6 +1319,24 @@
1318
1319
  "attemptedPayerWalletAddress": "0x2e2e5c79f571ef1658d4c2d3684a1fe97dd30570",
1319
1320
  "timestamp": "2026-08-10T10:05:00.000Z"
1320
1321
  }
1322
+ },
1323
+ "payment_confirmed_fee_extension": {
1324
+ "summary": "Synthetic fee-extension compatibility regression",
1325
+ "value": {
1326
+ "event": "payment.confirmed",
1327
+ "requestId": "synthetic-payment-confirmed-fee-extension",
1328
+ "timestamp": "2026-09-08T00:00:00Z",
1329
+ "fees": [
1330
+ {
1331
+ "type": "network",
1332
+ "amount": null,
1333
+ "currency": "ETH",
1334
+ "syntheticExtension": {
1335
+ "fixture": "contract-regression"
1336
+ }
1337
+ }
1338
+ ]
1339
+ }
1321
1340
  }
1322
1341
  }
1323
1342
  }