@alleyboss/micropay-solana-x402-paywall 3.5.0 → 3.5.2

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/dist/index.cjs CHANGED
@@ -631,6 +631,456 @@ async function getRemainingCredits(token, secret) {
631
631
  };
632
632
  }
633
633
 
634
+ // src/fetch/types.ts
635
+ var X402ErrorCode = {
636
+ /** User rejected the payment */
637
+ USER_REJECTED: "USER_REJECTED",
638
+ /** Insufficient wallet balance */
639
+ INSUFFICIENT_BALANCE: "INSUFFICIENT_BALANCE",
640
+ /** Transaction failed on-chain */
641
+ TRANSACTION_FAILED: "TRANSACTION_FAILED",
642
+ /** Network/RPC error */
643
+ NETWORK_ERROR: "NETWORK_ERROR",
644
+ /** Invalid 402 response format */
645
+ INVALID_402_RESPONSE: "INVALID_402_RESPONSE",
646
+ /** Payment timeout */
647
+ TIMEOUT: "TIMEOUT",
648
+ /** Wallet not connected */
649
+ WALLET_NOT_CONNECTED: "WALLET_NOT_CONNECTED",
650
+ /** Payment amount exceeds maxPaymentPerRequest */
651
+ AMOUNT_EXCEEDS_LIMIT: "AMOUNT_EXCEEDS_LIMIT",
652
+ /** Recipient address not in allowedRecipients whitelist */
653
+ RECIPIENT_NOT_ALLOWED: "RECIPIENT_NOT_ALLOWED",
654
+ /** Rate limit exceeded */
655
+ RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED"
656
+ };
657
+
658
+ // src/fetch/errors.ts
659
+ var X402PaymentError = class _X402PaymentError extends Error {
660
+ constructor(message, code, requirements, cause) {
661
+ super(message);
662
+ this.code = code;
663
+ this.requirements = requirements;
664
+ this.cause = cause;
665
+ if (Error.captureStackTrace) {
666
+ Error.captureStackTrace(this, _X402PaymentError);
667
+ }
668
+ }
669
+ name = "X402PaymentError";
670
+ /**
671
+ * Check if error is retryable
672
+ */
673
+ get isRetryable() {
674
+ const retryableCodes = [
675
+ X402ErrorCode.NETWORK_ERROR,
676
+ X402ErrorCode.TIMEOUT,
677
+ X402ErrorCode.TRANSACTION_FAILED
678
+ ];
679
+ return retryableCodes.includes(this.code);
680
+ }
681
+ /**
682
+ * Convert to JSON-serializable object
683
+ */
684
+ toJSON() {
685
+ return {
686
+ name: this.name,
687
+ message: this.message,
688
+ code: this.code,
689
+ requirements: this.requirements,
690
+ isRetryable: this.isRetryable
691
+ };
692
+ }
693
+ };
694
+ function userRejectedError(requirements) {
695
+ return new X402PaymentError(
696
+ "User rejected the payment request",
697
+ X402ErrorCode.USER_REJECTED,
698
+ requirements
699
+ );
700
+ }
701
+ function insufficientBalanceError(requirements, balance) {
702
+ return new X402PaymentError(
703
+ `Insufficient balance: have ${balance} lamports, need ${requirements.amount}`,
704
+ X402ErrorCode.INSUFFICIENT_BALANCE,
705
+ requirements
706
+ );
707
+ }
708
+ function transactionFailedError(requirements, cause) {
709
+ return new X402PaymentError(
710
+ `Transaction failed: ${cause?.message ?? "Unknown error"}`,
711
+ X402ErrorCode.TRANSACTION_FAILED,
712
+ requirements,
713
+ cause
714
+ );
715
+ }
716
+ function networkError(cause) {
717
+ return new X402PaymentError(
718
+ `Network error: ${cause?.message ?? "Connection failed"}`,
719
+ X402ErrorCode.NETWORK_ERROR,
720
+ void 0,
721
+ cause
722
+ );
723
+ }
724
+ function invalid402ResponseError(details) {
725
+ return new X402PaymentError(
726
+ `Invalid 402 response: ${details ?? "Missing or malformed payment requirements"}`,
727
+ X402ErrorCode.INVALID_402_RESPONSE
728
+ );
729
+ }
730
+ function timeoutError(requirements) {
731
+ return new X402PaymentError(
732
+ "Payment flow timed out",
733
+ X402ErrorCode.TIMEOUT,
734
+ requirements
735
+ );
736
+ }
737
+ function walletNotConnectedError() {
738
+ return new X402PaymentError(
739
+ "Wallet is not connected",
740
+ X402ErrorCode.WALLET_NOT_CONNECTED
741
+ );
742
+ }
743
+ function amountExceedsLimitError(requirements, limit) {
744
+ return new X402PaymentError(
745
+ `Payment amount ${requirements.amount} exceeds limit of ${limit} lamports`,
746
+ X402ErrorCode.AMOUNT_EXCEEDS_LIMIT,
747
+ requirements
748
+ );
749
+ }
750
+ function recipientNotAllowedError(requirements, recipient) {
751
+ return new X402PaymentError(
752
+ `Recipient ${recipient} is not in the allowed recipients list`,
753
+ X402ErrorCode.RECIPIENT_NOT_ALLOWED,
754
+ requirements
755
+ );
756
+ }
757
+ function rateLimitExceededError(limit, windowMs) {
758
+ return new X402PaymentError(
759
+ `Rate limit exceeded: max ${limit} payments per ${windowMs / 1e3}s`,
760
+ X402ErrorCode.RATE_LIMIT_EXCEEDED
761
+ );
762
+ }
763
+
764
+ // src/shared/constants.ts
765
+ var RPC_ENDPOINTS = {
766
+ "mainnet-beta": "https://api.mainnet-beta.solana.com",
767
+ "devnet": "https://api.devnet.solana.com",
768
+ "testnet": "https://api.testnet.solana.com"
769
+ };
770
+ var DEFAULT_CONFIRMATION_TIMEOUT = 3e4;
771
+ var DEFAULT_MAX_RETRIES = 3;
772
+ var DEFAULT_RATE_LIMIT_WINDOW_MS = 6e4;
773
+ var DEFAULT_RATE_LIMIT_MAX_PAYMENTS = 10;
774
+
775
+ // src/fetch/x402Fetch.ts
776
+ function isKeypair(wallet) {
777
+ return wallet instanceof web3_js.Keypair;
778
+ }
779
+ function isWalletConnected(wallet) {
780
+ if (isKeypair(wallet)) return true;
781
+ return wallet.connected && wallet.publicKey != null;
782
+ }
783
+ function getPublicKey(wallet) {
784
+ if (isKeypair(wallet)) return wallet.publicKey;
785
+ if (!wallet.publicKey) throw walletNotConnectedError();
786
+ return wallet.publicKey;
787
+ }
788
+ function parse402Response(response) {
789
+ const x402Header = response.headers.get("X-Payment-Requirements");
790
+ if (x402Header) {
791
+ try {
792
+ const parsed = JSON.parse(x402Header);
793
+ return {
794
+ payTo: parsed.payTo ?? parsed.recipient,
795
+ amount: String(parsed.amount),
796
+ asset: parsed.asset ?? "SOL",
797
+ network: parsed.network ?? "solana-mainnet",
798
+ description: parsed.description,
799
+ resource: parsed.resource,
800
+ maxAge: parsed.maxAge
801
+ };
802
+ } catch {
803
+ throw invalid402ResponseError("Invalid X-Payment-Requirements header");
804
+ }
805
+ }
806
+ const wwwAuth = response.headers.get("WWW-Authenticate");
807
+ if (wwwAuth?.startsWith("X402")) {
808
+ try {
809
+ const base64Part = wwwAuth.slice(5).trim();
810
+ const jsonStr = atob(base64Part);
811
+ const parsed = JSON.parse(jsonStr);
812
+ return {
813
+ payTo: parsed.payTo ?? parsed.recipient,
814
+ amount: String(parsed.amount),
815
+ asset: parsed.asset ?? "SOL",
816
+ network: parsed.network ?? "solana-mainnet",
817
+ description: parsed.description,
818
+ resource: parsed.resource,
819
+ maxAge: parsed.maxAge
820
+ };
821
+ } catch {
822
+ throw invalid402ResponseError("Invalid WWW-Authenticate header");
823
+ }
824
+ }
825
+ const paymentRequired = response.headers.get("payment-required");
826
+ if (paymentRequired) {
827
+ try {
828
+ const jsonStr = atob(paymentRequired.trim());
829
+ const parsed = JSON.parse(jsonStr);
830
+ const option = Array.isArray(parsed.accepts) && parsed.accepts.length > 0 ? parsed.accepts[0] : parsed;
831
+ return {
832
+ payTo: option.payTo ?? option.recipient,
833
+ amount: String(option.amount),
834
+ asset: option.asset ?? "SOL",
835
+ network: option.network ?? "solana-mainnet",
836
+ description: parsed.description ?? option.description,
837
+ resource: parsed.resource ?? option.resource,
838
+ maxAge: parsed.maxAge ?? option.maxAge
839
+ };
840
+ } catch {
841
+ throw invalid402ResponseError("Invalid payment-required header");
842
+ }
843
+ }
844
+ throw invalid402ResponseError("No payment requirements found in 402 response");
845
+ }
846
+ function buildPaymentHeader(signature) {
847
+ const payload = {
848
+ x402Version: 2,
849
+ scheme: "exact",
850
+ payload: { signature }
851
+ };
852
+ return `X402 ${btoa(JSON.stringify(payload))}`;
853
+ }
854
+ function createX402Fetch(config) {
855
+ const {
856
+ wallet,
857
+ network = "mainnet-beta",
858
+ connection: providedConnection,
859
+ // Reserved for future facilitator integration
860
+ onPaymentRequired,
861
+ onPaymentSuccess,
862
+ onPaymentError,
863
+ priorityFee,
864
+ maxRetries = DEFAULT_MAX_RETRIES,
865
+ timeout = DEFAULT_CONFIRMATION_TIMEOUT,
866
+ // Security options
867
+ maxPaymentPerRequest,
868
+ allowedRecipients,
869
+ // UX options
870
+ commitment = "confirmed",
871
+ rateLimit
872
+ } = config;
873
+ const paymentTimestamps = [];
874
+ const rateLimitMax = rateLimit?.maxPayments ?? DEFAULT_RATE_LIMIT_MAX_PAYMENTS;
875
+ const rateLimitWindow = rateLimit?.windowMs ?? DEFAULT_RATE_LIMIT_WINDOW_MS;
876
+ function checkRateLimit() {
877
+ const now = Date.now();
878
+ while (paymentTimestamps.length > 0 && paymentTimestamps[0] < now - rateLimitWindow) {
879
+ paymentTimestamps.shift();
880
+ }
881
+ if (paymentTimestamps.length >= rateLimitMax) {
882
+ throw rateLimitExceededError(rateLimitMax, rateLimitWindow);
883
+ }
884
+ }
885
+ function recordPayment() {
886
+ paymentTimestamps.push(Date.now());
887
+ }
888
+ function validateSecurityRequirements(requirements) {
889
+ const amountLamports = BigInt(requirements.amount);
890
+ if (maxPaymentPerRequest !== void 0 && amountLamports > maxPaymentPerRequest) {
891
+ throw amountExceedsLimitError(requirements, maxPaymentPerRequest);
892
+ }
893
+ if (allowedRecipients !== void 0 && allowedRecipients.length > 0) {
894
+ if (!allowedRecipients.includes(requirements.payTo)) {
895
+ throw recipientNotAllowedError(requirements, requirements.payTo);
896
+ }
897
+ }
898
+ }
899
+ const connection = providedConnection ?? new web3_js.Connection(RPC_ENDPOINTS[network], {
900
+ commitment
901
+ });
902
+ async function executePayment(requirements) {
903
+ const payer = getPublicKey(wallet);
904
+ const recipient = new web3_js.PublicKey(requirements.payTo);
905
+ const amountLamports = BigInt(requirements.amount);
906
+ const balance = await connection.getBalance(payer);
907
+ if (BigInt(balance) < amountLamports) {
908
+ throw insufficientBalanceError(requirements, BigInt(balance));
909
+ }
910
+ const instructions = [];
911
+ if (priorityFee?.enabled) {
912
+ instructions.push(
913
+ web3_js.ComputeBudgetProgram.setComputeUnitPrice({
914
+ microLamports: priorityFee.microLamports ?? 5e3
915
+ })
916
+ );
917
+ }
918
+ instructions.push(
919
+ web3_js.SystemProgram.transfer({
920
+ fromPubkey: payer,
921
+ toPubkey: recipient,
922
+ lamports: amountLamports
923
+ })
924
+ );
925
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
926
+ const messageV0 = new web3_js.TransactionMessage({
927
+ payerKey: payer,
928
+ recentBlockhash: blockhash,
929
+ instructions
930
+ }).compileToV0Message();
931
+ const tx = new web3_js.VersionedTransaction(messageV0);
932
+ if (isKeypair(wallet)) {
933
+ tx.sign([wallet]);
934
+ } else {
935
+ const signerWallet = wallet;
936
+ if (!signerWallet.signTransaction) {
937
+ throw new X402PaymentError(
938
+ "Wallet does not support transaction signing. Use a SignerWalletAdapter.",
939
+ "WALLET_NOT_CONNECTED"
940
+ );
941
+ }
942
+ const signedTx = await signerWallet.signTransaction(tx);
943
+ if (signedTx.signatures[0]) {
944
+ tx.signatures[0] = signedTx.signatures[0];
945
+ }
946
+ }
947
+ const signature = await connection.sendTransaction(tx, {
948
+ maxRetries
949
+ });
950
+ await connection.confirmTransaction({
951
+ signature,
952
+ blockhash,
953
+ lastValidBlockHeight
954
+ }, commitment);
955
+ return signature;
956
+ }
957
+ async function x402Fetch(input, init) {
958
+ const { skipPayment, paymentOverride, ...fetchInit } = init ?? {};
959
+ let response;
960
+ try {
961
+ response = await fetch(input, fetchInit);
962
+ } catch (error) {
963
+ throw networkError(error instanceof Error ? error : void 0);
964
+ }
965
+ if (response.status !== 402) {
966
+ return response;
967
+ }
968
+ if (skipPayment) {
969
+ return response;
970
+ }
971
+ if (!isWalletConnected(wallet)) {
972
+ throw walletNotConnectedError();
973
+ }
974
+ let requirements;
975
+ try {
976
+ requirements = parse402Response(response);
977
+ if (paymentOverride) {
978
+ requirements = { ...requirements, ...paymentOverride };
979
+ }
980
+ } catch (error) {
981
+ if (error instanceof X402PaymentError) throw error;
982
+ throw invalid402ResponseError(error instanceof Error ? error.message : void 0);
983
+ }
984
+ validateSecurityRequirements(requirements);
985
+ checkRateLimit();
986
+ if (onPaymentRequired) {
987
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
988
+ const shouldProceed = await onPaymentRequired(requirements, url);
989
+ if (!shouldProceed) {
990
+ throw userRejectedError(requirements);
991
+ }
992
+ }
993
+ let signature;
994
+ try {
995
+ const paymentPromise = executePayment(requirements);
996
+ const timeoutPromise = new Promise((_, reject) => {
997
+ setTimeout(() => reject(timeoutError(requirements)), timeout);
998
+ });
999
+ signature = await Promise.race([paymentPromise, timeoutPromise]);
1000
+ recordPayment();
1001
+ if (onPaymentSuccess) {
1002
+ await onPaymentSuccess(signature, requirements);
1003
+ }
1004
+ } catch (error) {
1005
+ if (error instanceof X402PaymentError) {
1006
+ if (onPaymentError) {
1007
+ await onPaymentError(error, requirements);
1008
+ }
1009
+ throw error;
1010
+ }
1011
+ const wrappedError = transactionFailedError(
1012
+ requirements,
1013
+ error instanceof Error ? error : void 0
1014
+ );
1015
+ if (onPaymentError) {
1016
+ await onPaymentError(wrappedError, requirements);
1017
+ }
1018
+ throw wrappedError;
1019
+ }
1020
+ const retryHeaders = new Headers(fetchInit?.headers);
1021
+ retryHeaders.set("Authorization", buildPaymentHeader(signature));
1022
+ try {
1023
+ return await fetch(input, {
1024
+ ...fetchInit,
1025
+ headers: retryHeaders
1026
+ });
1027
+ } catch (error) {
1028
+ throw networkError(error instanceof Error ? error : void 0);
1029
+ }
1030
+ }
1031
+ return x402Fetch;
1032
+ }
1033
+
1034
+ // src/agent/payingAgent.ts
1035
+ function createPayingAgent(privateKey, config = {}) {
1036
+ const {
1037
+ network = "mainnet-beta",
1038
+ rpcUrl,
1039
+ maxPaymentPerRequest,
1040
+ allowedRecipients,
1041
+ priorityFee = true
1042
+ } = config;
1043
+ let keypair;
1044
+ if (privateKey.includes(",")) {
1045
+ const bytes = new Uint8Array(privateKey.split(",").map((n) => parseInt(n.trim(), 10)));
1046
+ keypair = web3_js.Keypair.fromSecretKey(bytes);
1047
+ } else {
1048
+ keypair = web3_js.Keypair.fromSecretKey(bs58__default.default.decode(privateKey));
1049
+ }
1050
+ const endpoint = rpcUrl ?? RPC_ENDPOINTS[network];
1051
+ const connection = new web3_js.Connection(endpoint, "confirmed");
1052
+ const fetchConfig = {
1053
+ wallet: keypair,
1054
+ network,
1055
+ connection,
1056
+ maxPaymentPerRequest,
1057
+ allowedRecipients,
1058
+ priorityFee: priorityFee ? { enabled: true, microLamports: 5e3 } : void 0
1059
+ };
1060
+ const x402Fetch = createX402Fetch(fetchConfig);
1061
+ return {
1062
+ get: (url, init) => x402Fetch(url, { ...init, method: "GET" }),
1063
+ post: (url, body, init) => x402Fetch(url, {
1064
+ ...init,
1065
+ method: "POST",
1066
+ body: JSON.stringify(body),
1067
+ headers: {
1068
+ "Content-Type": "application/json",
1069
+ ...init?.headers
1070
+ }
1071
+ }),
1072
+ fetch: (url, init) => x402Fetch(url, init),
1073
+ publicKey: keypair.publicKey.toBase58(),
1074
+ getBalance: async () => {
1075
+ const lamports = await connection.getBalance(keypair.publicKey);
1076
+ return {
1077
+ lamports: BigInt(lamports),
1078
+ sol: lamports / 1e9
1079
+ };
1080
+ }
1081
+ };
1082
+ }
1083
+
634
1084
  // src/pricing/utils.ts
