@alleyboss/micropay-solana-x402-paywall 2.0.0 → 2.0.1

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