@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 +110 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +109 -48
- package/dist/index.js.map +1 -1
- package/dist/pricing/index.cjs +90 -27
- package/dist/pricing/index.cjs.map +1 -1
- package/dist/pricing/index.d.cts +53 -9
- package/dist/pricing/index.d.ts +53 -9
- package/dist/pricing/index.js +89 -28
- package/dist/pricing/index.js.map +1 -1
- package/package.json +1 -1
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(
|
|
17
|
-
const { network, rpcUrl, tatumApiKey } =
|
|
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(
|
|
31
|
-
const { network } =
|
|
30
|
+
function getConnection(config2) {
|
|
31
|
+
const { network } = config2;
|
|
32
32
|
if (cachedConnection && cachedNetwork === network) {
|
|
33
33
|
return cachedConnection;
|
|
34
34
|
}
|
|
35
|
-
const rpcUrl = buildRpcUrl(
|
|
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,
|
|
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 (!
|
|
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 +
|
|
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(`${
|
|
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,
|
|
808
|
-
if (!matchesProtectedPath(path,
|
|
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,
|
|
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(
|
|
832
|
-
const { cookieName = "x402_session" } =
|
|
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,
|
|
844
|
+
const result = await checkPaywallAccess(path, sessionToken, config2);
|
|
845
845
|
if (!result.allowed && result.requiresPayment) {
|
|
846
|
-
const body =
|
|
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(
|
|
983
|
-
const { network, recipientWallet, amount, asset = "native", memo } =
|
|
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: () => ({ ...
|
|
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
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
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
|
-
"
|
|
1053
|
-
|
|
1054
|
-
|
|
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(`
|
|
1082
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1060
1083
|
}
|
|
1061
1084
|
const data = await response.json();
|
|
1062
|
-
|
|
1063
|
-
|
|
1085
|
+
const price = provider.parse(data);
|
|
1086
|
+
if (!price || price <= 0) {
|
|
1087
|
+
throw new Error("Invalid price");
|
|
1064
1088
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
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
|
-
}
|
|
1072
|
-
|
|
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
|