@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 +5 -0
- package/docs/UPDATES.md +10 -0
- package/fixtures/webhooks/payment-confirmed-fee-extension.json +15 -0
- package/package.json +1 -1
- package/scripts/sync-openapi.mjs +1 -1
- package/scripts/sync-webhooks.mjs +11 -0
- package/scripts/verify.js +15 -0
- package/specs/openapi/manifest.json +1 -1
- package/specs/webhooks/request-network-webhooks.json +21 -2
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
package/scripts/sync-openapi.mjs
CHANGED
|
@@ -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.
|
|
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}`);
|
|
@@ -494,7 +494,7 @@
|
|
|
494
494
|
"schemas": {
|
|
495
495
|
"WebhookFeeItem": {
|
|
496
496
|
"type": "object",
|
|
497
|
-
"additionalProperties":
|
|
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
|
}
|