@dvmkit/sdk 0.7.0-rc.1 → 0.7.0-rc.10
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 +39 -2
- package/dist/{chunk-S3OY533W.js → chunk-2NUFE2PG.js} +47 -5
- package/dist/{chunk-RA2WIX7G.js → chunk-3E4C6W7W.js} +17 -6
- package/dist/{chunk-GY66THYI.js → chunk-53SNRGQQ.js} +372 -153
- package/dist/{chunk-CXCQLOOK.js → chunk-62FI3NJF.js} +42 -107
- package/dist/chunk-6HJEVCVI.js +17 -0
- package/dist/chunk-A262WVYX.js +6 -0
- package/dist/{chunk-IV6Q7NRW.js → chunk-C5RIQWG4.js} +1 -1
- package/dist/{chunk-GAOAYHZA.js → chunk-FBQVOOYF.js} +37 -6
- package/dist/{chunk-Z4ZT5LNT.js → chunk-GEI4MAXE.js} +3 -3
- package/dist/{chunk-OML32F6I.js → chunk-GLIAR7RN.js} +1 -1
- package/dist/{chunk-X7EZ5CLI.js → chunk-HCATFJDD.js} +3 -3
- package/dist/{chunk-BYCEKO65.js → chunk-HPCXF3DV.js} +220 -32
- package/dist/{chunk-6XF3RVVX.js → chunk-IRXIH775.js} +5 -5
- package/dist/{chunk-QR2EF2NI.js → chunk-KCQDXIJU.js} +1 -42
- package/dist/chunk-MDW7ZR7V.js +47 -0
- package/dist/{chunk-KA6VN5FK.js → chunk-MQYM4T66.js} +3 -3
- package/dist/{chunk-P6QON7ER.js → chunk-NV7Z7377.js} +1 -1
- package/dist/{chunk-KMED3H3A.js → chunk-OBBVDKLE.js} +476 -230
- package/dist/{chunk-EQLA4FFY.js → chunk-OHUXCAOM.js} +17 -6
- package/dist/{chunk-FWGWLPXF.js → chunk-PZ5IAWFD.js} +1 -1
- package/dist/{chunk-IXR5RFK7.js → chunk-R3TQSXWT.js} +191 -5
- package/dist/{chunk-VHNPVGSA.js → chunk-T5DIXOLR.js} +3 -3
- package/dist/{chunk-RCM3P2HE.js → chunk-UKD3R2KU.js} +55 -4
- package/dist/chunk-WMGXJQI3.js +120 -0
- package/dist/{chunk-N5RZ5HVB.js → chunk-X6S2CHFM.js} +2 -2
- package/dist/{chunk-ETXDTUJO.js → chunk-ZCNPIILZ.js} +112 -1
- package/dist/{content-retention-DHyLdA6b.d.ts → content-retention-D2K4MKNO.d.ts} +1 -1
- package/dist/{credit-ledger-MCUXYKSJ.js → credit-ledger-MFIIOSZH.js} +4 -4
- package/dist/{credit-menu-DjnkeEUU.d.ts → credit-menu-D2t1-Tf6.d.ts} +13 -6
- package/dist/{fx-zMWFzfrU.d.ts → fx-Ql4nvdbh.d.ts} +10 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +248 -16
- package/dist/internal/caller.d.ts +5032 -8272
- package/dist/internal/caller.js +4302 -8084
- package/dist/internal/index.js +1 -1
- package/dist/internal/server.d.ts +7 -7
- package/dist/internal/server.js +30 -31
- package/dist/{job-store-DdLXBpol.d.ts → job-store-DCvJQUX0.d.ts} +1 -1
- package/dist/{lightning-backend-Bw-qtAbt.d.ts → lightning-backend-CY5ML3km.d.ts} +2 -2
- package/dist/{memory-credit-ledger-SBSMGMDX.js → memory-credit-ledger-44NE4PZC.js} +5 -5
- package/dist/{mpp-setup-RF5FXGGV.js → mpp-setup-HRWUBULX.js} +4 -2
- package/dist/{payout-reporter-3Y2U55ZX.js → payout-reporter-7P66T77I.js} +6 -4
- package/dist/{postgres-consumed-credential-store-BUVCAPLS.js → postgres-consumed-credential-store-IDGPFKIL.js} +3 -3
- package/dist/{postgres-job-store-VHLQBJZC.js → postgres-job-store-UKNJ2F6Z.js} +2 -2
- package/dist/{postgres-replay-store-ARZSQ45X.js → postgres-replay-store-PMA4ADXN.js} +2 -2
- package/dist/{pricing-XT2IAPNE.js → pricing-52XJHJ6K.js} +12 -12
- package/dist/{revenue-reporter-MAERPYGK.js → revenue-reporter-HV7RMS54.js} +2 -2
- package/dist/server/index.d.ts +12 -11
- package/dist/server/index.js +47 -48
- package/dist/{step-cache-2MkvenJn.d.ts → step-cache-BOWAn_BH.d.ts} +84 -7
- package/dist/{tempo-charge-store-Q3D7KYMH.js → tempo-charge-store-P2Z4RMU7.js} +2 -2
- package/dist/{tempo-session-store-ZRNCOX7P.js → tempo-session-store-VSP7KLBS.js} +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +7 -6
- package/dist/{usd-DI3d3gTK.d.ts → usd-MAtMp3DB.d.ts} +1 -1
- package/dist/{x402-FT4S4JS4.js → x402-3IQ3HXSG.js} +8 -26
- package/package.json +1 -1
- package/dist/chunk-AT6V3SY7.js +0 -102
- package/dist/chunk-EZXAJ6EI.js +0 -1082
- package/dist/chunk-HCLVYZUZ.js +0 -244
- package/dist/chunk-JEVLNAO3.js +0 -1668
- package/dist/chunk-NFMOPYBA.js +0 -357
- package/dist/chunk-O2OTZ5GP.js +0 -1869
- package/dist/chunk-OFVV37XC.js +0 -57
- package/dist/chunk-SZVXOQ75.js +0 -211
- package/dist/chunk-WBOKPC37.js +0 -795
- package/dist/tempo-lifecycle-GJ77YKVY.js +0 -15
- package/dist/tempo-wallet-X2GGAZ6X.js +0 -40
package/README.md
CHANGED
|
@@ -92,12 +92,16 @@ Builders can pass `onPaidJobCompleted` to observe settled customer charges after
|
|
|
92
92
|
- A `credit` block opts into reusable prepaid balances and their reclaim obligations. Reusable channels return undrawn collateral through their channel lifecycle; one-payment stablecoin funding is disabled unless `allowOneShotStablecoin: true` because it creates manual refunds. Run and monitor the documented reclaim/payout worker.
|
|
93
93
|
- Honour `ctx.signal`, use `ctx.fetch` for abortable provider calls, and declare provider cost with `ctx.cost` only after the durable step that incurred it.
|
|
94
94
|
|
|
95
|
+
Reusable x402 channels default to a 24-hour payer withdrawal grace (86400 seconds) on every network. For Base Sepolia (`eip155:84532`) acceptance tests, set `x402.batchSettlement.withdrawDelay: 900` or `DVMKIT_X402_WITHDRAW_DELAY_SECONDS=900` with `DVMKIT_X402_BATCH_SETTLEMENT=true`. Explicit delays must be integer seconds and at least 900 on Base Sepolia; Base mainnet retains the 86400 minimum even in development mode and still requires self-relay. The grace is bound into each channel's immutable configuration: changing the server setting affects new channels, not existing deposits. A shorter testnet run exercises early-withdrawal refusal and post-deadline recovery; retain a separate 24-hour run to cover next-day recovery.
|
|
96
|
+
|
|
95
97
|
For Bitcoin credit monitoring, `GET /admin/credit/bitcoin-liability` reports exact in-kind obligations in satoshis. Uncovered and basisless credits need live-rate pricing on reclaim, so their whole balances appear in `uncovered_by_currency` and `unbacked_by_currency` as `{ currency, micro, credits }` entries. Sum the credit counts if useful, but display or price each currency separately. Older consumers must replace the former `uncovered_micro` and `unbacked_micro` scalar reads; the [portable response fixture](./protocol-fixtures/bitcoin-liability.json) shows the full shape.
|
|
96
98
|
|
|
97
99
|
The public [SDK guide](https://dvmkit.com/docs/sdk) covers signed requests, custom routes, rail configuration, credit reclaim, persistence, and deployment in depth. The [protocol guide](https://dvmkit.com/docs/protocol) specifies the HTTP contract.
|
|
98
100
|
|
|
99
101
|
## Diagnostic retention
|
|
100
102
|
|
|
103
|
+
[Payment diagnostics](./docs/DIAGNOSTICS.md) documents the closed failure categories, stages, and top-up causes retained by safe logging sinks.
|
|
104
|
+
|
|
101
105
|
The SDK advertises 730 days of retention for sanitized diagnostics in every existing span dataset: `platform`, `cashu`, `lightning`, `stripe`, `x402`, `tempo`, `isolate`, `audit`, and `cron`. Dataset names, producer permissions, and query semantics remain unchanged; only operator services may write to `audit`.
|
|
102
106
|
|
|
103
107
|
This metadata describes the retention schedule. The SDK does not automatically delete diagnostics or override platform destruction or case controls. The platform must enforce the schedule and those controls.
|
|
@@ -106,7 +110,7 @@ This metadata describes the retention schedule. The SDK does not automatically d
|
|
|
106
110
|
|
|
107
111
|
Use the Node.js release in `.nvmrc` (Node 22). Install with `npm ci`, then run the same gate CI runs with `npm run verify`. The gate checks repository metadata in the checkout, copies only tracked and unignored source into an isolated directory, installs its locked dependencies, and runs lint, build, typecheck (including the release scripts under `checkJs`), unit and database tests, API, package, consumer, packed-artifact safety, and release checks there. The clean source tree owns the complete build and test pass, so verification never depends on the checkout's `node_modules` or pre-existing `dist` and does not repeat the full suite; after every check passes, its verified `dist` replaces the checkout's build output for release packaging.
|
|
108
112
|
|
|
109
|
-
`DATABASE_URL` is optional locally. Without it the database suite is skipped rather than passed, and `verify` says so in its summary, because a skipped suite verified nothing and must not be reported as green. Point `DATABASE_URL` at a scratch Postgres to run it, and pass `npm run verify -- --require-database` to make a missing one a failure, which is what CI does.
|
|
113
|
+
`DATABASE_URL` is optional locally. Without it the database suite is skipped rather than passed, and `verify` says so in its summary, because a skipped suite verified nothing and must not be reported as green. Point `DATABASE_URL` at a scratch Postgres to run it, and pass `npm run verify -- --require-database` to make a missing one a failure, which is what CI does. That mode also requires the local test mint started by `node scripts/start-cashu-mint.mjs`.
|
|
110
114
|
|
|
111
115
|
The public surface is exactly `@dvmkit/sdk`, `@dvmkit/sdk/server`, and `@dvmkit/sdk/testing`.
|
|
112
116
|
|
|
@@ -146,6 +150,10 @@ The same values are accepted as `--endpoint <url>` and `--job-token <hex>`. Thes
|
|
|
146
150
|
|
|
147
151
|
If an upgrade broke something for you, an [API compatibility report](https://github.com/dvmkit/sdk/issues/new/choose) is the most useful thing you can send us. It tells us which parts of the published surface people actually build on, which no test of ours can.
|
|
148
152
|
|
|
153
|
+
## Job identity field
|
|
154
|
+
|
|
155
|
+
Every 200 or 202 response to `POST /v1/job`, `POST /v1/request-resume/:id` and `POST /v1/request-result/:id` carries a `DVM-Job-Identity-V1` response-head field whose value is the body's `job_id`, byte for byte, so a router or caller can learn which job a response names without reading a body of any size. No other status carries it. The value grammar is `^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$`; a server whose job ID falls outside it sends no field. A reader takes the value from `Headers.get`, trims nothing and never folds case: a repeated field (joined with `", "`), an empty value or any other character is invalid, and a trailer of the same name is never identity. The portable contract and its classification vectors are in `protocol-fixtures/job-identity-v1.json`. Hosting control's status reports `jobIdentityHeader: "v1"` when the deployment emits the field.
|
|
156
|
+
|
|
149
157
|
## Per-job costs
|
|
150
158
|
|
|
151
159
|
A handler can declare what a job cost **you**, the builder, to serve — a vendor API call, a GPU minute, a per-request licence fee — and the SDK reports it to the platform alongside what the caller paid, so your dashboard can state a margin instead of guessing at one.
|
|
@@ -167,7 +175,36 @@ Three things worth knowing:
|
|
|
167
175
|
- **Declare the cost where you incur it, including inside `ctx.step`.** The step cache carries those declarations with the result and applies them once when a replay skips the body.
|
|
168
176
|
- **Every terminal job reports its declared cost.** A paid completion carries the cost on its revenue report. A free completion, failure, or cancellation sends a separate cost-only report and does not create revenue.
|
|
169
177
|
|
|
170
|
-
The cost is yours, never the caller's: it is recorded separately from what the caller paid and is never netted against it. Both report shapes use the same durable outbox. Cost-only reports are keyed on the job id and revisioned, so retries and cross-machine updates converge on the latest cumulative value. With the Postgres job store, boot-time and periodic reconciliation enqueue any terminal cost revision that a process exited before reporting.
|
|
178
|
+
The cost is yours, never the caller's: it is recorded separately from what the caller paid and is never netted against it. Both report shapes use the same durable outbox. Cost-only reports are keyed on the job id and revisioned, so retries and cross-machine updates converge on the latest cumulative value. With the Postgres job store, boot-time and periodic reconciliation enqueue any terminal cost revision that a process exited before reporting. Other job stores get no reconciliation: their cost-only report is durable only once the outbox insert commits, so an exit between the terminal save and that insert loses it.
|
|
179
|
+
|
|
180
|
+
## Rate cards
|
|
181
|
+
|
|
182
|
+
A priced capability can tell callers what it charges per unit, so an agent can explain the price before it asks for a quote. Declare `rates` next to `price` or `onQuote`, in the flat shape or on an entry in `capabilities`:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
capabilities: {
|
|
186
|
+
synthesise: {
|
|
187
|
+
onQuote: { schema, handler: quoteBySize },
|
|
188
|
+
rates: [
|
|
189
|
+
{ label: "balanced", amount: "0.00015", currency: "USD", per: "character" },
|
|
190
|
+
{ label: "expressive", amount: "0.00030", currency: "USD", per: "character" },
|
|
191
|
+
],
|
|
192
|
+
onJob,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`/v1/info` publishes the list as `capabilities.<name>.pricing.rates`, exactly as you wrote it: same order, same strings. A capability without `rates` publishes nothing extra. For a quoted capability the rates describe how your quote is computed; the SDK does not compute or check quotes against them, so keep the two in step yourself.
|
|
198
|
+
|
|
199
|
+
The server refuses to start, naming the capability and the entry, when a rate card breaks one of these limits:
|
|
200
|
+
|
|
201
|
+
- 1 to 5 entries, on a capability with `price` or `onQuote`. A free capability cannot declare rates.
|
|
202
|
+
- `amount` is decimal dollars as a string, above zero and at most 12 characters: `"0.00015"`, not `0.00015` or `"$0.00015"`.
|
|
203
|
+
- `currency` is `"USD"`.
|
|
204
|
+
- `per` names the unit (`"character"`, `"episode"`, `"GB per day stored"`) and `label`, which is optional, names a tier. Each is 1 to 24 characters with no leading or trailing spaces.
|
|
205
|
+
- No other keys.
|
|
206
|
+
|
|
207
|
+
The [portable fixture](./protocol-fixtures/capability-rates.json) carries the same limits and the accepted and refused examples, for clients that parse the field.
|
|
171
208
|
|
|
172
209
|
## Contributing
|
|
173
210
|
|
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
2
|
X402_DEFAULT_FACILITATOR
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-62FI3NJF.js";
|
|
4
4
|
import {
|
|
5
5
|
callerLoggers
|
|
6
6
|
} from "./chunk-66HGCPBU.js";
|
|
7
|
+
import {
|
|
8
|
+
diagnosticConsole
|
|
9
|
+
} from "./chunk-ZCNPIILZ.js";
|
|
10
|
+
|
|
11
|
+
// src/observability/payment-diagnostics.ts
|
|
12
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
13
|
+
var scopes = new AsyncLocalStorage();
|
|
14
|
+
function withPaymentDiagnostics(fn) {
|
|
15
|
+
return scopes.run({}, fn);
|
|
16
|
+
}
|
|
17
|
+
async function withPaymentStage(stage, fn) {
|
|
18
|
+
try {
|
|
19
|
+
return await fn();
|
|
20
|
+
} catch (error) {
|
|
21
|
+
const scope = scopes.getStore();
|
|
22
|
+
if (scope && (!scope.failure || !Object.is(scope.failure.error, error))) {
|
|
23
|
+
scope.failure = { error, stage };
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function paymentFailureStage(error) {
|
|
29
|
+
const failure = scopes.getStore()?.failure;
|
|
30
|
+
return failure && Object.is(failure.error, error) ? failure.stage : "unknown";
|
|
31
|
+
}
|
|
7
32
|
|
|
8
33
|
// src/lib/x402/facilitator.ts
|
|
9
34
|
import { createPrivateKey, randomBytes } from "crypto";
|
|
@@ -82,7 +107,7 @@ async function verifyWithFacilitator(payload, requirements, config = {}, createA
|
|
|
82
107
|
return callerLoggers.x402.span(
|
|
83
108
|
"x402.verify_with_facilitator",
|
|
84
109
|
{ facilitator_url: url },
|
|
85
|
-
async () => {
|
|
110
|
+
() => withPaymentStage("verifier", async () => {
|
|
86
111
|
const authHeaders = (await createAuthHeaders?.())?.verify ?? {};
|
|
87
112
|
const res = await fetch(url, {
|
|
88
113
|
method: "POST",
|
|
@@ -96,14 +121,17 @@ async function verifyWithFacilitator(payload, requirements, config = {}, createA
|
|
|
96
121
|
if (!res.ok) {
|
|
97
122
|
const rejection = await readFacilitatorRejection(res);
|
|
98
123
|
if (rejection?.isValid === false && typeof rejection.invalidReason === "string" && rejection.invalidReason.length > 0) {
|
|
124
|
+
logRejected("verifier");
|
|
99
125
|
return rejection;
|
|
100
126
|
}
|
|
127
|
+
logRejected("verifier");
|
|
101
128
|
return { isValid: false, invalidReason: `facilitator_status_${res.status}` };
|
|
102
129
|
}
|
|
103
130
|
const data = await res.json();
|
|
131
|
+
if (!data.isValid) logRejected("verifier");
|
|
104
132
|
callerLoggers.x402.info("x402.verify_with_facilitator.result", { is_valid: data.isValid });
|
|
105
133
|
return data;
|
|
106
|
-
}
|
|
134
|
+
})
|
|
107
135
|
);
|
|
108
136
|
}
|
|
109
137
|
async function settleWithFacilitator(payload, requirements, config = {}, createAuthHeaders = createX402FacilitatorAuthHeaders(config)) {
|
|
@@ -112,7 +140,7 @@ async function settleWithFacilitator(payload, requirements, config = {}, createA
|
|
|
112
140
|
return callerLoggers.x402.span(
|
|
113
141
|
"x402.settle_with_facilitator",
|
|
114
142
|
{ facilitator_url: url },
|
|
115
|
-
async () => {
|
|
143
|
+
() => withPaymentStage("broadcast", async () => {
|
|
116
144
|
const authHeaders = (await createAuthHeaders?.())?.settle ?? {};
|
|
117
145
|
const res = await fetch(url, {
|
|
118
146
|
method: "POST",
|
|
@@ -126,17 +154,20 @@ async function settleWithFacilitator(payload, requirements, config = {}, createA
|
|
|
126
154
|
if (!res.ok) {
|
|
127
155
|
const rejection = await readFacilitatorRejection(res);
|
|
128
156
|
if (rejection?.success === false && typeof rejection.errorReason === "string" && rejection.errorReason.length > 0) {
|
|
157
|
+
logRejected("broadcast");
|
|
129
158
|
return rejection;
|
|
130
159
|
}
|
|
160
|
+
logRejected("broadcast");
|
|
131
161
|
return { success: false, errorReason: `facilitator_status_${res.status}` };
|
|
132
162
|
}
|
|
133
163
|
const data = await res.json();
|
|
164
|
+
if (!data.success) logRejected("broadcast");
|
|
134
165
|
callerLoggers.x402.info("x402.settle_with_facilitator.result", {
|
|
135
166
|
success: data.success,
|
|
136
167
|
tx_hash: data.transaction
|
|
137
168
|
});
|
|
138
169
|
return data;
|
|
139
|
-
}
|
|
170
|
+
})
|
|
140
171
|
);
|
|
141
172
|
}
|
|
142
173
|
async function readFacilitatorRejection(res) {
|
|
@@ -151,8 +182,19 @@ async function readFacilitatorRejection(res) {
|
|
|
151
182
|
function isRecord(value) {
|
|
152
183
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
153
184
|
}
|
|
185
|
+
function logRejected(stage) {
|
|
186
|
+
diagnosticConsole.warn({
|
|
187
|
+
event: "x402_credential_rejected",
|
|
188
|
+
rail: "x402",
|
|
189
|
+
failure_category: stage === "verifier" ? "verification_failed" : "settlement_failed",
|
|
190
|
+
failure_stage: stage
|
|
191
|
+
});
|
|
192
|
+
}
|
|
154
193
|
|
|
155
194
|
export {
|
|
195
|
+
withPaymentDiagnostics,
|
|
196
|
+
withPaymentStage,
|
|
197
|
+
paymentFailureStage,
|
|
156
198
|
createX402FacilitatorAuthHeaders,
|
|
157
199
|
createX402FacilitatorClient,
|
|
158
200
|
verifyWithFacilitator,
|
|
@@ -2,19 +2,20 @@ import {
|
|
|
2
2
|
createX402FacilitatorAuthHeaders,
|
|
3
3
|
settleWithFacilitator,
|
|
4
4
|
verifyWithFacilitator
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import {
|
|
7
|
-
usdcToMsats
|
|
8
|
-
} from "./chunk-NFMOPYBA.js";
|
|
5
|
+
} from "./chunk-2NUFE2PG.js";
|
|
9
6
|
import {
|
|
10
7
|
buildPaymentRequirements,
|
|
11
8
|
decodePayment,
|
|
12
9
|
encodeSettleResponseHeader,
|
|
13
|
-
exactEvmAuthorization
|
|
14
|
-
|
|
10
|
+
exactEvmAuthorization,
|
|
11
|
+
usdcToMsats
|
|
12
|
+
} from "./chunk-62FI3NJF.js";
|
|
15
13
|
import {
|
|
16
14
|
callerLoggers
|
|
17
15
|
} from "./chunk-66HGCPBU.js";
|
|
16
|
+
import {
|
|
17
|
+
diagnosticConsole
|
|
18
|
+
} from "./chunk-ZCNPIILZ.js";
|
|
18
19
|
|
|
19
20
|
// src/lib/x402/verify.ts
|
|
20
21
|
async function verifyX402Payment(paymentHeader, config, requiredUsdcMicro, resource, btcUsdRate, exactVersions = { v1: true, v2: true }) {
|
|
@@ -30,9 +31,11 @@ async function verifyX402Payment(paymentHeader, config, requiredUsdcMicro, resou
|
|
|
30
31
|
try {
|
|
31
32
|
payload = decodePayment(paymentHeader);
|
|
32
33
|
} catch {
|
|
34
|
+
logRejected("malformed_credential", "verifier");
|
|
33
35
|
return { verified: false, amountMsats: 0, invalidReason: "decode_failed" };
|
|
34
36
|
}
|
|
35
37
|
if (payload.x402Version === 1 && !exactVersions.v1 || payload.x402Version === 2 && !exactVersions.v2) {
|
|
38
|
+
logRejected("unsupported_version", "verifier");
|
|
36
39
|
return { verified: false, amountMsats: 0, invalidReason: "unsupported_version" };
|
|
37
40
|
}
|
|
38
41
|
const requirementOpts = { x402Config: config, requiredUsdcMicro, resource };
|
|
@@ -81,6 +84,14 @@ async function verifyX402Payment(paymentHeader, config, requiredUsdcMicro, resou
|
|
|
81
84
|
}
|
|
82
85
|
);
|
|
83
86
|
}
|
|
87
|
+
function logRejected(category, stage) {
|
|
88
|
+
diagnosticConsole.warn({
|
|
89
|
+
event: "x402_credential_rejected",
|
|
90
|
+
rail: "x402",
|
|
91
|
+
failure_category: category,
|
|
92
|
+
failure_stage: stage
|
|
93
|
+
});
|
|
94
|
+
}
|
|
84
95
|
|
|
85
96
|
export {
|
|
86
97
|
verifyX402Payment
|