@piprail/sdk 1.9.0 → 1.10.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.
Files changed (29) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/ERRORS.md +21 -8
  3. package/README.md +20 -0
  4. package/STANDARDS.md +5 -2
  5. package/dist/{algorand-IJJKE35X.cjs → algorand-MXUSKX46.cjs} +17 -17
  6. package/dist/{algorand-B67G4335.js → algorand-WGVF4KTU.js} +1 -1
  7. package/dist/{aptos-YQWTGFRZ.js → aptos-LPBLSEIQ.js} +1 -1
  8. package/dist/{aptos-X3G2UBYW.cjs → aptos-YT7SXWPF.cjs} +16 -16
  9. package/dist/{chunk-IQGT65WS.cjs → chunk-MDLZJGLY.cjs} +20 -16
  10. package/dist/{chunk-QDS6FBZP.js → chunk-SVMGHASK.js} +4 -0
  11. package/dist/index.cjs +811 -253
  12. package/dist/index.d.cts +400 -31
  13. package/dist/index.d.ts +400 -31
  14. package/dist/index.js +734 -176
  15. package/dist/{near-GGUHLXAF.cjs → near-7ZDNISUX.cjs} +19 -19
  16. package/dist/{near-7MBBCDUE.js → near-K6BDBABG.js} +1 -1
  17. package/dist/{solana-W24TCJV4.cjs → solana-PU7N2M64.cjs} +14 -14
  18. package/dist/{solana-7WJVZGDW.js → solana-S3UFI3FE.js} +1 -1
  19. package/dist/{stellar-HV6VGZX3.js → stellar-Q5PO23SC.js} +1 -1
  20. package/dist/{stellar-YMY3K2YB.cjs → stellar-VDQOFQEO.cjs} +21 -21
  21. package/dist/{sui-32KVESR5.cjs → sui-FKSMLKRF.cjs} +17 -17
  22. package/dist/{sui-2WFWVFJX.js → sui-WOXRKJXS.js} +1 -1
  23. package/dist/{ton-FIQGV2LC.cjs → ton-VK6KRJHP.cjs} +14 -14
  24. package/dist/{ton-DGZB7W4U.js → ton-WPTXGLVK.js} +1 -1
  25. package/dist/{tron-RLIL2FDI.js → tron-6GXBXTR4.js} +1 -1
  26. package/dist/{tron-ZSXAPZ2C.cjs → tron-WLOF5OUV.cjs} +24 -24
  27. package/dist/{xrpl-2PKP7HOI.cjs → xrpl-CMNI25BV.cjs} +21 -21
  28. package/dist/{xrpl-UEC2GYVV.js → xrpl-HEAPEXAM.js} +1 -1
  29. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  PaymentTimeoutError,
11
11
  PipRailError,
12
12
  RecipientNotReadyError,
13
+ SettlementError,
13
14
  UnknownTokenError,
14
15
  UnsupportedNetworkError,
15
16
  WrongChainError,
@@ -20,7 +21,7 @@ import {
20
21
  parseUnits,
21
22
  rejectForeignToken,
22
23
  toInsufficientFundsError
23
- } from "./chunk-QDS6FBZP.js";
24
+ } from "./chunk-SVMGHASK.js";
24
25
 
25
26
  // src/drivers/registry.ts
26
27
  var byFamily = /* @__PURE__ */ new Map();
@@ -63,7 +64,7 @@ function resolveNetwork(opts) {
63
64
  }
64
65
 
65
66
  // src/drivers/evm/index.ts
66
- import { BaseError, createPublicClient, erc20Abi as erc20Abi3, getAddress as getAddress2, http as http2, isAddress } from "viem";
67
+ import { BaseError, createPublicClient, erc20Abi as erc20Abi3, getAddress as getAddress3, http as http2, isAddress } from "viem";
67
68
 
68
69
  // src/drivers/evm/chains.ts
69
70
  import { defineChain } from "viem";
@@ -483,20 +484,367 @@ function sumTransfersTo(logs, asset, payTo) {
483
484
  return { total, from };
484
485
  }
485
486
 
