@piprail/sdk 2.13.0 → 2.14.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/CHANGELOG.md +96 -0
- package/dist/{algorand-AA3WXKW4.js → algorand-6J3IABVF.js} +12 -11
- package/dist/{algorand-FCEECDG6.cjs → algorand-QU6G2DQZ.cjs} +43 -42
- package/dist/{aptos-LY67Q6QF.js → aptos-DHOMTBLZ.js} +11 -10
- package/dist/{aptos-GHJPO6JJ.cjs → aptos-LFTQGBBK.cjs} +41 -40
- package/dist/{chunk-MWBT7MCE.cjs → chunk-GYM46G5L.cjs} +22 -4
- package/dist/{chunk-SC2ZYDHD.js → chunk-PAMKCWVW.js} +22 -4
- package/dist/index.cjs +505 -234
- package/dist/index.d.cts +281 -12
- package/dist/index.d.ts +281 -12
- package/dist/index.js +302 -31
- package/dist/{ledger-mF_SoiDB.d.ts → ledger-uFtXlIHY.d.cts} +31 -1
- package/dist/{ledger-mF_SoiDB.d.cts → ledger-uFtXlIHY.d.ts} +31 -1
- package/dist/{near-FZBUICCS.js → near-5PXTVQ47.js} +14 -5
- package/dist/{near-6KAQVVG2.cjs → near-YNNK7RW7.cjs} +39 -30
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/{solana-ELUWO6N5.js → solana-HITVI3HF.js} +22 -7
- package/dist/{solana-MYF4HBO4.cjs → solana-N3UCNCYE.cjs} +54 -39
- package/dist/{stellar-ASP2THL2.js → stellar-2QT6YYKN.js} +4 -4
- package/dist/{stellar-CRWBSX4E.cjs → stellar-Y3SQN7S5.cjs} +23 -23
- package/dist/{sui-Y4RLKKE2.cjs → sui-4AL4E6XU.cjs} +35 -27
- package/dist/{sui-FZIKZNVI.js → sui-FV5L6WF5.js} +20 -12
- package/dist/{ton-7GKCTC5H.js → ton-5FWDQ4QR.js} +18 -7
- package/dist/{ton-AOR3EURW.cjs → ton-5HDVOTMR.cjs} +33 -22
- package/dist/{tron-BMCWN5SS.js → tron-AZDY7BMQ.js} +10 -10
- package/dist/{tron-OMXB6EW2.cjs → tron-TX2B4N2A.cjs} +39 -39
- package/dist/{xrpl-Y6SQNYLC.cjs → xrpl-5TYDLQFS.cjs} +31 -31
- package/dist/{xrpl-DD7TJL5L.js → xrpl-UJL5J7CY.js} +13 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
parseUnits,
|
|
32
32
|
rejectForeignToken,
|
|
33
33
|
toInsufficientFundsError
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-PAMKCWVW.js";
|
|
35
35
|
|
|
36
36
|
// src/drivers/registry.ts
|
|
37
37
|
var byFamily = /* @__PURE__ */ new Map();
|
|
@@ -1780,6 +1780,37 @@ function buildReceiptExtension(bundle) {
|
|
|
1780
1780
|
if (bundle.attestation) info.receipt = bundle.attestation;
|
|
1781
1781
|
return { [EXT_OFFER_RECEIPT]: { info } };
|
|
1782
1782
|
}
|
|
1783
|
+
var EXT_PAYMENT_IDENTIFIER = "payment-identifier";
|
|
1784
|
+
var PAYMENT_ID_MIN = 16;
|
|
1785
|
+
var PAYMENT_ID_MAX = 128;
|
|
1786
|
+
var PAYMENT_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
1787
|
+
function buildPaymentIdentifierAdvertisement() {
|
|
1788
|
+
return {
|
|
1789
|
+
[EXT_PAYMENT_IDENTIFIER]: {
|
|
1790
|
+
info: { required: false },
|
|
1791
|
+
schema: { properties: { id: { type: "string", minLength: PAYMENT_ID_MIN, maxLength: PAYMENT_ID_MAX } } }
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
function readPaymentIdentifier(payload) {
|
|
1796
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
1797
|
+
const ext = payload.extensions;
|
|
1798
|
+
if (typeof ext !== "object" || ext === null) return null;
|
|
1799
|
+
const block = ext[EXT_PAYMENT_IDENTIFIER];
|
|
1800
|
+
if (typeof block !== "object" || block === null) return null;
|
|
1801
|
+
const info = block.info;
|
|
1802
|
+
if (typeof info !== "object" || info === null) return null;
|
|
1803
|
+
const id = info.id;
|
|
1804
|
+
if (id === void 0) return null;
|
|
1805
|
+
if (typeof id !== "string") return { invalid: "payment-identifier id must be a string" };
|
|
1806
|
+
if (id.length < PAYMENT_ID_MIN || id.length > PAYMENT_ID_MAX) {
|
|
1807
|
+
return { invalid: `payment-identifier id must be ${PAYMENT_ID_MIN}\u2013${PAYMENT_ID_MAX} chars (got ${id.length})` };
|
|
1808
|
+
}
|
|
1809
|
+
if (!PAYMENT_ID_RE.test(id)) {
|
|
1810
|
+
return { invalid: "payment-identifier id must match [A-Za-z0-9_-]" };
|
|
1811
|
+
}
|
|
1812
|
+
return id;
|
|
1813
|
+
}
|
|
1783
1814
|
function buildSignatureHeader(signature) {
|
|
1784
1815
|
return toBase64Json(signature);
|
|
1785
1816
|
}
|
|
@@ -1806,7 +1837,10 @@ function parseReceipt(response) {
|
|
|
1806
1837
|
const headerValue = response.headers.get(HEADER_RESPONSE) ?? response.headers.get(HEADER_RESPONSE_V1);
|
|
1807
1838
|
if (!headerValue) return null;
|
|
1808
1839
|
const parsed = fromBase64Json(headerValue);
|
|
1809
|
-
|
|
1840
|
+
if (!isValidReceipt(parsed)) return null;
|
|
1841
|
+
const r = parsed;
|
|
1842
|
+
if (typeof r.transaction !== "string" && typeof r.txHash === "string") r.transaction = r.txHash;
|
|
1843
|
+
return r;
|
|
1810
1844
|
}
|
|
1811
1845
|
function parseReceiptExtension(response) {
|
|
1812
1846
|
const headerValue = response.headers.get(HEADER_RESPONSE) ?? response.headers.get(HEADER_RESPONSE_V1);
|
|
@@ -1836,7 +1870,8 @@ function parseSettleResponse(response) {
|
|
|
1836
1870
|
if (!parsed || typeof parsed !== "object" || typeof parsed.success !== "boolean") return null;
|
|
1837
1871
|
return {
|
|
1838
1872
|
success: parsed.success,
|
|
1839
|
-
|
|
1873
|
+
// Tolerate the legacy v1 `txHash` alias for `transaction` (mirrors isValidReceipt/parseReceipt).
|
|
1874
|
+
...typeof parsed.transaction === "string" ? { transaction: parsed.transaction } : typeof parsed.txHash === "string" ? { transaction: parsed.txHash } : {},
|
|
1840
1875
|
...typeof parsed.network === "string" ? { network: parsed.network } : {},
|
|
1841
1876
|
...typeof parsed.payer === "string" ? { payer: parsed.payer } : {},
|
|
1842
1877
|
...typeof parsed.errorReason === "string" ? { errorReason: parsed.errorReason } : {},
|
|
@@ -1855,6 +1890,14 @@ function parseSignatureObject(parsed) {
|
|
|
1855
1890
|
if (!payload || typeof payload.txHash !== "string" || typeof payload.nonce !== "string") {
|
|
1856
1891
|
return null;
|
|
1857
1892
|
}
|
|
1893
|
+
if (!accepted) {
|
|
1894
|
+
const synthesized = {
|
|
1895
|
+
scheme: "onchain-proof",
|
|
1896
|
+
...typeof v.network === "string" ? { network: v.network } : {},
|
|
1897
|
+
...typeof v.asset === "string" ? { asset: v.asset } : {}
|
|
1898
|
+
};
|
|
1899
|
+
return { ...v, accepted: synthesized, payload };
|
|
1900
|
+
}
|
|
1858
1901
|
return parsed;
|
|
1859
1902
|
}
|
|
1860
1903
|
function parseSignatureHeader(value) {
|
|
@@ -1994,7 +2037,11 @@ var evmDriver = {
|
|
|
1994
2037
|
resolve(opts) {
|
|
1995
2038
|
if (typeof opts.chain === "string" && /^(solana|ton|stellar)/.test(opts.chain)) return null;
|
|
1996
2039
|
if (typeof opts.chain === "string") {
|
|
1997
|
-
|
|
2040
|
+
try {
|
|
2041
|
+
return makeEvmNetwork(resolveChain(opts.chain, opts.rpcUrl));
|
|
2042
|
+
} catch (err) {
|
|
2043
|
+
throw new UnsupportedNetworkError(err instanceof Error ? err.message : String(err), { cause: err });
|
|
2044
|
+
}
|
|
1998
2045
|
}
|
|
1999
2046
|
let resolved;
|
|
2000
2047
|
try {
|
|
@@ -2039,8 +2086,13 @@ function makeEvmNetwork(resolved) {
|
|
|
2039
2086
|
`chain ${network} is EVM; a custom token must be { address, decimals }.`
|
|
2040
2087
|
);
|
|
2041
2088
|
}
|
|
2089
|
+
if (!isAddress(token.address)) {
|
|
2090
|
+
throw new WrongFamilyError(
|
|
2091
|
+
`chain ${network} is EVM, but token address "${token.address}" is not a valid 0x address.`
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2042
2094
|
return {
|
|
2043
|
-
asset: token.address,
|
|
2095
|
+
asset: getAddress5(token.address),
|
|
2044
2096
|
decimals: token.decimals,
|
|
2045
2097
|
...token.symbol ? { symbol: token.symbol } : {}
|
|
2046
2098
|
};
|
|
@@ -2121,9 +2173,9 @@ function makeEvmNetwork(resolved) {
|
|
|
2121
2173
|
},
|
|
2122
2174
|
async estimateCost(accept) {
|
|
2123
2175
|
const { decimals, symbol } = resolved.chain.nativeCurrency;
|
|
2124
|
-
if (accept.scheme === "exact") {
|
|
2125
|
-
const m = accept.extra
|
|
2126
|
-
const permit2 = m === "permit2" || m === "permit2-exact";
|
|
2176
|
+
if (accept.scheme === "exact" || accept.scheme === "upto") {
|
|
2177
|
+
const m = accept.extra?.assetTransferMethod;
|
|
2178
|
+
const permit2 = m === "permit2" || m === "permit2-exact" || m === "permit2-upto";
|
|
2127
2179
|
return nativeCost({
|
|
2128
2180
|
symbol,
|
|
2129
2181
|
decimals,
|
|
@@ -2329,7 +2381,7 @@ var loaders = {
|
|
|
2329
2381
|
solana: async () => {
|
|
2330
2382
|
let mod;
|
|
2331
2383
|
try {
|
|
2332
|
-
mod = await import("./solana-
|
|
2384
|
+
mod = await import("./solana-HITVI3HF.js");
|
|
2333
2385
|
} catch (cause) {
|
|
2334
2386
|
throw new MissingDriverError(
|
|
2335
2387
|
`Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
|
|
@@ -2341,7 +2393,7 @@ var loaders = {
|
|
|
2341
2393
|
ton: async () => {
|
|
2342
2394
|
let mod;
|
|
2343
2395
|
try {
|
|
2344
|
-
mod = await import("./ton-
|
|
2396
|
+
mod = await import("./ton-5FWDQ4QR.js");
|
|
2345
2397
|
} catch (cause) {
|
|
2346
2398
|
throw new MissingDriverError(
|
|
2347
2399
|
`TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
|
|
@@ -2353,7 +2405,7 @@ var loaders = {
|
|
|
2353
2405
|
stellar: async () => {
|
|
2354
2406
|
let mod;
|
|
2355
2407
|
try {
|
|
2356
|
-
mod = await import("./stellar-
|
|
2408
|
+
mod = await import("./stellar-2QT6YYKN.js");
|
|
2357
2409
|
} catch (cause) {
|
|
2358
2410
|
throw new MissingDriverError(
|
|
2359
2411
|
`Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
|
|
@@ -2365,7 +2417,7 @@ var loaders = {
|
|
|
2365
2417
|
xrpl: async () => {
|
|
2366
2418
|
let mod;
|
|
2367
2419
|
try {
|
|
2368
|
-
mod = await import("./xrpl-
|
|
2420
|
+
mod = await import("./xrpl-UJL5J7CY.js");
|
|
2369
2421
|
} catch (cause) {
|
|
2370
2422
|
throw new MissingDriverError(
|
|
2371
2423
|
`XRPL selected, but its package isn't installed. Run: npm install xrpl`,
|
|
@@ -2377,7 +2429,7 @@ var loaders = {
|
|
|
2377
2429
|
tron: async () => {
|
|
2378
2430
|
let mod;
|
|
2379
2431
|
try {
|
|
2380
|
-
mod = await import("./tron-
|
|
2432
|
+
mod = await import("./tron-AZDY7BMQ.js");
|
|
2381
2433
|
} catch (cause) {
|
|
2382
2434
|
throw new MissingDriverError(
|
|
2383
2435
|
`Tron selected, but its package isn't installed. Run: npm install tronweb`,
|
|
@@ -2389,7 +2441,7 @@ var loaders = {
|
|
|
2389
2441
|
sui: async () => {
|
|
2390
2442
|
let mod;
|
|
2391
2443
|
try {
|
|
2392
|
-
mod = await import("./sui-
|
|
2444
|
+
mod = await import("./sui-FV5L6WF5.js");
|
|
2393
2445
|
} catch (cause) {
|
|
2394
2446
|
throw new MissingDriverError(
|
|
2395
2447
|
`Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
|
|
@@ -2401,7 +2453,7 @@ var loaders = {
|
|
|
2401
2453
|
near: async () => {
|
|
2402
2454
|
let mod;
|
|
2403
2455
|
try {
|
|
2404
|
-
mod = await import("./near-
|
|
2456
|
+
mod = await import("./near-5PXTVQ47.js");
|
|
2405
2457
|
} catch (cause) {
|
|
2406
2458
|
throw new MissingDriverError(
|
|
2407
2459
|
`NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
|
|
@@ -2413,7 +2465,7 @@ var loaders = {
|
|
|
2413
2465
|
aptos: async () => {
|
|
2414
2466
|
let mod;
|
|
2415
2467
|
try {
|
|
2416
|
-
mod = await import("./aptos-
|
|
2468
|
+
mod = await import("./aptos-DHOMTBLZ.js");
|
|
2417
2469
|
} catch (cause) {
|
|
2418
2470
|
throw new MissingDriverError(
|
|
2419
2471
|
`Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
|
|
@@ -2425,7 +2477,7 @@ var loaders = {
|
|
|
2425
2477
|
algorand: async () => {
|
|
2426
2478
|
let mod;
|
|
2427
2479
|
try {
|
|
2428
|
-
mod = await import("./algorand-
|
|
2480
|
+
mod = await import("./algorand-6J3IABVF.js");
|
|
2429
2481
|
} catch (cause) {
|
|
2430
2482
|
throw new MissingDriverError(
|
|
2431
2483
|
`Algorand selected, but its package isn't installed. Run: npm install algosdk`,
|
|
@@ -3449,6 +3501,24 @@ var SpendLedger = class {
|
|
|
3449
3501
|
}
|
|
3450
3502
|
};
|
|
3451
3503
|
|
|
3504
|
+
// src/util/exactRecovery.ts
|
|
3505
|
+
function exactSettleCheckHint(family, payerFrom, nonce) {
|
|
3506
|
+
switch (family) {
|
|
3507
|
+
case "evm":
|
|
3508
|
+
return `the EIP-3009 \`authorizationState(${payerFrom}, ${nonce})\` (or, on the Permit2 method, the Permit2 nonce bitmap)`;
|
|
3509
|
+
case "solana":
|
|
3510
|
+
return `the buyer's transaction signature (a duplicate signature is the chain's own replay guard; plus the SPL-Memo nonce when present)`;
|
|
3511
|
+
case "algorand":
|
|
3512
|
+
return `the atomic group / transaction id`;
|
|
3513
|
+
case "aptos":
|
|
3514
|
+
return `the sender's account sequence number`;
|
|
3515
|
+
case "near":
|
|
3516
|
+
return `the access-key nonce (\`${nonce}\`)`;
|
|
3517
|
+
default:
|
|
3518
|
+
return `the single-use marker (\`ref=${nonce}\`)`;
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
|
|
3452
3522
|
// src/client.ts
|
|
3453
3523
|
var DEFAULT_SCHEMES = ["onchain-proof"];
|
|
3454
3524
|
var RECIPIENT_FIX = {
|
|
@@ -4014,7 +4084,8 @@ var PipRailClient = class {
|
|
|
4014
4084
|
* before publishing, so retry with a brief backoff if a fresh listing is missing.
|
|
4015
4085
|
* - Results are cross-scheme (mostly the mainstream `exact` scheme); `fetch()` pays
|
|
4016
4086
|
* `onchain-proof` rails by default, and standard `exact` rails too once you opt in
|
|
4017
|
-
* with `schemes: ['onchain-proof', 'exact']` (EVM EIP-3009/Permit2 + Solana SVM + Algorand
|
|
4087
|
+
* with `schemes: ['onchain-proof', 'exact']` (EVM EIP-3009/Permit2 + Solana SVM + Algorand
|
|
4088
|
+
* + Aptos + NEAR).
|
|
4018
4089
|
*/
|
|
4019
4090
|
async discover(opts = {}) {
|
|
4020
4091
|
const found = await searchOpenIndexes({
|
|
@@ -4222,7 +4293,7 @@ var PipRailClient = class {
|
|
|
4222
4293
|
);
|
|
4223
4294
|
if (schemes.includes("exact") && exactOnNet && typeof net.payExact !== "function") {
|
|
4224
4295
|
throw new UnsupportedSchemeError(
|
|
4225
|
-
`This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (supported on EVM, Solana, Algorand + NEAR today), and no 'onchain-proof' rail was offered.`
|
|
4296
|
+
`This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (supported on EVM, Solana, Algorand, Aptos + NEAR today), and no 'onchain-proof' rail was offered.`
|
|
4226
4297
|
);
|
|
4227
4298
|
}
|
|
4228
4299
|
if (!schemes.includes("exact") && exactOnNet && typeof net.payExact === "function") {
|
|
@@ -4275,7 +4346,15 @@ var PipRailClient = class {
|
|
|
4275
4346
|
if (schemes.includes("exact")) {
|
|
4276
4347
|
out.push(
|
|
4277
4348
|
...challenge.accepts.filter(
|
|
4278
|
-
(a) => a.scheme === "exact" && this.supportsNetwork(net, a.network) && typeof net.payExact === "function" &&
|
|
4349
|
+
(a) => a.scheme === "exact" && this.supportsNetwork(net, a.network) && typeof net.payExact === "function" && // `native` is never `exact`-payable on ANY family (exact = an EIP-3009/Permit2-style
|
|
4350
|
+
// signed-authorization scheme a native coin can't support; every driver's payExact
|
|
4351
|
+
// throws/returns-null for it). describeAsset('native') IS non-null (onchain-proof needs
|
|
4352
|
+
// it), so without this guard a malformed 402 advertising a native `exact` rail would be
|
|
4353
|
+
// gathered, planned `payable`, even chosen by autoRoute — then throw at pay time.
|
|
4354
|
+
a.asset !== "native" && // the exact pay path (payExact → driver) reads `extra.assetTransferMethod`; a malformed
|
|
4355
|
+
// 402 offering an exact rail for a RECOGNISED token but omitting `extra` would otherwise
|
|
4356
|
+
// be planned payable then throw a raw TypeError mid-pay. Require it at gather time.
|
|
4357
|
+
typeof a.extra?.assetTransferMethod === "string" && net.describeAsset(a.asset) != null && // a foreign rail's maxTimeoutSeconds must be a usable positive integer, or
|
|
4279
4358
|
// signing it would build a NaN/garbage validBefore — drop it silently
|
|
4280
4359
|
// (symmetric with an unrecognised token) rather than leak a raw SyntaxError.
|
|
4281
4360
|
Number.isInteger(a.maxTimeoutSeconds) && a.maxTimeoutSeconds > 0
|
|
@@ -4285,7 +4364,8 @@ var PipRailClient = class {
|
|
|
4285
4364
|
if (schemes.includes("upto")) {
|
|
4286
4365
|
out.push(
|
|
4287
4366
|
...challenge.accepts.filter(
|
|
4288
|
-
(a) => a.scheme === "upto" && this.supportsNetwork(net, a.network) && typeof net.payUpto === "function" &&
|
|
4367
|
+
(a) => a.scheme === "upto" && this.supportsNetwork(net, a.network) && typeof net.payUpto === "function" && a.asset !== "native" && // native is not upto-payable either (same reason as exact)
|
|
4368
|
+
net.describeAsset(a.asset) != null && typeof a.extra?.facilitatorAddress === "string" && a.extra.facilitatorAddress.length > 0 && Number.isInteger(a.maxTimeoutSeconds) && a.maxTimeoutSeconds > 0
|
|
4289
4369
|
)
|
|
4290
4370
|
);
|
|
4291
4371
|
}
|
|
@@ -4760,7 +4840,7 @@ var PipRailClient = class {
|
|
|
4760
4840
|
async payExactRail(net, wallet, accept, url, init, quote) {
|
|
4761
4841
|
if (!net.payExact) {
|
|
4762
4842
|
throw new UnsupportedSchemeError(
|
|
4763
|
-
`the ${net.family} family can't pay a standard 'exact' rail (supported on EVM, Solana, Algorand + NEAR today).`
|
|
4843
|
+
`the ${net.family} family can't pay a standard 'exact' rail (supported on EVM, Solana, Algorand, Aptos + NEAR today).`
|
|
4764
4844
|
);
|
|
4765
4845
|
}
|
|
4766
4846
|
throwIfAborted(init?.signal);
|
|
@@ -4793,7 +4873,7 @@ var PipRailClient = class {
|
|
|
4793
4873
|
response = await fetch(url, { ...init ?? {}, headers, signal });
|
|
4794
4874
|
} catch (err) {
|
|
4795
4875
|
throw new PaymentTimeoutError(
|
|
4796
|
-
`exact: no response after submitting the authorization (nonce=${nonce}) to ${hostOf2(url)}. The facilitator may have already settled it \u2014 verify on-chain
|
|
4876
|
+
`exact: no response after submitting the authorization (nonce=${nonce}) to ${hostOf2(url)}. The facilitator may have already settled it \u2014 verify on-chain via ${exactSettleCheckHint(net.family, payerFrom, nonce)} before re-presenting the SAME signed authorization; do NOT re-pay.`,
|
|
4797
4877
|
{ cause: err, ref: nonce }
|
|
4798
4878
|
);
|
|
4799
4879
|
} finally {
|
|
@@ -4809,7 +4889,7 @@ var PipRailClient = class {
|
|
|
4809
4889
|
const receipt = parseReceipt(response);
|
|
4810
4890
|
this.captureReceipt(response, url);
|
|
4811
4891
|
this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
|
|
4812
|
-
const ref = settle?.transaction || receipt?.transaction ||
|
|
4892
|
+
const ref = settle?.transaction || receipt?.transaction || `${net.family === "evm" ? "eip3009" : net.family}-nonce:${nonce}`;
|
|
4813
4893
|
this.recordSpend(quote, ref);
|
|
4814
4894
|
return response;
|
|
4815
4895
|
}
|
|
@@ -4828,7 +4908,7 @@ var PipRailClient = class {
|
|
|
4828
4908
|
...lastReason ? { code: lastReason.error, detail: lastReason.detail } : {}
|
|
4829
4909
|
});
|
|
4830
4910
|
throw new MaxRetriesExceededError(
|
|
4831
|
-
`exact: server still returned 402 after submitting the signed authorization (nonce=${nonce}). Last rejection: ${why}. Re-present the SAME authorization \u2014 do NOT re-sign a fresh nonce; verify
|
|
4911
|
+
`exact: server still returned 402 after submitting the signed authorization (nonce=${nonce}). Last rejection: ${why}. Re-present the SAME authorization \u2014 do NOT re-sign a fresh nonce; verify on-chain via ${exactSettleCheckHint(net.family, payerFrom, nonce)} first. ref=${nonce}.`,
|
|
4832
4912
|
{ ref: nonce }
|
|
4833
4913
|
);
|
|
4834
4914
|
}
|
|
@@ -5499,8 +5579,8 @@ A 402 may offer up to three rails; you don't choose per payment \u2014 the clien
|
|
|
5499
5579
|
(the native coin \u2014 ETH/SOL/\u2026). Works on every chain.
|
|
5500
5580
|
- exact (the ratified x402 rail, opt-in): you only SIGN; the server \u2014 or a facilitator it chose
|
|
5501
5581
|
(e.g. PayAI) \u2014 broadcasts it, so you pay ZERO gas (you need only the token, no native coin). It
|
|
5502
|
-
works on EVM, Solana +
|
|
5503
|
-
fee-pooled group) is picked automatically.
|
|
5582
|
+
works on EVM, Solana, Algorand, Aptos + NEAR, and the on-chain method (EIP-3009 / Permit2 / SVM /
|
|
5583
|
+
Algorand fee-pooled group / Aptos fee-payer / NEAR SignedDelegateAction) is picked automatically.
|
|
5504
5584
|
- upto (the metered/variable x402 rail, opt-in, EVM): the amount you see is a MAXIMUM \u2014 you sign
|
|
5505
5585
|
a ceiling, the server meters real usage and settles the ACTUAL (<= the max). BUDGET AGAINST THE MAX:
|
|
5506
5586
|
the plan/policy treat the ceiling as the spend (a server may charge up to it), so a payable plan
|
|
@@ -6045,6 +6125,23 @@ function buildWellKnownX402(input) {
|
|
|
6045
6125
|
...input.ownershipProofs && input.ownershipProofs.length > 0 ? { ownershipProofs: input.ownershipProofs } : {}
|
|
6046
6126
|
};
|
|
6047
6127
|
}
|
|
6128
|
+
function buildWellKnownX402Manifest(input) {
|
|
6129
|
+
return {
|
|
6130
|
+
x402Version: 2,
|
|
6131
|
+
lastUpdated: input.lastUpdated ?? Math.floor(Date.now() / 1e3),
|
|
6132
|
+
items: input.resources.map((r) => ({
|
|
6133
|
+
resource: {
|
|
6134
|
+
url: r.url,
|
|
6135
|
+
...r.description ? { description: r.description } : {},
|
|
6136
|
+
...r.mimeType ? { mimeType: r.mimeType } : {}
|
|
6137
|
+
},
|
|
6138
|
+
type: "http",
|
|
6139
|
+
accepts: r.accepts,
|
|
6140
|
+
input: { method: (r.method ?? "GET").toUpperCase() },
|
|
6141
|
+
...r.mimeType ? { output: { mimeType: r.mimeType } } : {}
|
|
6142
|
+
}))
|
|
6143
|
+
};
|
|
6144
|
+
}
|
|
6048
6145
|
function buildX402DnsTxt(input) {
|
|
6049
6146
|
const descriptor = input.descriptor ? `descriptor=${input.descriptor};` : "";
|
|
6050
6147
|
return {
|
|
@@ -6775,7 +6872,7 @@ function createPaymentGate(options) {
|
|
|
6775
6872
|
if (hasCustomStore) {
|
|
6776
6873
|
return options.isUsed ? Boolean(await options.isUsed(ref)) : false;
|
|
6777
6874
|
}
|
|
6778
|
-
const key = ref.toLowerCase();
|
|
6875
|
+
const key = ref.startsWith("pid:") ? ref : ref.toLowerCase();
|
|
6779
6876
|
const now = Date.now();
|
|
6780
6877
|
pruneUsed(now);
|
|
6781
6878
|
if (localUsed.has(key)) return true;
|
|
@@ -6787,7 +6884,7 @@ function createPaymentGate(options) {
|
|
|
6787
6884
|
if (ok && options.markUsed) await options.markUsed(ref);
|
|
6788
6885
|
return;
|
|
6789
6886
|
}
|
|
6790
|
-
if (!ok) localUsed.delete(ref.toLowerCase());
|
|
6887
|
+
if (!ok) localUsed.delete(ref.startsWith("pid:") ? ref : ref.toLowerCase());
|
|
6791
6888
|
}
|
|
6792
6889
|
function buildAccept(s, nonce) {
|
|
6793
6890
|
return {
|
|
@@ -6860,6 +6957,7 @@ function createPaymentGate(options) {
|
|
|
6860
6957
|
const specs = await ready();
|
|
6861
6958
|
const nonce = genNonce();
|
|
6862
6959
|
const bazaar = options.discovery ? { bazaar: buildBazaarExtension(options.discovery === true ? {} : options.discovery) } : void 0;
|
|
6960
|
+
const paymentIdAd = options.paymentIdentifier ? buildPaymentIdentifierAdvertisement() : void 0;
|
|
6863
6961
|
const accepts = buildAccepts(specs, nonce);
|
|
6864
6962
|
const endpointInfo = buildEndpointInfo({
|
|
6865
6963
|
...options.description ? { description: options.description } : {},
|
|
@@ -6877,11 +6975,13 @@ function createPaymentGate(options) {
|
|
|
6877
6975
|
const bodyPiprail = { ...selfDescribe ?? {}, ...rejectionPiprail };
|
|
6878
6976
|
const bodyExtensions = {
|
|
6879
6977
|
...bazaar,
|
|
6978
|
+
...paymentIdAd,
|
|
6880
6979
|
...rejectionExt,
|
|
6881
6980
|
...Object.keys(bodyPiprail).length > 0 ? { piprail: bodyPiprail } : {}
|
|
6882
6981
|
};
|
|
6883
6982
|
const headerExtensions = {
|
|
6884
6983
|
...bazaar,
|
|
6984
|
+
...paymentIdAd,
|
|
6885
6985
|
...Object.keys(rejectionPiprail).length > 0 ? { piprail: rejectionPiprail } : {}
|
|
6886
6986
|
};
|
|
6887
6987
|
const challenge2 = {
|
|
@@ -7091,7 +7191,17 @@ function createPaymentGate(options) {
|
|
|
7091
7191
|
await settleTx(ref, false);
|
|
7092
7192
|
return rejection(result.error, result.detail);
|
|
7093
7193
|
}
|
|
7094
|
-
|
|
7194
|
+
const verified = result.receipt.transaction;
|
|
7195
|
+
if (verified && verified !== ref) {
|
|
7196
|
+
if (await claimTx(verified)) {
|
|
7197
|
+
await settleTx(ref, false);
|
|
7198
|
+
return rejection("tx_already_used", `Payment ${verified} was already redeemed.`);
|
|
7199
|
+
}
|
|
7200
|
+
await settleTx(ref, false);
|
|
7201
|
+
await settleTx(verified, true);
|
|
7202
|
+
} else {
|
|
7203
|
+
await settleTx(ref, true);
|
|
7204
|
+
}
|
|
7095
7205
|
await deliverOnPaid(spec, result.receipt);
|
|
7096
7206
|
return await buildPaidResult(spec, result.receipt, sig.payload.nonce);
|
|
7097
7207
|
}
|
|
@@ -7297,7 +7407,7 @@ function createPaymentGate(options) {
|
|
|
7297
7407
|
if (result.kind === "invalid") await deliverOnFailed(result);
|
|
7298
7408
|
return result;
|
|
7299
7409
|
}
|
|
7300
|
-
async function
|
|
7410
|
+
async function routeVerdictObject(obj) {
|
|
7301
7411
|
if (obj === void 0 || obj === null) return asChallenge();
|
|
7302
7412
|
const sig = parseSignatureObject(obj);
|
|
7303
7413
|
if (sig && sig.accepted && typeof sig.accepted.network === "string" && typeof sig.accepted.asset === "string") {
|
|
@@ -7309,6 +7419,44 @@ function createPaymentGate(options) {
|
|
|
7309
7419
|
if (exact) return verifyExact(exact);
|
|
7310
7420
|
return asChallenge();
|
|
7311
7421
|
}
|
|
7422
|
+
function echoPaymentIdentifier(result, id) {
|
|
7423
|
+
try {
|
|
7424
|
+
const decoded = decodeBase64Json(result.receiptHeader);
|
|
7425
|
+
if (!decoded) return result;
|
|
7426
|
+
const { extensions: existing, ...receiptOnly } = decoded;
|
|
7427
|
+
const merged = {
|
|
7428
|
+
...existing ?? {},
|
|
7429
|
+
[EXT_PAYMENT_IDENTIFIER]: { info: { required: false, id } }
|
|
7430
|
+
};
|
|
7431
|
+
return { ...result, receiptHeader: buildReceiptHeader(receiptOnly, merged) };
|
|
7432
|
+
} catch {
|
|
7433
|
+
return result;
|
|
7434
|
+
}
|
|
7435
|
+
}
|
|
7436
|
+
async function resolveVerdictObject(obj) {
|
|
7437
|
+
if (!options.paymentIdentifier) return routeVerdictObject(obj);
|
|
7438
|
+
const id = readPaymentIdentifier(obj);
|
|
7439
|
+
if (id !== null && typeof id === "object") {
|
|
7440
|
+
return rejection("signature_invalid", `payment-identifier: ${id.invalid}.`);
|
|
7441
|
+
}
|
|
7442
|
+
if (id === null) return routeVerdictObject(obj);
|
|
7443
|
+
const idKey = "pid:" + id;
|
|
7444
|
+
if (await claimTx(idKey)) {
|
|
7445
|
+
return rejection(
|
|
7446
|
+
"tx_already_used",
|
|
7447
|
+
`Idempotency id "${id}" is already bound to a settled payment; use a fresh id for a new payment.`
|
|
7448
|
+
);
|
|
7449
|
+
}
|
|
7450
|
+
let result;
|
|
7451
|
+
try {
|
|
7452
|
+
result = await routeVerdictObject(obj);
|
|
7453
|
+
} catch (err) {
|
|
7454
|
+
await settleTx(idKey, false);
|
|
7455
|
+
throw err;
|
|
7456
|
+
}
|
|
7457
|
+
await settleTx(idKey, result.kind === "paid");
|
|
7458
|
+
return result.kind === "paid" ? echoPaymentIdentifier(result, id) : result;
|
|
7459
|
+
}
|
|
7312
7460
|
return { challenge, verify, verifyObject, describe, landingPage };
|
|
7313
7461
|
}
|
|
7314
7462
|
function requirePayment(options) {
|
|
@@ -7708,6 +7856,115 @@ function resourceUrlFromMessage(message) {
|
|
|
7708
7856
|
}
|
|
7709
7857
|
return "";
|
|
7710
7858
|
}
|
|
7859
|
+
|
|
7860
|
+
// src/transports/mcp-types.ts
|
|
7861
|
+
var MCP_PAYMENT_META_KEY = "x402/payment";
|
|
7862
|
+
var MCP_PAYMENT_RESPONSE_META_KEY = "x402/payment-response";
|
|
7863
|
+
|
|
7864
|
+
// src/transports/mcp.ts
|
|
7865
|
+
function toMcpPaymentRequired(challenge) {
|
|
7866
|
+
const text = JSON.stringify(challenge);
|
|
7867
|
+
return {
|
|
7868
|
+
isError: true,
|
|
7869
|
+
structuredContent: challenge,
|
|
7870
|
+
content: [{ type: "text", text }]
|
|
7871
|
+
};
|
|
7872
|
+
}
|
|
7873
|
+
function toMcpPaymentResponse(content, receipt) {
|
|
7874
|
+
return {
|
|
7875
|
+
content,
|
|
7876
|
+
_meta: {
|
|
7877
|
+
[MCP_PAYMENT_RESPONSE_META_KEY]: {
|
|
7878
|
+
success: true,
|
|
7879
|
+
transaction: receipt.transaction,
|
|
7880
|
+
network: receipt.network,
|
|
7881
|
+
payer: receipt.payer
|
|
7882
|
+
}
|
|
7883
|
+
}
|
|
7884
|
+
};
|
|
7885
|
+
}
|
|
7886
|
+
function fromMcpPayment(params) {
|
|
7887
|
+
const meta = params?._meta?.[MCP_PAYMENT_META_KEY];
|
|
7888
|
+
return meta == null ? null : meta;
|
|
7889
|
+
}
|
|
7890
|
+
function fromMcpPaymentRequired(result) {
|
|
7891
|
+
if (!result || result.isError !== true) return null;
|
|
7892
|
+
const sc = result.structuredContent;
|
|
7893
|
+
if (sc && typeof sc === "object" && typeof sc.x402Version === "number") {
|
|
7894
|
+
return sc;
|
|
7895
|
+
}
|
|
7896
|
+
const text = result.content?.[0]?.text;
|
|
7897
|
+
if (typeof text === "string") {
|
|
7898
|
+
try {
|
|
7899
|
+
const parsed = JSON.parse(text);
|
|
7900
|
+
if (parsed && typeof parsed === "object" && typeof parsed.x402Version === "number") {
|
|
7901
|
+
return parsed;
|
|
7902
|
+
}
|
|
7903
|
+
} catch {
|
|
7904
|
+
}
|
|
7905
|
+
}
|
|
7906
|
+
return null;
|
|
7907
|
+
}
|
|
7908
|
+
function isMcpPaymentRequired(result) {
|
|
7909
|
+
return fromMcpPaymentRequired(result) != null;
|
|
7910
|
+
}
|
|
7911
|
+
function fromMcpPaymentResponse(result) {
|
|
7912
|
+
const meta = result?._meta?.[MCP_PAYMENT_RESPONSE_META_KEY];
|
|
7913
|
+
if (!meta || typeof meta !== "object") return null;
|
|
7914
|
+
return meta;
|
|
7915
|
+
}
|
|
7916
|
+
function buildMcpPaymentMeta(input) {
|
|
7917
|
+
return {
|
|
7918
|
+
[MCP_PAYMENT_META_KEY]: {
|
|
7919
|
+
x402Version: input.x402Version ?? 2,
|
|
7920
|
+
...input.resource ? { resource: input.resource } : {},
|
|
7921
|
+
accepted: input.accepted,
|
|
7922
|
+
payload: input.payload
|
|
7923
|
+
}
|
|
7924
|
+
};
|
|
7925
|
+
}
|
|
7926
|
+
function createMcpPaymentTool(options) {
|
|
7927
|
+
const gate = options.gate ?? createPaymentGate(options);
|
|
7928
|
+
function settlementFailed(code, detail) {
|
|
7929
|
+
return {
|
|
7930
|
+
isError: true,
|
|
7931
|
+
content: [
|
|
7932
|
+
{
|
|
7933
|
+
type: "text",
|
|
7934
|
+
text: `x402 settlement failed (${toA2AErrorCode(code)}): ${detail}. The buyer can retry, or pay the onchain-proof rail directly.`
|
|
7935
|
+
}
|
|
7936
|
+
]
|
|
7937
|
+
};
|
|
7938
|
+
}
|
|
7939
|
+
async function handleToolCall(params) {
|
|
7940
|
+
const raw = fromMcpPayment(params);
|
|
7941
|
+
if (raw == null) {
|
|
7942
|
+
const { challenge } = await gate.challenge(options.resourceUrl ?? "");
|
|
7943
|
+
return toMcpPaymentRequired(challenge);
|
|
7944
|
+
}
|
|
7945
|
+
let result;
|
|
7946
|
+
try {
|
|
7947
|
+
result = await gate.verifyObject(raw);
|
|
7948
|
+
} catch (err) {
|
|
7949
|
+
if (err instanceof SettlementError) return settlementFailed("settlement_failed", err.message);
|
|
7950
|
+
throw err;
|
|
7951
|
+
}
|
|
7952
|
+
if (result.kind === "paid") {
|
|
7953
|
+
try {
|
|
7954
|
+
const content = await options.fulfill({ receipt: result.receipt, params });
|
|
7955
|
+
return toMcpPaymentResponse(content, result.receipt);
|
|
7956
|
+
} catch (err) {
|
|
7957
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
7958
|
+
return toMcpPaymentResponse(
|
|
7959
|
+
[{ type: "text", text: `Payment settled (tx ${result.receipt.transaction}), but serving the result failed: ${detail}` }],
|
|
7960
|
+
result.receipt
|
|
7961
|
+
);
|
|
7962
|
+
}
|
|
7963
|
+
}
|
|
7964
|
+
return toMcpPaymentRequired(result.challenge);
|
|
7965
|
+
}
|
|
7966
|
+
return { handleToolCall, gate };
|
|
7967
|
+
}
|
|
7711
7968
|
export {
|
|
7712
7969
|
A2A_ERROR_KEY,
|
|
7713
7970
|
A2A_EXTENSIONS_HEADER,
|
|
@@ -7726,6 +7983,7 @@ export {
|
|
|
7726
7983
|
EIP3009_TYPES,
|
|
7727
7984
|
EXACT_NETWORK_SLUGS,
|
|
7728
7985
|
EXT_OFFER_RECEIPT,
|
|
7986
|
+
EXT_PAYMENT_IDENTIFIER,
|
|
7729
7987
|
GENERATOR,
|
|
7730
7988
|
HEADER_REQUIRED,
|
|
7731
7989
|
HEADER_RESPONSE,
|
|
@@ -7735,6 +7993,8 @@ export {
|
|
|
7735
7993
|
InsufficientFundsError,
|
|
7736
7994
|
InvalidEnvelopeError,
|
|
7737
7995
|
KNOWN_FACILITATORS,
|
|
7996
|
+
MCP_PAYMENT_META_KEY,
|
|
7997
|
+
MCP_PAYMENT_RESPONSE_META_KEY,
|
|
7738
7998
|
MaxRetriesExceededError,
|
|
7739
7999
|
MissingDriverError,
|
|
7740
8000
|
MultiChainPayer,
|
|
@@ -7772,18 +8032,22 @@ export {
|
|
|
7772
8032
|
buildEndpointInfo,
|
|
7773
8033
|
buildExactAuthorization,
|
|
7774
8034
|
buildExactSignatureHeader,
|
|
8035
|
+
buildMcpPaymentMeta,
|
|
7775
8036
|
buildOpenApi,
|
|
8037
|
+
buildPaymentIdentifierAdvertisement,
|
|
7776
8038
|
buildReceiptExtension,
|
|
7777
8039
|
buildReceiptHeader,
|
|
7778
8040
|
buildSelfDescription,
|
|
7779
8041
|
buildSignatureHeader,
|
|
7780
8042
|
buildUptoSignatureHeader,
|
|
7781
8043
|
buildWellKnownX402,
|
|
8044
|
+
buildWellKnownX402Manifest,
|
|
7782
8045
|
buildX402DnsTxt,
|
|
7783
8046
|
chainIdForExactNetwork,
|
|
7784
8047
|
claim402IndexDomain,
|
|
7785
8048
|
classifyChallenge,
|
|
7786
8049
|
createA2APaymentHandler,
|
|
8050
|
+
createMcpPaymentTool,
|
|
7787
8051
|
createPaymentGate,
|
|
7788
8052
|
decodeBase64Json,
|
|
7789
8053
|
decorateOutcome,
|
|
@@ -7801,7 +8065,11 @@ export {
|
|
|
7801
8065
|
formatSpendReport,
|
|
7802
8066
|
fromA2APaymentPayload,
|
|
7803
8067
|
fromA2APaymentRequired,
|
|
8068
|
+
fromMcpPayment,
|
|
8069
|
+
fromMcpPaymentRequired,
|
|
8070
|
+
fromMcpPaymentResponse,
|
|
7804
8071
|
getDirectoryInfo,
|
|
8072
|
+
isMcpPaymentRequired,
|
|
7805
8073
|
isPermit2ProxyChain,
|
|
7806
8074
|
isUptoProxyChain,
|
|
7807
8075
|
knownFacilitatorsFor,
|
|
@@ -7824,6 +8092,7 @@ export {
|
|
|
7824
8092
|
planAcross,
|
|
7825
8093
|
rankResources,
|
|
7826
8094
|
readExactDomain,
|
|
8095
|
+
readPaymentIdentifier,
|
|
7827
8096
|
register402Index,
|
|
7828
8097
|
registerDriver,
|
|
7829
8098
|
registerX402Scan,
|
|
@@ -7840,5 +8109,7 @@ export {
|
|
|
7840
8109
|
toA2APaymentRequired,
|
|
7841
8110
|
toInsufficientFundsError,
|
|
7842
8111
|
toInvalidBody,
|
|
8112
|
+
toMcpPaymentRequired,
|
|
8113
|
+
toMcpPaymentResponse,
|
|
7843
8114
|
verify402IndexDomain
|
|
7844
8115
|
};
|