@alleyboss/micropay-solana-x402-paywall 2.0.0 → 2.0.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.js CHANGED
@@ -13,8 +13,8 @@ var TOKEN_MINTS = {
13
13
  };
14
14
  var cachedConnection = null;
15
15
  var cachedNetwork = null;
16
- function buildRpcUrl(config) {
17
- const { network, rpcUrl, tatumApiKey } = config;
16
+ function buildRpcUrl(config2) {
17
+ const { network, rpcUrl, tatumApiKey } = config2;
18
18
  if (rpcUrl) {
19
19
  if (rpcUrl.includes("tatum.io") && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {
20
20
  return rpcUrl.endsWith("/") ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;
@@ -27,12 +27,12 @@ function buildRpcUrl(config) {
27
27
  }
28
28
  return clusterApiUrl(network);
29
29
  }
30
- function getConnection(config) {
31
- const { network } = config;
30
+ function getConnection(config2) {
31
+ const { network } = config2;
32
32
  if (cachedConnection && cachedNetwork === network) {
33
33
  return cachedConnection;
34
34
  }
35
- const rpcUrl = buildRpcUrl(config);
35
+ const rpcUrl = buildRpcUrl(config2);
36
36
  cachedConnection = new Connection(rpcUrl, {
37
37
  commitment: "confirmed",
38
38
  confirmTransactionInitialTimeout: 6e4
@@ -418,19 +418,19 @@ function validateArticleId(articleId) {
418
418
  const safeIdRegex = /^[a-zA-Z0-9_-]+$/;
419
419
  return safeIdRegex.test(articleId);
420
420
  }
421
- async function createSession(walletAddress, articleId, config, siteWide = false) {
421
+ async function createSession(walletAddress, articleId, config2, siteWide = false) {
422
422
  if (!validateWalletAddress(walletAddress)) {
423
423
  throw new Error("Invalid wallet address format");
424
424
  }
425
425
  if (!validateArticleId(articleId)) {
426
426
  throw new Error("Invalid article ID format");
427
427
  }
428
- if (!config.durationHours || config.durationHours <= 0 || config.durationHours > 720) {
428
+ if (!config2.durationHours || config2.durationHours <= 0 || config2.durationHours > 720) {
429
429
  throw new Error("Session duration must be between 1 and 720 hours");
430
430
  }
431
431
  const sessionId = v4();
432
432
  const now = Math.floor(Date.now() / 1e3);
433
- const expiresAt = now + config.durationHours * 3600;
433
+ const expiresAt = now + config2.durationHours * 3600;
434
434
  const session = {
435
435
  id: sessionId,
436
436
  walletAddress,
@@ -447,7 +447,7 @@ async function createSession(walletAddress, articleId, config, siteWide = false)
447
447
  iat: now,
448
448
  exp: expiresAt
449
449
  };
450
- const token = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config.durationHours}h`).sign(getSecretKey(config.secret));
450
+ const token = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config2.durationHours}h`).sign(getSecretKey(config2.secret));
451
451
  return { token, session };
452
452
  }
453
453
  async function validateSession(token, secret) {
@@ -804,8 +804,8 @@ function matchesProtectedPath(path, patterns) {
804
804
  }
805
805
  return false;
806
806
  }
807
- async function checkPaywallAccess(path, sessionToken, config) {
808
- if (!matchesProtectedPath(path, config.protectedPaths)) {
807
+ async function checkPaywallAccess(path, sessionToken, config2) {
808
+ if (!matchesProtectedPath(path, config2.protectedPaths)) {
809
809
  return { allowed: true };
810
810
  }
811
811
  if (!sessionToken) {
@@ -815,7 +815,7 @@ async function checkPaywallAccess(path, sessionToken, config) {
815
815
  requiresPayment: true
816
816
  };
817
817
  }
818
- const validation = await validateSession(sessionToken, config.sessionSecret);
818
+ const validation = await validateSession(sessionToken, config2.sessionSecret);
819
819
  if (!validation.valid || !validation.session) {
820
820
  return {
821
821
  allowed: false,
@@ -828,8 +828,8 @@ async function checkPaywallAccess(path, sessionToken, config) {
828
828
  session: validation.session
829
829
  };
830
830
  }
831
- function createPaywallMiddleware(config) {
832
- const { cookieName = "x402_session" } = config;
831
+ function createPaywallMiddleware(config2) {
832
+ const { cookieName = "x402_session" } = config2;
833
833
  return async function middleware(request) {
834
834
  const url = new URL(request.url);
835
835
  const path = url.pathname;
@@ -841,9 +841,9 @@ function createPaywallMiddleware(config) {
841
841
  })
842
842
  );
843
843
  const sessionToken = cookies[cookieName];
844
- const result = await checkPaywallAccess(path, sessionToken, config);
844
+ const result = await checkPaywallAccess(path, sessionToken, config2);
845
845
  if (!result.allowed && result.requiresPayment) {
846
- const body = config.custom402Response ? config.custom402Response(path) : {
846
+ const body = config2.custom402Response ? config2.custom402Response(path) : {
847
847
  error: "Payment Required",
848
848
  message: "This resource requires payment to access",
849
849
  path
@@ -979,8 +979,8 @@ function buildSolanaPayUrl(params) {
979
979
  }
980
980
  return url.toString();
981
981
  }
982
- function createPaymentFlow(config) {
983
- const { network, recipientWallet, amount, asset = "native", memo } = config;
982
+ function createPaymentFlow(config2) {
983
+ const { network, recipientWallet, amount, asset = "native", memo } = config2;
984
984
  let decimals = 9;
985
985
  let mintAddress;
986
986
  if (asset === "usdc") {
@@ -996,7 +996,7 @@ function createPaymentFlow(config) {
996
996
  const naturalAmount = Number(amount) / Math.pow(10, decimals);
997
997
  return {
998
998
  /** Get the payment configuration */
999
- getConfig: () => ({ ...config }),
999
+ getConfig: () => ({ ...config2 }),
1000
1000
  /** Get amount in natural display units (e.g., 0.01 SOL) */
1001
1001
  getDisplayAmount: () => naturalAmount,
1002
1002
  /** Get amount formatted with symbol */
@@ -1042,43 +1042,100 @@ function createPaymentReference() {
1042
1042
 
1043
1043
  // src/pricing/index.ts
1044
1044
  var cachedPrice = null;
1045
- var CACHE_TTL_MS = 6e4;
1046
- async function getSolPrice() {
1047
- if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {
1048
- return cachedPrice;
1049
- }
1045
+ var config = {};
1046
+ var lastProviderIndex = -1;
1047
+ function configurePricing(newConfig) {
1048
+ config = { ...config, ...newConfig };
1049
+ cachedPrice = null;
1050
+ }
1051
+ var PROVIDERS = [
1052
+ {
1053
+ name: "coincap",
1054
+ url: "https://api.coincap.io/v2/assets/solana",
1055
+ parse: (data) => parseFloat(data.data?.priceUsd || "0")
1056
+ },
1057
+ {
1058
+ name: "binance",
1059
+ url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
1060
+ parse: (data) => parseFloat(data.price || "0")
1061
+ },
1062
+ {
1063
+ name: "coingecko",
1064
+ url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
1065
+ parse: (data) => data.solana?.usd || 0
1066
+ },
1067
+ {
1068
+ name: "kraken",
1069
+ url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
1070
+ parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
1071
+ }
1072
+ ];
1073
+ async function fetchFromProvider(provider, timeout) {
1074
+ const controller = new AbortController();
1075
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1050
1076
  try {
1051
- const response = await fetch(
1052
- "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
1053
- {
1054
- headers: { "Accept": "application/json" },
1055
- signal: AbortSignal.timeout(5e3)
1056
- }
1057
- );
1077
+ const response = await fetch(provider.url, {
1078
+ headers: { "Accept": "application/json" },
1079
+ signal: controller.signal
1080
+ });
1058
1081
  if (!response.ok) {
1059
- throw new Error(`Price fetch failed: ${response.status}`);
1082
+ throw new Error(`HTTP ${response.status}`);
1060
1083
  }
1061
1084
  const data = await response.json();
1062
- if (!data.solana?.usd) {
1063
- throw new Error("Invalid price response");
1085
+ const price = provider.parse(data);
1086
+ if (!price || price <= 0) {
1087
+ throw new Error("Invalid price");
1064
1088
  }
1065
- cachedPrice = {
1066
- solPrice: data.solana.usd,
1067
- fetchedAt: /* @__PURE__ */ new Date(),
1068
- source: "coingecko"
1069
- };
1089
+ return price;
1090
+ } finally {
1091
+ clearTimeout(timeoutId);
1092
+ }
1093
+ }
1094
+ async function getSolPrice() {
1095
+ const cacheTTL = config.cacheTTL ?? 6e4;
1096
+ const timeout = config.timeout ?? 5e3;
1097
+ if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
1070
1098
  return cachedPrice;
1071
- } catch (error) {
1072
- if (cachedPrice) {
1099
+ }
1100
+ if (config.customProvider) {
1101
+ try {
1102
+ const price = await config.customProvider();
1103
+ if (price > 0) {
1104
+ cachedPrice = {
1105
+ solPrice: price,
1106
+ fetchedAt: /* @__PURE__ */ new Date(),
1107
+ source: "custom"
1108
+ };
1109
+ return cachedPrice;
1110
+ }
1111
+ } catch {
1112
+ }
1113
+ }
1114
+ for (let i = 0; i < PROVIDERS.length; i++) {
1115
+ const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
1116
+ const provider = PROVIDERS[idx];
1117
+ try {
1118
+ const price = await fetchFromProvider(provider, timeout);
1119
+ lastProviderIndex = idx;
1120
+ cachedPrice = {
1121
+ solPrice: price,
1122
+ fetchedAt: /* @__PURE__ */ new Date(),
1123
+ source: provider.name
1124
+ };
1073
1125
  return cachedPrice;
1126
+ } catch {
1127
+ continue;
1074
1128
  }
1075
- return {
1076
- solPrice: 150,
1077
- // Fallback price
1078
- fetchedAt: /* @__PURE__ */ new Date(),
1079
- source: "fallback"
1080
- };
1081
1129
  }
1130
+ if (cachedPrice) {
1131
+ return cachedPrice;
1132
+ }
1133
+ return {
1134
+ solPrice: 150,
1135
+ // Reasonable fallback
1136
+ fetchedAt: /* @__PURE__ */ new Date(),
1137
+ source: "fallback"
1138
+ };
1082
1139
  }
1083
1140
  async function lamportsToUsd(lamports) {
1084
1141
  const { solPrice } = await getSolPrice();
@@ -1107,8 +1164,12 @@ function formatPriceSync(lamports, solPrice) {
1107
1164
  }
1108
1165
  function clearPriceCache() {
1109
1166
  cachedPrice = null;
1167
+ lastProviderIndex = -1;
1168
+ }
1169
+ function getProviders() {
1170
+ return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
1110
1171
  }
1111
1172
 
1112
- export { TOKEN_MINTS, X402_HEADERS, addArticleToSession, buildPaymentRequirement, buildSolanaPayUrl, checkPaywallAccess, clearPriceCache, create402Headers, create402ResponseBody, createMemoryStore, createPaymentFlow, createPaymentReference, createPaywallMiddleware, createRedisStore, createSession, decodePaymentRequired, encodePaymentRequired, encodePaymentResponse, formatPriceDisplay, formatPriceSync, getConnection, getSolPrice, getTokenDecimals, getWalletTransactions, isArticleUnlocked, isMainnet, isNativeAsset, isRetryableRPCError, lamportsToSol, lamportsToUsd, parsePaymentHeader, resetConnection, resolveMintAddress, solToLamports, toX402Network, usdToLamports, validateSession, verifyPayment, verifySPLPayment, verifyX402Payment, waitForConfirmation, withPaywall, withRetry };
1173
+ export { TOKEN_MINTS, X402_HEADERS, addArticleToSession, buildPaymentRequirement, buildSolanaPayUrl, checkPaywallAccess, clearPriceCache, configurePricing, create402Headers, create402ResponseBody, createMemoryStore, createPaymentFlow, createPaymentReference, createPaywallMiddleware, createRedisStore, createSession, decodePaymentRequired, encodePaymentRequired, encodePaymentResponse, formatPriceDisplay, formatPriceSync, getConnection, getProviders, getSolPrice, getTokenDecimals, getWalletTransactions, isArticleUnlocked, isMainnet, isNativeAsset, isRetryableRPCError, lamportsToSol, lamportsToUsd, parsePaymentHeader, resetConnection, resolveMintAddress, solToLamports, toX402Network, usdToLamports, validateSession, verifyPayment, verifySPLPayment, verifyX402Payment, waitForConfirmation, withPaywall, withRetry };
1113
1174
  //# sourceMappingURL=index.js.map
1114
1175
  //# sourceMappingURL=index.js.map