487
+ // src/drivers/evm/exact.ts
488
+ import {
489
+ getAddress as getAddress2,
490
+ parseSignature,
491
+ recoverTypedDataAddress
492
+ } from "viem";
493
+ var EXACT_NETWORK_SLUGS = {
494
+ ethereum: 1,
495
+ base: 8453,
496
+ "base-sepolia": 84532,
497
+ arbitrum: 42161,
498
+ optimism: 10,
499
+ polygon: 137,
500
+ avalanche: 43114
501
+ };
502
+ function chainIdForExactNetwork(slug) {
503
+ return EXACT_NETWORK_SLUGS[slug] ?? null;
504
+ }
505
+ var EIP3009_TYPES = {
506
+ TransferWithAuthorization: [
507
+ { name: "from", type: "address" },
508
+ { name: "to", type: "address" },
509
+ { name: "value", type: "uint256" },
510
+ { name: "validAfter", type: "uint256" },
511
+ { name: "validBefore", type: "uint256" },
512
+ { name: "nonce", type: "bytes32" }
513
+ ]
514
+ };
515
+ function parseExactRequirements(body) {
516
+ if (!body || typeof body !== "object") return null;
517
+ const accepts = body.accepts;
518
+ if (!Array.isArray(accepts)) return null;
519
+ const out = [];
520
+ for (const raw of accepts) {
521
+ if (!raw || typeof raw !== "object") continue;
522
+ const a = raw;
523
+ if (a.scheme !== "exact") continue;
524
+ const amount = a.maxAmountRequired ?? a.amount;
525
+ if (typeof a.network !== "string" || typeof amount !== "string" || typeof a.asset !== "string" || typeof a.payTo !== "string") {
526
+ continue;
527
+ }
528
+ out.push({
529
+ scheme: "exact",
530
+ network: a.network,
531
+ maxAmountRequired: amount,
532
+ asset: a.asset,
533
+ payTo: a.payTo,
534
+ maxTimeoutSeconds: typeof a.maxTimeoutSeconds === "number" ? a.maxTimeoutSeconds : 600,
535
+ ...a.extra && typeof a.extra === "object" ? { extra: a.extra } : {},
536
+ ...typeof a.description === "string" ? { description: a.description } : {},
537
+ ...typeof a.resource === "string" ? { resource: a.resource } : {}
538
+ });
539
+ }
540
+ return out;
541
+ }
542
+ async function buildExactAuthorization(params) {
543
+ const { account, accept, chainId, now, nonce } = params;
544
+ if (!account.signTypedData) {
545
+ throw new Error("buildExactAuthorization: the account cannot sign EIP-712 typed data.");
546
+ }
547
+ const authorization = {
548
+ from: account.address,
549
+ to: accept.payTo,
550
+ value: accept.maxAmountRequired,
551
+ validAfter: "0",
552
+ validBefore: String(now + accept.maxTimeoutSeconds),
553
+ nonce
554
+ };
555
+ const signature = await account.signTypedData({
556
+ domain: {
557
+ name: accept.extra?.name ?? "USD Coin",
558
+ version: accept.extra?.version ?? "2",
559
+ chainId,
560
+ verifyingContract: accept.asset
561
+ },
562
+ types: EIP3009_TYPES,
563
+ primaryType: "TransferWithAuthorization",
564
+ message: {
565
+ from: authorization.from,
566
+ to: authorization.to,
567
+ value: BigInt(authorization.value),
568
+ validAfter: BigInt(authorization.validAfter),
569
+ validBefore: BigInt(authorization.validBefore),
570
+ nonce: authorization.nonce
571
+ }
572
+ });
573
+ return { authorization, signature };
574
+ }
575
+ function base64(str) {
576
+ if (typeof Buffer !== "undefined") return Buffer.from(str, "utf8").toString("base64");
577
+ if (typeof btoa === "function" && typeof TextEncoder !== "undefined") {
578
+ const bytes = new TextEncoder().encode(str);
579
+ let binary = "";
580
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
581
+ return btoa(binary);
582
+ }
583
+ throw new Error("No base64 encoder available in this runtime.");
584
+ }
585
+ function encodeXPaymentHeader(input) {
586
+ const payload = {
587
+ x402Version: input.x402Version ?? 1,
588
+ scheme: "exact",
589
+ network: input.network,
590
+ payload: { signature: input.signature, authorization: input.authorization }
591
+ };
592
+ return base64(JSON.stringify(payload));
593
+ }
594
+ var eip3009Abi = [
595
+ {
596
+ type: "function",
597
+ name: "transferWithAuthorization",
598
+ stateMutability: "nonpayable",
599
+ outputs: [],
600
+ inputs: [
601
+ { name: "from", type: "address" },
602
+ { name: "to", type: "address" },
603
+ { name: "value", type: "uint256" },
604
+ { name: "validAfter", type: "uint256" },
605
+ { name: "validBefore", type: "uint256" },
606
+ { name: "nonce", type: "bytes32" },
607
+ { name: "v", type: "uint8" },
608
+ { name: "r", type: "bytes32" },
609
+ { name: "s", type: "bytes32" }
610
+ ]
611
+ },
612
+ {
613
+ type: "function",
614
+ name: "transferWithAuthorization",
615
+ stateMutability: "nonpayable",
616
+ outputs: [],
617
+ inputs: [
618
+ { name: "from", type: "address" },
619
+ { name: "to", type: "address" },
620
+ { name: "value", type: "uint256" },
621
+ { name: "validAfter", type: "uint256" },
622
+ { name: "validBefore", type: "uint256" },
623
+ { name: "nonce", type: "bytes32" },
624
+ { name: "signature", type: "bytes" }
625
+ ]
626
+ },
627
+ {
628
+ type: "function",
629
+ name: "authorizationState",
630
+ stateMutability: "view",
631
+ inputs: [
632
+ { name: "authorizer", type: "address" },
633
+ { name: "nonce", type: "bytes32" }
634
+ ],
635
+ outputs: [{ type: "bool" }]
636
+ },
637
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
638
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] }
639
+ ];
640
+ var ZERO_ADDR = "0x0000000000000000000000000000000000000000";
641
+ var ZERO_NONCE = `0x${"00".repeat(32)}`;
642
+ async function readExactDomain(publicClient, asset) {
643
+ if (asset === "native") return null;
644
+ let token;
645
+ try {
646
+ token = getAddress2(asset);
647
+ } catch {
648
+ return null;
649
+ }
650
+ try {
651
+ const [name, version] = await Promise.all([
652
+ publicClient.readContract({ address: token, abi: eip3009Abi, functionName: "name" }),
653
+ publicClient.readContract({ address: token, abi: eip3009Abi, functionName: "version" }),
654
+ // The EIP-3009 probe: this view exists only on EIP-3009 tokens; it reverts on
655
+ // a plain ERC-20 / USDT, marking the token as not exact-payable.
656
+ publicClient.readContract({
657
+ address: token,
658
+ abi: eip3009Abi,
659
+ functionName: "authorizationState",
660
+ args: [ZERO_ADDR, ZERO_NONCE]
661
+ })
662
+ ]);
663
+ if (typeof name !== "string" || typeof version !== "string" || !name || !version) return null;
664
+ return { name, version };
665
+ } catch {
666
+ return null;
667
+ }
668
+ }
669
+ function shorten(msg) {
670
+ const oneLine = msg.replace(/\s+/g, " ").trim();
671
+ return oneLine.length > 200 ? `${oneLine.slice(0, 200)}\u2026` : oneLine;
672
+ }
673
+ async function verifyAndSettleExactEvm(input) {
674
+ const { publicClient, walletClient, account, chain, payload, accept } = input;
675
+ const token = getAddress2(accept.asset);
676
+ const payTo = getAddress2(accept.payTo);
677
+ const requiredAmount = BigInt(accept.amount);
678
+ let from;
679
+ let to;
680
+ let value;
681
+ let validAfter;
682
+ let validBefore;
683
+ let nonce;
684
+ try {
685
+ from = getAddress2(payload.authorization.from);
686
+ to = getAddress2(payload.authorization.to);
687
+ value = BigInt(payload.authorization.value);
688
+ validAfter = BigInt(payload.authorization.validAfter);
689
+ validBefore = BigInt(payload.authorization.validBefore);
690
+ nonce = payload.authorization.nonce;
691
+ if (!/^0x[0-9a-fA-F]{64}$/.test(nonce)) throw new Error("nonce must be 32-byte hex");
692
+ if (!/^0x[0-9a-fA-F]+$/.test(payload.signature)) throw new Error("signature must be hex");
693
+ } catch (err) {
694
+ return {
695
+ ok: false,
696
+ error: "signature_invalid",
697
+ detail: `Malformed exact authorization: ${err instanceof Error ? err.message : String(err)}.`
698
+ };
699
+ }
700
+ if (to !== payTo) {
701
+ return { ok: false, error: "wrong_recipient", detail: `Authorization pays ${to}, not ${payTo}.` };
702
+ }
703
+ if (value < requiredAmount) {
704
+ return { ok: false, error: "amount_too_low", detail: `Authorized ${value}, required ${requiredAmount}.` };
705
+ }
706
+ const now = BigInt(Math.floor(Date.now() / 1e3));
707
+ if (validBefore <= now) {
708
+ return { ok: false, error: "payment_expired", detail: `Authorization expired: validBefore ${validBefore} <= now ${now}.` };
709
+ }
710
+ let fromCode;
711
+ try {
712
+ fromCode = await publicClient.getCode({ address: from });
713
+ } catch {
714
+ return { ok: false, error: "tx_not_found", detail: `Could not read code at ${from} (transient RPC) \u2014 retry.` };
715
+ }
716
+ const isContractWallet = Boolean(fromCode && fromCode !== "0x");
717
+ if (!isContractWallet) {
718
+ let recovered;
719
+ try {
720
+ recovered = await recoverTypedDataAddress({
721
+ domain: {
722
+ name: accept.extra.name,
723
+ version: accept.extra.version,
724
+ chainId: chain.id,
725
+ verifyingContract: token
726
+ },
727
+ types: EIP3009_TYPES,
728
+ primaryType: "TransferWithAuthorization",
729
+ message: { from, to, value, validAfter, validBefore, nonce },
730
+ signature: payload.signature
731
+ });
732
+ } catch (err) {
733
+ return { ok: false, error: "signature_invalid", detail: `Not a valid EIP-712 signature: ${shorten(err instanceof Error ? err.message : String(err))}.` };
734
+ }
735
+ if (recovered !== from) {
736
+ return { ok: false, error: "signature_invalid", detail: `Signature recovered to ${recovered}, not the authorizer ${from}.` };
737
+ }
738
+ }
739
+ try {
740
+ const used = await publicClient.readContract({
741
+ address: token,
742
+ abi: eip3009Abi,
743
+ functionName: "authorizationState",
744
+ args: [from, nonce]
745
+ });
746
+ if (used) {
747
+ return { ok: false, error: "tx_already_used", detail: `Authorization nonce ${nonce} already used or canceled on-chain.` };
748
+ }
749
+ } catch {
750
+ return { ok: false, error: "tx_not_found", detail: "Could not read authorizationState (transient RPC) \u2014 retry." };
751
+ }
752
+ const baseArgs = [from, to, value, validAfter, validBefore, nonce];
753
+ const isEcdsa = !isContractWallet && payload.signature.length - 2 === 130;
754
+ let writeArgs;
755
+ if (isEcdsa) {
756
+ const { r, s, yParity } = parseSignature(payload.signature);
757
+ writeArgs = [...baseArgs, BigInt(yParity + 27), r, s];
758
+ } else {
759
+ writeArgs = [...baseArgs, payload.signature];
760
+ }
761
+ try {
762
+ await publicClient.simulateContract({
763
+ account,
764
+ address: token,
765
+ abi: eip3009Abi,
766
+ functionName: "transferWithAuthorization",
767
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
768
+ args: writeArgs
769
+ });
770
+ } catch (err) {
771
+ const msg = err instanceof Error ? err.message : String(err);
772
+ if (/used or canceled/i.test(msg)) return { ok: false, error: "tx_already_used", detail: "Authorization is used or canceled." };
773
+ if (/expired|not yet valid/i.test(msg)) return { ok: false, error: "payment_expired", detail: shorten(msg) };
774
+ if (/invalid signature/i.test(msg)) return { ok: false, error: "signature_invalid", detail: shorten(msg) };
775
+ return { ok: false, error: "tx_reverted", detail: `transferWithAuthorization would revert: ${shorten(msg)}` };
776
+ }
777
+ let txHash;
778
+ try {
779
+ txHash = await walletClient.writeContract({
780
+ account,
781
+ chain,
782
+ address: token,
783
+ abi: eip3009Abi,
784
+ functionName: "transferWithAuthorization",
785
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
786
+ args: writeArgs
787
+ });
788
+ } catch (err) {
789
+ throw new SettlementError(
790
+ `exact settle: the merchant relayer failed to broadcast transferWithAuthorization (${shorten(err instanceof Error ? err.message : String(err))}). The payer's authorization is still valid and unused \u2014 fund/fix the relayer and the payer can retry.`,
791
+ { cause: err }
792
+ );
793
+ }
794
+ try {
795
+ const confirmations = accept.extra.minConfirmations ?? 1;
796
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash, confirmations });
797
+ if (receipt.status !== "success") {
798
+ return { ok: false, error: "tx_reverted", detail: `Settlement tx ${txHash} reverted on-chain.` };
799
+ }
800
+ } catch (err) {
801
+ throw new SettlementError(
802
+ `exact settle: broadcast ${txHash} but couldn't confirm it (${shorten(err instanceof Error ? err.message : String(err))}).`,
803
+ { cause: err }
804
+ );
805
+ }
806
+ return {
807
+ ok: true,
808
+ receipt: {
809
+ scheme: "exact",
810
+ success: true,
811
+ network: accept.network,
812
+ transaction: txHash,
813
+ asset: accept.asset,
814
+ amount: accept.amount,
815
+ payer: from,
816
+ payTo: accept.payTo,
817
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
818
+ }
819
+ };
820
+ }
821
+
486
822
  // src/x402.ts
