uvd-x402-sdk 2.78.0 → 2.80.0
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 +107 -0
- package/dist/backend/index.d.mts +179 -21
- package/dist/backend/index.d.ts +179 -21
- package/dist/backend/index.js +133 -31
- package/dist/backend/index.js.map +1 -1
- package/dist/backend/index.mjs +131 -32
- package/dist/backend/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +102 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +100 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/backend/facilitator-error.ts +201 -11
- package/src/backend/index.ts +201 -44
- package/src/index.ts +6 -0
package/dist/backend/index.mjs
CHANGED
|
@@ -1106,6 +1106,7 @@ var REPLAYABLE_LEASE_REASONS = [
|
|
|
1106
1106
|
"body_unreadable"
|
|
1107
1107
|
];
|
|
1108
1108
|
var AMBIGUOUS_LEASE_REASONS = ["forward_failed"];
|
|
1109
|
+
var SETTLEMENT_UNCONFIRMED = "settlement_unconfirmed";
|
|
1109
1110
|
var MAX_RETRY_AFTER_SECONDS = 15;
|
|
1110
1111
|
var DEFAULT_RETRY_AFTER_SECONDS = 5;
|
|
1111
1112
|
var DEFAULT_FACILITATOR_RETRIES = 2;
|
|
@@ -1127,15 +1128,44 @@ function parseRetryAfterSeconds(response) {
|
|
|
1127
1128
|
if (!Number.isFinite(seconds) || seconds < 0) return void 0;
|
|
1128
1129
|
return Math.min(seconds, MAX_RETRY_AFTER_SECONDS);
|
|
1129
1130
|
}
|
|
1130
|
-
function
|
|
1131
|
+
function stringField(source, key) {
|
|
1132
|
+
const value = source[key];
|
|
1133
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1134
|
+
}
|
|
1135
|
+
function transactionHashIn(source) {
|
|
1136
|
+
const tx = source.transaction;
|
|
1137
|
+
if (tx && typeof tx === "object") {
|
|
1138
|
+
const hash = tx.hash;
|
|
1139
|
+
if (typeof hash === "string" && hash !== "") return hash;
|
|
1140
|
+
}
|
|
1141
|
+
if (typeof tx === "string" && tx !== "") return tx;
|
|
1142
|
+
for (const key of ["txHash", "tx_hash", "transaction_hash"]) {
|
|
1143
|
+
const value = stringField(source, key);
|
|
1144
|
+
if (value !== void 0) return value;
|
|
1145
|
+
}
|
|
1146
|
+
return void 0;
|
|
1147
|
+
}
|
|
1148
|
+
function parseFacilitatorErrorBody(body) {
|
|
1149
|
+
let parsed;
|
|
1131
1150
|
try {
|
|
1132
|
-
|
|
1133
|
-
if (parsed && typeof parsed === "object" && typeof parsed.reason === "string") {
|
|
1134
|
-
return parsed.reason;
|
|
1135
|
-
}
|
|
1151
|
+
parsed = JSON.parse(body);
|
|
1136
1152
|
} catch {
|
|
1153
|
+
return {};
|
|
1137
1154
|
}
|
|
1138
|
-
return
|
|
1155
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
1156
|
+
return {
|
|
1157
|
+
errorCode: stringField(parsed, "error"),
|
|
1158
|
+
reason: stringField(parsed, "reason"),
|
|
1159
|
+
transaction: transactionHashIn(parsed),
|
|
1160
|
+
paymentId: stringField(parsed, "paymentId"),
|
|
1161
|
+
retryable: typeof parsed.retryable === "boolean" ? parsed.retryable : void 0
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
function isDeclaredUnretryable(parsed) {
|
|
1165
|
+
return parsed.retryable === false || parsed.errorCode === SETTLEMENT_UNCONFIRMED || parsed.transaction !== void 0;
|
|
1166
|
+
}
|
|
1167
|
+
function isSettlementUnconfirmed(failure) {
|
|
1168
|
+
return failure.errorCode === SETTLEMENT_UNCONFIRMED;
|
|
1139
1169
|
}
|
|
1140
1170
|
async function readFacilitatorError(response) {
|
|
1141
1171
|
let body = "";
|
|
@@ -1145,14 +1175,19 @@ async function readFacilitatorError(response) {
|
|
|
1145
1175
|
body = "";
|
|
1146
1176
|
}
|
|
1147
1177
|
const status = response.status;
|
|
1148
|
-
const
|
|
1149
|
-
const
|
|
1178
|
+
const parsed = parseFacilitatorErrorBody(body);
|
|
1179
|
+
const reason = parsed.reason;
|
|
1180
|
+
const transportRetryable = status === 429 || status === 502 || status === 503 || status === 504;
|
|
1181
|
+
const retryable = transportRetryable && !isDeclaredUnretryable(parsed);
|
|
1150
1182
|
const retryAfterSeconds = retryable ? parseRetryAfterSeconds(response) ?? DEFAULT_RETRY_AFTER_SECONDS : void 0;
|
|
1151
|
-
const safeToReplay = status === 429 || status === 503 && isReplayableLeaseReason(reason);
|
|
1183
|
+
const safeToReplay = retryable && (status === 429 || status === 503 && isReplayableLeaseReason(reason));
|
|
1152
1184
|
return {
|
|
1153
1185
|
error: `Facilitator error: ${status} - ${body}`,
|
|
1154
1186
|
status,
|
|
1155
1187
|
reason,
|
|
1188
|
+
errorCode: parsed.errorCode,
|
|
1189
|
+
transaction: parsed.transaction,
|
|
1190
|
+
paymentId: parsed.paymentId,
|
|
1156
1191
|
retryAfterSeconds,
|
|
1157
1192
|
retryable,
|
|
1158
1193
|
safeToReplay,
|
|
@@ -1165,6 +1200,11 @@ function failureFields(info) {
|
|
|
1165
1200
|
retryable: info.retryable,
|
|
1166
1201
|
safeToReplay: info.safeToReplay,
|
|
1167
1202
|
...info.reason !== void 0 ? { reason: info.reason } : {},
|
|
1203
|
+
...info.errorCode !== void 0 ? { errorCode: info.errorCode } : {},
|
|
1204
|
+
// Without these the caller is told "do not retry" and given nothing to do
|
|
1205
|
+
// instead — an error that swallows the hash is the same defect one layer up.
|
|
1206
|
+
...info.transaction !== void 0 ? { transaction: info.transaction } : {},
|
|
1207
|
+
...info.paymentId !== void 0 ? { paymentId: info.paymentId } : {},
|
|
1168
1208
|
...info.retryAfterSeconds !== void 0 ? { retryAfterSeconds: info.retryAfterSeconds } : {}
|
|
1169
1209
|
};
|
|
1170
1210
|
}
|
|
@@ -1172,6 +1212,9 @@ function carryFailureFields(source) {
|
|
|
1172
1212
|
return {
|
|
1173
1213
|
...source.status !== void 0 ? { status: source.status } : {},
|
|
1174
1214
|
...source.reason !== void 0 ? { reason: source.reason } : {},
|
|
1215
|
+
...source.errorCode !== void 0 ? { errorCode: source.errorCode } : {},
|
|
1216
|
+
...source.transaction !== void 0 ? { transaction: source.transaction } : {},
|
|
1217
|
+
...source.paymentId !== void 0 ? { paymentId: source.paymentId } : {},
|
|
1175
1218
|
...source.retryable !== void 0 ? { retryable: source.retryable } : {},
|
|
1176
1219
|
...source.retryAfterSeconds !== void 0 ? { retryAfterSeconds: source.retryAfterSeconds } : {},
|
|
1177
1220
|
...source.safeToReplay !== void 0 ? { safeToReplay: source.safeToReplay } : {}
|
|
@@ -1259,7 +1302,23 @@ function buildPaymentRequirements(options) {
|
|
|
1259
1302
|
}
|
|
1260
1303
|
function buildVerifyRequest(paymentHeader, requirements) {
|
|
1261
1304
|
return {
|
|
1262
|
-
|
|
1305
|
+
// The literal `1` names THIS ENVELOPE, not the payer's header. Echoing
|
|
1306
|
+
// `paymentHeader.x402Version` here -- what this did until 2026-09-04 --
|
|
1307
|
+
// let a buyer who declared `2` produce a body that says "2" while carrying
|
|
1308
|
+
// `paymentRequirements`, which is the v1 shape. The facilitator serves it
|
|
1309
|
+
// anyway because its envelope enum is untagged and matches on shape, so
|
|
1310
|
+
// nothing broke; but it ALREADY picks the hint in its 400 off this marker:
|
|
1311
|
+
//
|
|
1312
|
+
// "This body declares `x402Version: 2`. x402 v2 is a JSON object with
|
|
1313
|
+
// `paymentPayload`, `resource` and `accepted`..."
|
|
1314
|
+
//
|
|
1315
|
+
// So the day that body fails for any other reason, the diagnosis sends the
|
|
1316
|
+
// integrator to document the wrong shape. That inversion -- being told to
|
|
1317
|
+
// fix the fields when the wrapper is what is wrong -- is what cost two
|
|
1318
|
+
// teams a day. The payer's own marker survives untouched inside
|
|
1319
|
+
// `paymentPayload`, where it belongs: it describes the payment, not the
|
|
1320
|
+
// envelope carrying it.
|
|
1321
|
+
x402Version: 1,
|
|
1263
1322
|
paymentPayload: paymentHeader,
|
|
1264
1323
|
paymentRequirements: requirements
|
|
1265
1324
|
};
|
|
@@ -1282,13 +1341,21 @@ function buildSettleRequestV2(payload, resource, accepted) {
|
|
|
1282
1341
|
}
|
|
1283
1342
|
function buildSettleRequest(paymentHeader, requirements) {
|
|
1284
1343
|
return {
|
|
1285
|
-
|
|
1344
|
+
// `1` for the same reason as {@link buildVerifyRequest}: it names the
|
|
1345
|
+
// envelope, and `/settle` takes the same body as `/verify`.
|
|
1346
|
+
x402Version: 1,
|
|
1286
1347
|
paymentPayload: paymentHeader,
|
|
1287
1348
|
paymentRequirements: requirements
|
|
1288
1349
|
};
|
|
1289
1350
|
}
|
|
1290
1351
|
function isCaip2Network(network) {
|
|
1291
|
-
return network.includes(":");
|
|
1352
|
+
return typeof network === "string" && network.includes(":");
|
|
1353
|
+
}
|
|
1354
|
+
function networkOfPayload(payload) {
|
|
1355
|
+
const top = payload.network;
|
|
1356
|
+
if (typeof top === "string") return top;
|
|
1357
|
+
const accepted = payload.accepted;
|
|
1358
|
+
return typeof accepted?.network === "string" ? accepted.network : void 0;
|
|
1292
1359
|
}
|
|
1293
1360
|
function toResourceInfoV2(requirements) {
|
|
1294
1361
|
return {
|
|
@@ -1298,9 +1365,15 @@ function toResourceInfoV2(requirements) {
|
|
|
1298
1365
|
};
|
|
1299
1366
|
}
|
|
1300
1367
|
function toPaymentRequirementsV2(requirements) {
|
|
1368
|
+
const network = isCaip2Network(requirements.network) ? requirements.network : chainToCAIP2(requirements.network);
|
|
1369
|
+
if (!isCaip2Network(network)) {
|
|
1370
|
+
throw new Error(
|
|
1371
|
+
`Network '${requirements.network}' has no CAIP-2 form, so it cannot travel in the x402 v2 envelope. Use x402Version: 1 for this network.`
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1301
1374
|
return {
|
|
1302
1375
|
scheme: requirements.scheme,
|
|
1303
|
-
network
|
|
1376
|
+
network,
|
|
1304
1377
|
asset: requirements.asset,
|
|
1305
1378
|
amount: requirements.maxAmountRequired,
|
|
1306
1379
|
payTo: requirements.payTo,
|
|
@@ -1313,7 +1386,7 @@ function resolveEnvelopeVersion(paymentHeader, requirements, requested = "auto")
|
|
|
1313
1386
|
if (requested !== "auto") {
|
|
1314
1387
|
return requested;
|
|
1315
1388
|
}
|
|
1316
|
-
return isCaip2Network(paymentHeader
|
|
1389
|
+
return isCaip2Network(networkOfPayload(paymentHeader)) || isCaip2Network(requirements.network) ? 2 : 1;
|
|
1317
1390
|
}
|
|
1318
1391
|
function buildVerifyRequestForVersion(paymentHeader, requirements, version) {
|
|
1319
1392
|
if (version === 2) {
|
|
@@ -1986,16 +2059,23 @@ function createPaymentMiddleware(getRequirements, options = {}) {
|
|
|
1986
2059
|
respondUnavailable(res, "Payment settlement unavailable", settleResult);
|
|
1987
2060
|
return;
|
|
1988
2061
|
}
|
|
1989
|
-
res.status(500).json(
|
|
1990
|
-
error: "Payment settlement failed",
|
|
1991
|
-
reason: settleResult.error || "Unknown settlement error"
|
|
1992
|
-
});
|
|
2062
|
+
res.status(500).json(settlementFailureBody(settleResult, "Unknown settlement error"));
|
|
1993
2063
|
return;
|
|
1994
2064
|
}
|
|
1995
2065
|
}
|
|
1996
2066
|
next();
|
|
1997
2067
|
};
|
|
1998
2068
|
}
|
|
2069
|
+
function settlementFailureBody(failure, fallbackReason) {
|
|
2070
|
+
return {
|
|
2071
|
+
error: "Payment settlement failed",
|
|
2072
|
+
reason: failure.error || fallbackReason,
|
|
2073
|
+
retryable: false,
|
|
2074
|
+
...failure.errorCode !== void 0 ? { errorCode: failure.errorCode } : {},
|
|
2075
|
+
...failure.transaction !== void 0 ? { transaction: failure.transaction } : {},
|
|
2076
|
+
...failure.paymentId !== void 0 ? { paymentId: failure.paymentId } : {}
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
1999
2079
|
function honoUnavailable(c, message, failure) {
|
|
2000
2080
|
const seconds = Math.max(1, Math.ceil(failure.retryAfterSeconds ?? DEFAULT_RETRY_AFTER_SECONDS));
|
|
2001
2081
|
c.header?.("Retry-After", String(seconds));
|
|
@@ -2070,10 +2150,7 @@ function createHonoMiddleware(options) {
|
|
|
2070
2150
|
if (settleResult.retryable) {
|
|
2071
2151
|
return honoUnavailable(c, "Payment settlement unavailable", settleResult);
|
|
2072
2152
|
}
|
|
2073
|
-
return c.json(
|
|
2074
|
-
error: "Payment settlement failed",
|
|
2075
|
-
reason: settleResult.error || "Unknown error"
|
|
2076
|
-
}, 500);
|
|
2153
|
+
return c.json(settlementFailureBody(settleResult, "Unknown error"), 500);
|
|
2077
2154
|
}
|
|
2078
2155
|
}
|
|
2079
2156
|
await next();
|
|
@@ -2876,8 +2953,19 @@ var Erc8004LookupError = class extends Error {
|
|
|
2876
2953
|
* `502` and `504` join `503` and `429` here: a gateway that answered on the
|
|
2877
2954
|
* facilitator's behalf is exactly as silent about the agent's existence, and
|
|
2878
2955
|
* reading either as absence has the same consequence -- a duplicate mint.
|
|
2956
|
+
*
|
|
2957
|
+
* **Except when the body says otherwise.** `POST /register` goes through the
|
|
2958
|
+
* same EVM `send_transaction_from` as a settle, so it can answer
|
|
2959
|
+
* `settlement_unconfirmed`: the mint was broadcast and may be mined. That is
|
|
2960
|
+
* a `502` where retrying is precisely the thing that mints the duplicate this
|
|
2961
|
+
* class exists to prevent, so an explicit `retryable: false` wins over the
|
|
2962
|
+
* status. See {@link SETTLEMENT_UNCONFIRMED}.
|
|
2879
2963
|
*/
|
|
2880
2964
|
get retryable() {
|
|
2965
|
+
const parsed = parseFacilitatorErrorBody(this.body);
|
|
2966
|
+
if (parsed.retryable === false || parsed.errorCode === SETTLEMENT_UNCONFIRMED) {
|
|
2967
|
+
return false;
|
|
2968
|
+
}
|
|
2881
2969
|
return this.status === 429 || this.status === 502 || this.status === 503 || this.status === 504;
|
|
2882
2970
|
}
|
|
2883
2971
|
/**
|
|
@@ -2887,14 +2975,25 @@ var Erc8004LookupError = class extends Error {
|
|
|
2887
2975
|
* request may be re-sent; see {@link isReplayableLeaseReason}.
|
|
2888
2976
|
*/
|
|
2889
2977
|
get reason() {
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2978
|
+
return parseFacilitatorErrorBody(this.body).reason;
|
|
2979
|
+
}
|
|
2980
|
+
/** The facilitator's machine-readable `error` code, when the body carried one. */
|
|
2981
|
+
get errorCode() {
|
|
2982
|
+
return parseFacilitatorErrorBody(this.body).errorCode;
|
|
2983
|
+
}
|
|
2984
|
+
/**
|
|
2985
|
+
* A transaction that WAS broadcast and could not be confirmed.
|
|
2986
|
+
*
|
|
2987
|
+
* Present on `settlement_unconfirmed`. This is what to do INSTEAD of
|
|
2988
|
+
* retrying: look it up on chain. An error that carries "do not retry" and no
|
|
2989
|
+
* hash leaves the caller with nothing to act on.
|
|
2990
|
+
*/
|
|
2991
|
+
get transaction() {
|
|
2992
|
+
return parseFacilitatorErrorBody(this.body).transaction;
|
|
2993
|
+
}
|
|
2994
|
+
/** The payment id for {@link transaction}, identical to a successful settle's. */
|
|
2995
|
+
get paymentId() {
|
|
2996
|
+
return parseFacilitatorErrorBody(this.body).paymentId;
|
|
2898
2997
|
}
|
|
2899
2998
|
/**
|
|
2900
2999
|
* The facilitator NAMED a reason proving it executed nothing.
|
|
@@ -2904,7 +3003,7 @@ var Erc8004LookupError = class extends Error {
|
|
|
2904
3003
|
* this is false is the sequence that minted five duplicate agents.
|
|
2905
3004
|
*/
|
|
2906
3005
|
get safeToReplay() {
|
|
2907
|
-
return this.status === 429 || this.status === 503 && isReplayableLeaseReason(this.reason);
|
|
3006
|
+
return this.retryable && (this.status === 429 || this.status === 503 && isReplayableLeaseReason(this.reason));
|
|
2908
3007
|
}
|
|
2909
3008
|
/** Seconds to wait before retrying, clamped. Absent when not retryable. */
|
|
2910
3009
|
get retryAfterSeconds() {
|
|
@@ -4741,6 +4840,6 @@ var AdvancedEscrowClient = class {
|
|
|
4741
4840
|
}
|
|
4742
4841
|
};
|
|
4743
4842
|
|
|
4744
|
-
export { AMBIGUOUS_LEASE_REASONS, AdvancedEscrowClient, BASE_MAINNET_CONTRACTS, BazaarClient, DEFAULT_FACILITATOR_RETRIES, DEFAULT_RETRY_AFTER_SECONDS, DEPOSIT_LIMIT_USDC, ERC8004_CONTRACTS, ERC8004_EXTENSION_ID, ESCROW_CONTRACTS, ESCROW_TIMEOUT_MS, Erc8004Client, Erc8004LookupError, EscrowClient, FacilitatorClient, HEALTH_FILTERS, MAX_RETRY_AFTER_SECONDS, MAX_SEARCH_LEN, OPERATOR_ABI, OPERATOR_ABI_CREATE3, PAYMENT_INFO_TYPEHASH, RELAYED_FEEDBACK_NETWORKS, REPLAYABLE_LEASE_REASONS, RegistrationPendingError, TIER_FILTERS, TIER_TIMINGS, USDC_DOMAIN_NAME, WRITER_LEASE_REASONS, X402_CORS_HEADERS, X402_HEADER_NAMES, ZERO_ADDRESS, buildErc8004PaymentRequirements, buildPaymentRequirements, buildSettleRequest, buildSettleRequestForVersion, buildSettleRequestV2, buildVerifyRequest, buildVerifyRequestForVersion, buildVerifyRequestV2, canRefundEscrow, canReleaseEscrow, carryFailureFields, create402Response, createHonoMiddleware, createPaymentMiddleware, epochToDate, escrowTimeRemaining, extractPaymentFromHeaders, facilitatorFetch, getCorsHeaders, getEscrowContractsByChainId, getEscrowSupportedChainIds, isAlive, isAmbiguousLeaseReason, isEscrowExpired, isEscrowSupportedOnChain, isRegisterJobTerminal, isReplayableLeaseReason, parsePaymentHeader, parseRetryAfterSeconds, readFacilitatorError, resolveEnvelopeVersion, supportsRelayedFeedback, toPaymentRequirementsV2, toResourceInfoV2, wireNetwork };
|
|
4843
|
+
export { AMBIGUOUS_LEASE_REASONS, AdvancedEscrowClient, BASE_MAINNET_CONTRACTS, BazaarClient, DEFAULT_FACILITATOR_RETRIES, DEFAULT_RETRY_AFTER_SECONDS, DEPOSIT_LIMIT_USDC, ERC8004_CONTRACTS, ERC8004_EXTENSION_ID, ESCROW_CONTRACTS, ESCROW_TIMEOUT_MS, Erc8004Client, Erc8004LookupError, EscrowClient, FacilitatorClient, HEALTH_FILTERS, MAX_RETRY_AFTER_SECONDS, MAX_SEARCH_LEN, OPERATOR_ABI, OPERATOR_ABI_CREATE3, PAYMENT_INFO_TYPEHASH, RELAYED_FEEDBACK_NETWORKS, REPLAYABLE_LEASE_REASONS, RegistrationPendingError, SETTLEMENT_UNCONFIRMED, TIER_FILTERS, TIER_TIMINGS, USDC_DOMAIN_NAME, WRITER_LEASE_REASONS, X402_CORS_HEADERS, X402_HEADER_NAMES, ZERO_ADDRESS, buildErc8004PaymentRequirements, buildPaymentRequirements, buildSettleRequest, buildSettleRequestForVersion, buildSettleRequestV2, buildVerifyRequest, buildVerifyRequestForVersion, buildVerifyRequestV2, canRefundEscrow, canReleaseEscrow, carryFailureFields, create402Response, createHonoMiddleware, createPaymentMiddleware, epochToDate, escrowTimeRemaining, extractPaymentFromHeaders, facilitatorFetch, getCorsHeaders, getEscrowContractsByChainId, getEscrowSupportedChainIds, isAlive, isAmbiguousLeaseReason, isEscrowExpired, isEscrowSupportedOnChain, isRegisterJobTerminal, isReplayableLeaseReason, isSettlementUnconfirmed, parseFacilitatorErrorBody, parsePaymentHeader, parseRetryAfterSeconds, readFacilitatorError, resolveEnvelopeVersion, supportsRelayedFeedback, toPaymentRequirementsV2, toResourceInfoV2, wireNetwork };
|
|
4745
4844
|
//# sourceMappingURL=index.mjs.map
|
|
4746
4845
|
//# sourceMappingURL=index.mjs.map
|