635
1085
  function lamportsToSol(lamports) {
636
1086
  return Number(lamports) / 1e9;
@@ -788,6 +1238,7 @@ exports.addCredits = addCredits;
788
1238
  exports.clearPriceCache = clearPriceCache;
789
1239
  exports.configurePricing = configurePricing;
790
1240
  exports.createCreditSession = createCreditSession;
1241
+ exports.createPayingAgent = createPayingAgent;
791
1242
  exports.executeAgentPayment = executeAgentPayment;
792
1243
  exports.formatPriceDisplay = formatPriceDisplay;
793
1244
  exports.formatPriceSync = formatPriceSync;
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@ import { PaymentPayload, PaymentRequirements, VerifyResponse, SettleResponse, Su
3
3
  export * from '@x402/core/types';
4
4
  export * from '@x402/core/client';
5
5
  export * from '@x402/svm';
6
- export { AgentPaymentResult, CreditSessionClaims, CreditSessionConfig, CreditSessionData, CreditValidation, ExecuteAgentPaymentParams, UseCreditResult, addCredits, createCreditSession, executeAgentPayment, generateAgentKeypair, getAgentBalance, getRemainingCredits, hasAgentSufficientBalance, keypairFromBase58, useCredit, validateCreditSession } from './agent/index.cjs';
6
+ export { AgentPaymentResult, CreditSessionClaims, CreditSessionConfig, CreditSessionData, CreditValidation, ExecuteAgentPaymentParams, PayingAgent, PayingAgentConfig, UseCreditResult, addCredits, createCreditSession, createPayingAgent, executeAgentPayment, generateAgentKeypair, getAgentBalance, getRemainingCredits, hasAgentSufficientBalance, keypairFromBase58, useCredit, validateCreditSession } from './agent/index.cjs';
7
7
  export { CustomPriceProvider, PriceConfig, PriceData, clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToSol, lamportsToUsd, usdToLamports } from './pricing/index.cjs';
8
8
  import '@solana/web3.js';
9
9
  import './types-BfKfgout.cjs';
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { PaymentPayload, PaymentRequirements, VerifyResponse, SettleResponse, Su
3
3
  export * from '@x402/core/types';
4
4
  export * from '@x402/core/client';
5
5
  export * from '@x402/svm';
6
- export { AgentPaymentResult, CreditSessionClaims, CreditSessionConfig, CreditSessionData, CreditValidation, ExecuteAgentPaymentParams, UseCreditResult, addCredits, createCreditSession, executeAgentPayment, generateAgentKeypair, getAgentBalance, getRemainingCredits, hasAgentSufficientBalance, keypairFromBase58, useCredit, validateCreditSession } from './agent/index.js';
6
+ export { AgentPaymentResult, CreditSessionClaims, CreditSessionConfig, CreditSessionData, CreditValidation, ExecuteAgentPaymentParams, PayingAgent, PayingAgentConfig, UseCreditResult, addCredits, createCreditSession, createPayingAgent, executeAgentPayment, generateAgentKeypair, getAgentBalance, getRemainingCredits, hasAgentSufficientBalance, keypairFromBase58, useCredit, validateCreditSession } from './agent/index.js';
7
7
  export { CustomPriceProvider, PriceConfig, PriceData, clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToSol, lamportsToUsd, usdToLamports } from './pricing/index.js';
8
8
  import '@solana/web3.js';
9
9
  import './types-BfKfgout.js';