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.
@@ -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 reasonFrom(body) {
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
- const parsed = JSON.parse(body);
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 void 0;
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 reason = reasonFrom(body);
1149
- const retryable = status === 429 || status === 502 || status === 503 || status === 504;
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
- x402Version: paymentHeader.x402Version,
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
- x402Version: paymentHeader.x402Version,
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: isCaip2Network(requirements.network) ? requirements.network : chainToCAIP2(requirements.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.network) || isCaip2Network(requirements.network) ? 2 : 1;
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
- try {
2891
- const parsed = JSON.parse(this.body);
2892
- if (parsed && typeof parsed === "object" && typeof parsed.reason === "string") {
2893
- return parsed.reason;
2894
- }
2895
- } catch {
2896
- }
2897
- return void 0;
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