487
823
  var HEADER_REQUIRED = "payment-required";
488
824
  var HEADER_SIGNATURE = "payment-signature";
489
825
  var HEADER_RESPONSE = "payment-response";
490
- function decodeBase64(b64) {
491
- if (typeof atob === "function") return atob(b64);
492
- if (typeof Buffer !== "undefined") return Buffer.from(b64, "base64").toString("utf8");
493
- throw new Error("No base64 decoder available in this runtime.");
494
- }
826
+ var HEADER_SIGNATURE_V1 = "x-payment";
827
+ var HEADER_RESPONSE_V1 = "x-payment-response";
495
828
  function encodeBase64(str) {
496
- if (typeof btoa === "function") return btoa(str);
497
829
  if (typeof Buffer !== "undefined") return Buffer.from(str, "utf8").toString("base64");
830
+ if (typeof btoa === "function" && typeof TextEncoder !== "undefined") {
831
+ const bytes = new TextEncoder().encode(str);
832
+ let binary = "";
833
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
834
+ return btoa(binary);
835
+ }
498
836
  throw new Error("No base64 encoder available in this runtime.");
499
837
  }
838
+ function decodeBase64(b64) {
839
+ if (typeof Buffer !== "undefined") return Buffer.from(b64, "base64").toString("utf8");
840
+ if (typeof atob === "function" && typeof TextDecoder !== "undefined") {
841
+ const binary = atob(b64);
842
+ const bytes = new Uint8Array(binary.length);
843
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
844
+ return new TextDecoder().decode(bytes);
845
+ }
846
+ throw new Error("No base64 decoder available in this runtime.");
847
+ }
500
848
  function fromBase64Json(b64) {
501
849
  try {
502
850
  return JSON.parse(decodeBase64(b64));
@@ -557,6 +905,36 @@ function parseSignatureHeader(value) {
557
905
  }
558
906
  return parsed;
559
907
  }
908
+ function parseExactPaymentHeader(value) {
909
+ const parsed = fromBase64Json(value);
910
+ if (!parsed || typeof parsed !== "object") return null;
911
+ const v = parsed;
912
+ const accepted = v.accepted ?? null;
913
+ const scheme = accepted?.scheme ?? v.scheme;
914
+ if (scheme !== "exact") return null;
915
+ const network = accepted?.network ?? v.network;
916
+ if (typeof network !== "string") return null;
917
+ const payload = v.payload;
918
+ if (!payload || typeof payload !== "object") return null;
919
+ const signature = payload.signature;
920
+ const authorization = payload.authorization;
921
+ if (typeof signature !== "string" || !authorization || typeof authorization !== "object") return null;
922
+ for (const k of ["from", "to", "value", "validAfter", "validBefore", "nonce"]) {
923
+ if (typeof authorization[k] !== "string") return null;
924
+ }
925
+ const x402Version = typeof v.x402Version === "number" ? v.x402Version : 2;
926
+ const asset = accepted && typeof accepted.asset === "string" ? accepted.asset : void 0;
927
+ return {
928
+ x402Version,
929
+ network,
930
+ ...asset ? { asset } : {},
931
+ payload: {
932
+ signature,
933
+ authorization
934
+ },
935
+ raw: v
936
+ };
937
+ }
560
938
  function isValidChallenge(value) {
561
939
  if (!value || typeof value !== "object") return false;
562
940
  const v = value;
@@ -568,7 +946,7 @@ function isValidChallenge(value) {
568
946
  function isValidReceipt(value) {
569
947
  if (!value || typeof value !== "object") return false;
570
948
  const v = value;
571
- if (v.scheme !== "onchain-proof") return false;
949
+ if (v.scheme !== "onchain-proof" && v.scheme !== "exact") return false;
572
950
  if (typeof v.transaction !== "string" && typeof v.txHash !== "string") return false;
573
951
  if (typeof v.payer !== "string") return false;
574
952
  return true;
@@ -646,12 +1024,12 @@ function makeEvmNetwork(resolved) {
646
1024
  }
647
1025
  let normalized;
648
1026
  try {
649
- normalized = getAddress2(asset);
1027
+ normalized = getAddress3(asset);
650
1028
  } catch {
651
1029
  return null;
652
1030
  }
653
1031
  for (const info of Object.values(resolved.tokens)) {
654
- if (getAddress2(info.address) === normalized) {
1032
+ if (getAddress3(info.address) === normalized) {
655
1033
  return { symbol: info.symbol, decimals: info.decimals };
656
1034
  }
657
1035
  }
@@ -735,7 +1113,7 @@ function makeEvmNetwork(resolved) {
735
1113
  let token = null;
736
1114
  try {
737
1115
  token = await publicClient.readContract({
738
- address: getAddress2(asset),
1116
+ address: getAddress3(asset),
739
1117
  abi: erc20Abi3,
740
1118
  functionName: "balanceOf",
741
1119
  args: [owner]
@@ -767,6 +1145,21 @@ function makeEvmNetwork(resolved) {
767
1145
  accept,
768
1146
  minConfirmations: accept.extra.minConfirmations
769
1147
  });
1148
+ },
1149
+ // Standard x402 `exact` rail (EIP-3009), seller side — EVM only.
1150
+ async exactDomain(asset) {
1151
+ return readExactDomain(publicClient, asset);
1152
+ },
1153
+ async settleExactSelf({ relayer, payload, accept }) {
1154
+ const a = relayer._native;
1155
+ return verifyAndSettleExactEvm({
1156
+ publicClient,
1157
+ walletClient: a.walletClient,
1158
+ account: a.account,
1159
+ chain: resolved.chain,
1160
+ payload,
1161
+ accept
1162
+ });
770
1163
  }
771
1164
  };
772
1165
  }
@@ -783,7 +1176,7 @@ var loaders = {
783
1176
  solana: async () => {
784
1177
  let mod;
785
1178
  try {
786
- mod = await import("./solana-7WJVZGDW.js");
1179
+ mod = await import("./solana-S3UFI3FE.js");
787
1180
  } catch (cause) {
788
1181
  throw new MissingDriverError(
789
1182
  `Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
@@ -795,7 +1188,7 @@ var loaders = {
795
1188
  ton: async () => {
796
1189
  let mod;
797
1190
  try {
798
- mod = await import("./ton-DGZB7W4U.js");
1191
+ mod = await import("./ton-WPTXGLVK.js");
799
1192
  } catch (cause) {
800
1193
  throw new MissingDriverError(
801
1194
  `TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
@@ -807,7 +1200,7 @@ var loaders = {
807
1200
  stellar: async () => {
808
1201
  let mod;
809
1202
  try {
810
- mod = await import("./stellar-HV6VGZX3.js");
1203
+ mod = await import("./stellar-Q5PO23SC.js");
811
1204
  } catch (cause) {
812
1205
  throw new MissingDriverError(
813
1206
  `Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
@@ -819,7 +1212,7 @@ var loaders = {
819
1212
  xrpl: async () => {
820
1213
  let mod;
821
1214
  try {
822
- mod = await import("./xrpl-UEC2GYVV.js");
1215
+ mod = await import("./xrpl-HEAPEXAM.js");
823
1216
  } catch (cause) {
824
1217
  throw new MissingDriverError(
825
1218
  `XRPL selected, but its package isn't installed. Run: npm install xrpl`,
@@ -831,7 +1224,7 @@ var loaders = {
831
1224
  tron: async () => {
832
1225
  let mod;
833
1226
  try {
834
- mod = await import("./tron-RLIL2FDI.js");
1227
+ mod = await import("./tron-6GXBXTR4.js");
835
1228
  } catch (cause) {
836
1229
  throw new MissingDriverError(
837
1230
  `Tron selected, but its package isn't installed. Run: npm install tronweb`,
@@ -843,7 +1236,7 @@ var loaders = {
843
1236
  sui: async () => {
844
1237
  let mod;
845
1238
  try {
846
- mod = await import("./sui-2WFWVFJX.js");
1239
+ mod = await import("./sui-WOXRKJXS.js");
847
1240
  } catch (cause) {
848
1241
  throw new MissingDriverError(
849
1242
  `Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
@@ -855,7 +1248,7 @@ var loaders = {
855
1248
  near: async () => {
856
1249
  let mod;
857
1250
  try {
858
- mod = await import("./near-7MBBCDUE.js");
1251
+ mod = await import("./near-K6BDBABG.js");
859
1252
  } catch (cause) {
860
1253
  throw new MissingDriverError(
861
1254
  `NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
@@ -867,7 +1260,7 @@ var loaders = {
867
1260
  aptos: async () => {
868
1261
  let mod;
869
1262
  try {
870
- mod = await import("./aptos-YQWTGFRZ.js");
1263
+ mod = await import("./aptos-LPBLSEIQ.js");
871
1264
  } catch (cause) {
872
1265
  throw new MissingDriverError(
873
1266
  `Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
@@ -879,7 +1272,7 @@ var loaders = {
879
1272
  algorand: async () => {
880
1273
  let mod;
881
1274
  try {
882
- mod = await import("./algorand-B67G4335.js");
1275
+ mod = await import("./algorand-WGVF4KTU.js");
883
1276
  } catch (cause) {
884
1277
  throw new MissingDriverError(
885
1278
  `Algorand selected, but its package isn't installed. Run: npm install algosdk`,
@@ -1663,7 +2056,10 @@ var PipRailClient = class {
1663
2056
  const chosen = priced.find((p) => p.quote.withinPolicy) ?? priced[0];
1664
2057
  return { net, wallet, accept: chosen.accept, challenge, quote: chosen.quote };
1665
2058
  }
1666
- /** The candidate accepts this client could pay: our scheme, on the bound network. */
2059
+ /** The candidate accepts this client could pay: our scheme, on the bound network.
2060
+ * A dual-advertised challenge may also carry standard `exact` rails — the PipRail
2061
+ * client ignores those (it pays the backendless `onchain-proof` rail); the type
2062
+ * predicate narrows the `X402AnyAccept` union to the rails we settle. */
1667
2063
  gatherCandidates(net, challenge) {
1668
2064
  return challenge.accepts.filter(
1669
2065
  (a) => a.scheme === "onchain-proof" && net.supports(a.network)
@@ -2026,6 +2422,14 @@ function isReplayableBodyInit(value) {
2026
2422
  async function readInvalidReason(response) {
2027
2423
  try {
2028
2424
  const body = await response.clone().json();
2425
+ const ext = body?.extensions;
2426
+ const piprail = ext?.piprail;
2427
+ if (piprail && typeof piprail.code === "string") {
2428
+ return {
2429
+ error: piprail.code,
2430
+ detail: typeof piprail.detail === "string" ? piprail.detail : ""
2431
+ };
2432
+ }
2029
2433
  if (body && (body.status === "invalid" || typeof body.error === "string")) {
2030
2434
  return {
2031
2435
  error: typeof body.error === "string" ? body.error : "no error code",
@@ -2257,6 +2661,100 @@ function paymentTools(client) {
2257
2661
  ];
2258
2662
  }
2259
2663
 
2664
+ // src/facilitator.ts
2665
+ function safeStringify(value) {
2666
+ return JSON.stringify(value, (_k, v) => typeof v === "bigint" ? v.toString() : v);
2667
+ }
2668
+ function mapReason(reason) {
2669
+ const r = (reason ?? "").toLowerCase();
2670
+ if (r.includes("signature")) return "signature_invalid";
2671
+ if (r.includes("recipient")) return "wrong_recipient";
2672
+ if (r.includes("value") || r.includes("amount")) return "amount_too_low";
2673
+ if (r.includes("valid_before") || r.includes("valid_after") || r.includes("expired")) return "payment_expired";
2674
+ if (r.includes("used") || r.includes("replay") || r.includes("nonce") || r.includes("transaction_state")) {
2675
+ return "tx_already_used";
2676
+ }
2677
+ return "tx_reverted";
2678
+ }
2679
+ async function post(url, body, headers) {
2680
+ const res = await fetch(url, {
2681
+ method: "POST",
2682
+ headers: { "content-type": "application/json", ...headers },
2683
+ body: safeStringify(body)
2684
+ });
2685
+ let json = null;
2686
+ try {
2687
+ json = await res.json();
2688
+ } catch {
2689
+ }
2690
+ return { status: res.status, json };
2691
+ }
2692
+ async function settleViaFacilitator(input) {
2693
+ const base2 = input.url.replace(/\/+$/, "");
2694
+ const body = {
2695
+ x402Version: input.x402Version,
2696
+ paymentPayload: input.paymentPayload,
2697
+ paymentRequirements: input.paymentRequirements
2698
+ };
2699
+ const auth = input.authHeaders ? await input.authHeaders() : {};
2700
+ let verify;
2701
+ try {
2702
+ verify = await post(`${base2}/verify`, body, auth);
2703
+ } catch (err) {
2704
+ throw new SettlementError(
2705
+ `exact settle (facilitator ${base2}): /verify request failed (${err instanceof Error ? err.message : String(err)}).`,
2706
+ { cause: err }
2707
+ );
2708
+ }
2709
+ if (verify.status !== 200) {
2710
+ throw new SettlementError(
2711
+ `exact settle (facilitator ${base2}): /verify returned HTTP ${verify.status} (transport/auth error).`
2712
+ );
2713
+ }
2714
+ const vr = verify.json ?? {};
2715
+ if (vr.isValid === false) {
2716
+ return {
2717
+ ok: false,
2718
+ error: mapReason(vr.invalidReason),
2719
+ detail: `Facilitator rejected the payment: ${vr.invalidReason ?? "invalid"}${vr.invalidMessage ? ` \u2014 ${vr.invalidMessage}` : ""}.`
2720
+ };
2721
+ }
2722
+ let settle;
2723
+ try {
2724
+ settle = await post(`${base2}/settle`, body, auth);
2725
+ } catch (err) {
2726
+ throw new SettlementError(
2727
+ `exact settle (facilitator ${base2}): /settle request failed (${err instanceof Error ? err.message : String(err)}).`,
2728
+ { cause: err }
2729
+ );
2730
+ }
2731
+ if (settle.status !== 200) {
2732
+ throw new SettlementError(
2733
+ `exact settle (facilitator ${base2}): /settle returned HTTP ${settle.status} (transport/auth error).`
2734
+ );
2735
+ }
2736
+ const sr = settle.json ?? {};
2737
+ if (!sr.success) {
2738
+ return {
2739
+ ok: false,
2740
+ error: mapReason(sr.errorReason),
2741
+ detail: `Facilitator settlement failed: ${sr.errorReason ?? "unknown"}${sr.errorMessage ? ` \u2014 ${sr.errorMessage}` : ""}.`
2742
+ };
2743
+ }
2744
+ const receipt = {
2745
+ scheme: "exact",
2746
+ success: true,
2747
+ network: input.receipt.network,
2748
+ transaction: sr.transaction,
2749
+ asset: input.receipt.asset,
2750
+ amount: input.receipt.amount,
2751
+ payer: sr.payer ?? input.payerHint ?? "",
2752
+ payTo: input.receipt.payTo,
2753
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
2754
+ };
2755
+ return { ok: true, receipt };
2756
+ }
2757
+
2260
2758
  // src/server.ts
2261
2759
  function toInvalidBody(result) {
2262
2760
  return { x402Version: 2, status: "invalid", error: result.error, detail: result.detail };
@@ -2283,9 +2781,10 @@ function createPaymentGate(options) {
2283
2781
  const genNonce = options.generateNonce ?? (() => globalThis.crypto.randomUUID());
2284
2782
  let resolved;
2285
2783
  function ready() {
2286
- return resolved ??= (async () => {
2784
+ if (resolved) return resolved;
2785
+ const p = (async () => {
2287
2786
  const accepts = normaliseAccepts(options);
2288
- return Promise.all(
2787
+ const specs = await Promise.all(
2289
2788
  accepts.map(async (a) => {
2290
2789
  const net = await resolveNetwork2({ chain: a.chain, rpcUrl: a.rpcUrl ?? options.rpcUrl });
2291
2790
  const payTo = a.payTo ?? options.payTo;
@@ -2297,10 +2796,52 @@ function createPaymentGate(options) {
2297
2796
  net.assertValidPayTo(payTo);
2298
2797
  const { asset, decimals, symbol } = net.resolveToken(a.token);
2299
2798
  const amountBase = parseUnits(a.amount, decimals);
2300
- return { net, asset, decimals, symbol, amountBase, amountFormatted: a.amount, payTo };
2799
+ const spec = { net, asset, decimals, symbol, amountBase, amountFormatted: a.amount, payTo };
2800
+ if (options.exact) spec.exact = await resolveExactRail(net, asset);
2801
+ return spec;
2301
2802
  })
2302
2803
  );
2804
+ if (options.exact && !specs.some((s) => s.exact)) {
2805
+ throw new Error(
2806
+ "requirePayment: `exact` was requested but none of the offered rails support it. The standard `exact` rail is EVM + EIP-3009 only (USDC / EURC) \u2014 not native coins, not USDT, not non-EVM chains. Offer an EVM EIP-3009 token, or drop `exact`."
2807
+ );
2808
+ }
2809
+ return specs;
2303
2810
  })();
2811
+ p.catch(() => {
2812
+ if (resolved === p) resolved = void 0;
2813
+ });
2814
+ resolved = p;
2815
+ return p;
2816
+ }
2817
+ async function resolveExactRail(net, asset) {
2818
+ const cfg = options.exact;
2819
+ if (net.family !== "evm" || asset === "native" || !net.exactDomain || !net.settleExactSelf) {
2820
+ return void 0;
2821
+ }
2822
+ const domain = await net.exactDomain(asset);
2823
+ if (!domain) {
2824
+ throw new Error(
2825
+ `requirePayment: \`exact\` requested for asset ${asset} on ${net.network}, but it isn't an EIP-3009 token (couldn't read name()/version()/authorizationState). The exact rail supports USDC / EURC and other EIP-3009 tokens \u2014 USDT and native coins need onchain-proof. (Or check your rpcUrl is reachable.)`
2826
+ );
2827
+ }
2828
+ if (cfg.settle === "self") {
2829
+ if (cfg.relayer === void 0) {
2830
+ throw new Error(
2831
+ "requirePayment: exact `settle: 'self'` needs a `relayer` wallet (the gas-paying key that broadcasts transferWithAuthorization), e.g. exact: { settle: 'self', relayer: { privateKey } }."
2832
+ );
2833
+ }
2834
+ const relayer = net.bindWallet(cfg.relayer);
2835
+ return { domain, mode: { kind: "self", relayer } };
2836
+ }
2837
+ return {
2838
+ domain,
2839
+ mode: {
2840
+ kind: "facilitator",
2841
+ url: cfg.settle.facilitator,
2842
+ ...cfg.settle.authHeaders ? { authHeaders: cfg.settle.authHeaders } : {}
2843
+ }
2844
+ };
2304
2845
  }
2305
2846
  const hasCustomStore = Boolean(options.isUsed || options.markUsed);
2306
2847
  const localUsed = /* @__PURE__ */ new Set();
@@ -2337,93 +2878,191 @@ function createPaymentGate(options) {
2337
2878
  }
2338
2879
  };
2339
2880
  }
2340
- async function challenge(resourceUrl = "") {
2881
+ function buildExactAccept(s) {
2882
+ const d = s.exact.domain;
2883
+ return {
2884
+ scheme: "exact",
2885
+ network: s.net.network,
2886
+ amount: s.amountBase.toString(),
2887
+ asset: s.asset,
2888
+ payTo: s.payTo,
2889
+ maxTimeoutSeconds,
2890
+ extra: {
2891
+ assetTransferMethod: "eip3009",
2892
+ name: d.name,
2893
+ version: d.version,
2894
+ minConfirmations,
2895
+ decimals: s.decimals,
2896
+ amountFormatted: s.amountFormatted,
2897
+ ...s.symbol ? { symbol: s.symbol } : {}
2898
+ }
2899
+ };
2900
+ }
2901
+ function buildAccepts(specs, nonce) {
2902
+ const out = [];
2903
+ for (const s of specs) {
2904
+ if (s.exact) out.push(buildExactAccept(s));
2905
+ out.push(buildAccept(s, nonce));
2906
+ }
2907
+ return out;
2908
+ }
2909
+ async function makeChallenge(resourceUrl, opts) {
2341
2910
  const specs = await ready();
2342
2911
  const nonce = genNonce();
2343
2912
  const challenge2 = {
2344
2913
  x402Version: 2,
2345
- error: null,
2346
2914
  resource: {
2347
2915
  url: resourceUrl,
2348
2916
  ...options.description ? { description: options.description } : {}
2349
2917
  },
2350
- accepts: specs.map((s) => buildAccept(s, nonce))
2918
+ accepts: buildAccepts(specs, nonce),
2919
+ ...opts?.error ? { error: opts.error } : {},
2920
+ ...opts?.extensions ? { extensions: opts.extensions } : {}
2351
2921
  };
2352
2922
  return { challenge: challenge2, requiredHeader: buildChallengeHeader(challenge2) };
2353
2923
  }
2924
+ async function challenge(resourceUrl = "") {
2925
+ return makeChallenge(resourceUrl);
2926
+ }
2354
2927
  async function asChallenge() {
2355
- const { challenge: c, requiredHeader } = await challenge();
2928
+ const { challenge: c, requiredHeader } = await makeChallenge("");
2356
2929
  return { kind: "challenge", challenge: c, requiredHeader, statusCode: 402 };
2357
2930
  }
2931
+ async function rejection(code, detail) {
2932
+ const { challenge: c, requiredHeader } = await makeChallenge("", {
2933
+ error: `${code}: ${detail}`,
2934
+ extensions: { piprail: { code, detail } }
2935
+ });
2936
+ return { kind: "invalid", error: code, detail, challenge: c, requiredHeader, statusCode: 402 };
2937
+ }
2938
+ function fireOnPaid(receipt) {
2939
+ if (options.onPaid) {
2940
+ try {
2941
+ options.onPaid(receipt);
2942
+ } catch {
2943
+ }
2944
+ }
2945
+ }
2358
2946
  async function describe(resourceUrl = "") {
2359
2947
  const specs = await ready();
2360
- const accepts = specs.map((s) => ({
2361
- scheme: "onchain-proof",
2362
- network: s.net.network,
2363
- asset: s.asset,
2364
- payTo: s.payTo,
2365
- amount: s.amountBase.toString(),
2366
- amountFormatted: s.amountFormatted,
2367
- decimals: s.decimals,
2368
- maxTimeoutSeconds,
2369
- ...s.symbol ? { symbol: s.symbol } : {}
2370
- }));
2948
+ const accepts = [];
2949
+ for (const s of specs) {
2950
+ const base2 = {
2951
+ network: s.net.network,
2952
+ asset: s.asset,
2953
+ payTo: s.payTo,
2954
+ amount: s.amountBase.toString(),
2955
+ amountFormatted: s.amountFormatted,
2956
+ decimals: s.decimals,
2957
+ maxTimeoutSeconds,
2958
+ ...s.symbol ? { symbol: s.symbol } : {}
2959
+ };
2960
+ if (s.exact) accepts.push({ scheme: "exact", ...base2 });
2961
+ accepts.push({ scheme: "onchain-proof", ...base2 });
2962
+ }
2371
2963
  return {
2372
2964
  url: resourceUrl,
2373
2965
  ...options.description ? { description: options.description } : {},
2374
2966
  accepts
2375
2967
  };
2376
2968
  }
2377
- async function verify(paymentSignature) {
2378
- const raw = normaliseHeader(paymentSignature);
2379
- if (!raw) return asChallenge();
2380
- const sig = parseSignatureHeader(raw);
2381
- if (!sig || !sig.accepted || typeof sig.accepted.network !== "string" || typeof sig.accepted.asset !== "string") {
2382
- return asChallenge();
2383
- }
2969
+ async function verifyOnchainProof(sig) {
2384
2970
  const specs = await ready();
2385
2971
  const spec = specs.find(
2386
2972
  (s) => s.net.network === sig.accepted.network && s.asset === sig.accepted.asset
2387
2973
  );
2388
2974
  if (!spec) {
2389
- return {
2390
- kind: "invalid",
2391
- error: "transfer_not_found",
2392
- detail: `Proof claims ${sig.accepted.asset} on ${sig.accepted.network}, which this resource doesn't accept (offered: ${specs.map((s) => `${s.asset}@${s.net.network}`).join(", ")}).`,
2393
- statusCode: 402
2394
- };
2975
+ return rejection(
2976
+ "transfer_not_found",
2977
+ `Proof claims ${sig.accepted.asset} on ${sig.accepted.network}, which this resource doesn't accept (offered: ${specs.map((s) => `${s.asset}@${s.net.network}`).join(", ")}).`
2978
+ );
2395
2979
  }
2396
2980
  const ref = sig.payload.txHash;
2397
- if (await claimTx(ref)) {
2398
- return {
2399
- kind: "invalid",
2400
- error: "tx_already_used",
2401
- detail: `Proof ${ref} was already redeemed.`,
2402
- statusCode: 402
2403
- };
2404
- }
2981
+ if (await claimTx(ref)) return rejection("tx_already_used", `Proof ${ref} was already redeemed.`);
2405
2982
  const result = await spec.net.verify(ref, buildAccept(spec, sig.payload.nonce));
2406
2983
  if (!result.ok) {
2407
2984
  await settleTx(ref, false);
2408
- return {
2409
- kind: "invalid",
2410
- error: result.error,
2411
- detail: result.detail,
2412
- statusCode: 402
2413
- };
2985
+ return rejection(result.error, result.detail);
2414
2986
  }
2415
2987
  await settleTx(ref, true);
2416
- if (options.onPaid) {
2417
- try {
2418
- options.onPaid(result.receipt);
2419
- } catch {
2988
+ fireOnPaid(result.receipt);
2989
+ return { kind: "paid", receipt: result.receipt, receiptHeader: buildReceiptHeader(result.receipt) };
2990
+ }
2991
+ async function verifyExact(exact) {
2992
+ const specs = await ready();
2993
+ const exactSpecs = specs.filter((s) => s.exact);
2994
+ if (exactSpecs.length === 0) {
2995
+ return rejection("transfer_not_found", "This resource offers no standard `exact` rail.");
2996
+ }
2997
+ const isCaip = exact.network.startsWith("eip155:");
2998
+ let candidates = isCaip ? exactSpecs.filter((s) => s.net.network === exact.network) : exactSpecs;
2999
+ if (exact.asset) {
3000
+ candidates = candidates.filter((s) => s.asset.toLowerCase() === exact.asset.toLowerCase());
3001
+ }
3002
+ let spec = candidates[0];
3003
+ if (!isCaip && !exact.asset && exactSpecs.length > 1) spec = void 0;
3004
+ if (!spec && !isCaip && !exact.asset && exactSpecs.length === 1) spec = exactSpecs[0];
3005
+ if (!spec || !spec.exact) {
3006
+ return rejection(
3007
+ "transfer_not_found",
3008
+ `No \`exact\` rail offered for ${exact.network}${exact.asset ? `/${exact.asset}` : ""} (offered: ${exactSpecs.map((s) => `${s.asset}@${s.net.network}`).join(", ")}).`
3009
+ );
3010
+ }
3011
+ const nonce = exact.payload.authorization.nonce;
3012
+ if (await claimTx(nonce)) {
3013
+ return rejection("tx_already_used", `Authorization nonce ${nonce} was already redeemed.`);
3014
+ }
3015
+ const accept = buildExactAccept(spec);
3016
+ const mode = spec.exact.mode;
3017
+ let result;
3018
+ try {
3019
+ if (mode.kind === "self") {
3020
+ result = await spec.net.settleExactSelf({ relayer: mode.relayer, payload: exact.payload, accept });
3021
+ } else {
3022
+ result = await settleViaFacilitator({
3023
+ url: mode.url,
3024
+ ...mode.authHeaders ? { authHeaders: mode.authHeaders } : {},
3025
+ // PipRail always builds a v2-shaped paymentRequirements (CAIP-2 network + `amount`),
3026
+ // so force x402Version:2 — echoing a v1 client's version here would hand the facilitator
3027
+ // a self-inconsistent request (v1 envelope, v2 requirements). The inner payload is
3028
+ // byte-identical across versions, so forwarding it verbatim is fine.
3029
+ x402Version: 2,
3030
+ paymentPayload: exact.raw,
3031
+ paymentRequirements: {
3032
+ scheme: "exact",
3033
+ network: accept.network,
3034
+ asset: accept.asset,
3035
+ amount: accept.amount,
3036
+ payTo: accept.payTo,
3037
+ maxTimeoutSeconds: accept.maxTimeoutSeconds,
3038
+ extra: { name: accept.extra.name, version: accept.extra.version }
3039
+ },
3040
+ receipt: { network: accept.network, asset: accept.asset, payTo: accept.payTo, amount: accept.amount },
3041
+ payerHint: exact.payload.authorization.from
3042
+ });
2420
3043
  }
3044
+ } catch (err) {
3045
+ await settleTx(nonce, false);
3046
+ throw err;
2421
3047
  }
2422
- return {
2423
- kind: "paid",
2424
- receipt: result.receipt,
2425
- receiptHeader: buildReceiptHeader(result.receipt)
2426
- };
3048
+ if (!result.ok) {
3049
+ await settleTx(nonce, false);
3050
+ return rejection(result.error, result.detail);
3051
+ }
3052
+ await settleTx(nonce, true);
3053
+ fireOnPaid(result.receipt);
3054
+ return { kind: "paid", receipt: result.receipt, receiptHeader: buildReceiptHeader(result.receipt) };
3055
+ }
3056
+ async function verify(paymentSignature) {
3057
+ const raw = normaliseHeader(paymentSignature);
3058
+ if (!raw) return asChallenge();
3059
+ const sig = parseSignatureHeader(raw);
3060
+ if (sig && sig.accepted && typeof sig.accepted.network === "string" && typeof sig.accepted.asset === "string") {
3061
+ return verifyOnchainProof(sig);
3062
+ }
3063
+ const exact = parseExactPaymentHeader(raw);
3064
+ if (exact) return verifyExact(exact);
3065
+ return asChallenge();
2427
3066
  }
2428
3067
  return { challenge, verify, describe };
2429
3068
  }
@@ -2432,14 +3071,20 @@ function requirePayment(options) {
2432
3071
  return async (req, res, next) => {
2433
3072
  let result;
2434
3073
  try {
2435
- result = await gate.verify(req.headers[HEADER_SIGNATURE]);
3074
+ result = await gate.verify(req.headers[HEADER_SIGNATURE] ?? req.headers[HEADER_SIGNATURE_V1]);
2436
3075
  } catch (err) {
3076
+ if (err instanceof SettlementError) {
3077
+ res.status(502);
3078
+ res.json({ x402Version: 2, error: "settlement_failed", detail: err.message });
3079
+ return;
3080
+ }
2437
3081
  next(err);
2438
3082
  return;
2439
3083
  }
2440
3084
  switch (result.kind) {
2441
3085
  case "paid":
2442
3086
  res.setHeader(HEADER_RESPONSE, result.receiptHeader);
3087
+ res.setHeader(HEADER_RESPONSE_V1, result.receiptHeader);
2443
3088
  return next();
2444
3089
  case "challenge":
2445
3090
  res.setHeader(HEADER_REQUIRED, result.requiredHeader);
@@ -2447,8 +3092,9 @@ function requirePayment(options) {
2447
3092
  res.json(result.challenge);
2448
3093
  return;
2449
3094
  case "invalid":
3095
+ res.setHeader(HEADER_REQUIRED, result.requiredHeader);
2450
3096
  res.status(result.statusCode);
2451
- res.json(toInvalidBody(result));
3097
+ res.json(result.challenge);
2452
3098
  return;
2453
3099
  }
2454
3100
  };
@@ -2458,104 +3104,6 @@ function normaliseHeader(value) {
2458
3104
  return value;
2459
3105
  }
2460
3106
 
2461
- // src/drivers/evm/exact.ts
2462
- var EXACT_NETWORK_SLUGS = {
2463
- ethereum: 1,
2464
- base: 8453,
2465
- "base-sepolia": 84532,
2466
- arbitrum: 42161,
2467
- optimism: 10,
2468
- polygon: 137,
2469
- avalanche: 43114
2470
- };
2471
- function chainIdForExactNetwork(slug) {
2472
- return EXACT_NETWORK_SLUGS[slug] ?? null;
2473
- }
2474
- var EIP3009_TYPES = {
2475
- TransferWithAuthorization: [
2476
- { name: "from", type: "address" },
2477
- { name: "to", type: "address" },
2478
- { name: "value", type: "uint256" },
2479
- { name: "validAfter", type: "uint256" },
2480
- { name: "validBefore", type: "uint256" },
2481
- { name: "nonce", type: "bytes32" }
2482
- ]
2483
- };
2484
- function parseExactRequirements(body) {
2485
- if (!body || typeof body !== "object") return null;
2486
- const accepts = body.accepts;
2487
- if (!Array.isArray(accepts)) return null;
2488
- const out = [];
2489
- for (const raw of accepts) {
2490
- if (!raw || typeof raw !== "object") continue;
2491
- const a = raw;
2492
- if (a.scheme !== "exact") continue;
2493
- const amount = a.maxAmountRequired ?? a.amount;
2494
- if (typeof a.network !== "string" || typeof amount !== "string" || typeof a.asset !== "string" || typeof a.payTo !== "string") {
2495
- continue;
2496
- }
2497
- out.push({
2498
- scheme: "exact",
2499
- network: a.network,
2500
- maxAmountRequired: amount,
2501
- asset: a.asset,
2502
- payTo: a.payTo,
2503
- maxTimeoutSeconds: typeof a.maxTimeoutSeconds === "number" ? a.maxTimeoutSeconds : 600,
2504
- ...a.extra && typeof a.extra === "object" ? { extra: a.extra } : {},
2505
- ...typeof a.description === "string" ? { description: a.description } : {},
2506
- ...typeof a.resource === "string" ? { resource: a.resource } : {}
2507
- });
2508
- }
2509
- return out;
2510
- }
2511
- async function buildExactAuthorization(params) {
2512
- const { account, accept, chainId, now, nonce } = params;
2513
- if (!account.signTypedData) {
2514
- throw new Error("buildExactAuthorization: the account cannot sign EIP-712 typed data.");
2515
- }
2516
- const authorization = {
2517
- from: account.address,
2518
- to: accept.payTo,
2519
- value: accept.maxAmountRequired,
2520
- validAfter: "0",
2521
- validBefore: String(now + accept.maxTimeoutSeconds),
2522
- nonce
2523
- };
2524
- const signature = await account.signTypedData({
2525
- domain: {
2526
- name: accept.extra?.name ?? "USD Coin",
2527
- version: accept.extra?.version ?? "2",
2528
- chainId,
2529
- verifyingContract: accept.asset
2530
- },
2531
- types: EIP3009_TYPES,
2532
- primaryType: "TransferWithAuthorization",
2533
- message: {
2534
- from: authorization.from,
2535
- to: authorization.to,
2536
- value: BigInt(authorization.value),
2537
- validAfter: BigInt(authorization.validAfter),
2538
- validBefore: BigInt(authorization.validBefore),
2539
- nonce: authorization.nonce
2540
- }
2541
- });
2542
- return { authorization, signature };
2543
- }
2544
- function base64(str) {
2545
- if (typeof btoa === "function") return btoa(str);
2546
- if (typeof Buffer !== "undefined") return Buffer.from(str, "utf8").toString("base64");
2547
- throw new Error("No base64 encoder available in this runtime.");
2548
- }
2549
- function encodeXPaymentHeader(input) {
2550
- const payload = {
2551
- x402Version: input.x402Version ?? 1,
2552
- scheme: "exact",
2553
- network: input.network,
2554
- payload: { signature: input.signature, authorization: input.authorization }
2555
- };
2556
- return base64(JSON.stringify(payload));
2557
- }
2558
-
2559
3107
  // src/discovery.ts
2560
3108
  var GENERATOR = "@piprail/sdk \xB7 https://piprail.com";
2561
3109
  function pathOf(url) {
@@ -2616,6 +3164,11 @@ export {
2616
3164
  EIP3009_TYPES,
2617
3165
  EXACT_NETWORK_SLUGS,
2618
3166
  GENERATOR,
3167
+ HEADER_REQUIRED,
3168
+ HEADER_RESPONSE,
3169
+ HEADER_RESPONSE_V1,
3170
+ HEADER_SIGNATURE,
3171
+ HEADER_SIGNATURE_V1,
2619
3172
  InsufficientFundsError,
2620
3173
  InvalidEnvelopeError,
2621
3174
  MaxRetriesExceededError,
@@ -2627,6 +3180,7 @@ export {
2627
3180
  PipRailClient,
2628
3181
  PipRailError,
2629
3182
  RecipientNotReadyError,
3183
+ SettlementError,
2630
3184
  UnknownTokenError,
2631
3185
  UnsupportedNetworkError,
2632
3186
  WrongChainError,
@@ -2640,22 +3194,26 @@ export {
2640
3194
  buildX402DnsTxt,
2641
3195
  chainIdForExactNetwork,
2642
3196
  createPaymentGate,
3197
+ eip3009Abi,
2643
3198
  encodeXPaymentHeader,
2644
3199
  evaluatePolicy,
2645
3200
  normalizeNetwork,
2646
3201
  parseChallenge,
3202
+ parseExactPaymentHeader,
2647
3203
  parseExactRequirements,
2648
3204
  parseReceipt,
2649
3205
  parseSignatureHeader,
2650
3206
  paymentTools,
2651
3207
  pickAccept,
2652
3208
  planAcross,
3209
+ readExactDomain,
2653
3210
  register402Index,
2654
3211
  registerDriver,
2655
3212
  registerX402Scan,
2656
3213
  requirePayment,
2657
3214
  resolveChain,
2658
3215
  searchOpenIndexes,
3216
+ settleViaFacilitator,
2659
3217
  toInsufficientFundsError,
2660
3218
  toInvalidBody
2661
3219
  };