@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.cjs
CHANGED
|
@@ -15,8 +15,8 @@ var TOKEN_MINTS = {
|
|
|
15
15
|
};
|
|
16
16
|
var cachedConnection = null;
|
|
17
17
|
var cachedNetwork = null;
|
|
18
|
-
function buildRpcUrl(
|
|
19
|
-
const { network, rpcUrl, tatumApiKey } =
|
|
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(
|
|
33
|
-
const { network } =
|
|
32
|
+
function getConnection(config2) {
|
|
33
|
+
const { network } = config2;
|
|
34
34
|
if (cachedConnection && cachedNetwork === network) {
|
|
35
35
|
return cachedConnection;
|
|
36
36
|
}
|
|
37
|
-
const rpcUrl = buildRpcUrl(
|
|
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,
|
|
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 (!
|
|
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 +
|
|
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(`${
|
|
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,
|
|
810
|
-
if (!matchesProtectedPath(path,
|
|
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,
|
|
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(
|
|
834
|
-
const { cookieName = "x402_session" } =
|
|
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,
|
|
846
|
+
const result = await checkPaywallAccess(path, sessionToken, config2);
|
|
847
847
|
if (!result.allowed && result.requiresPayment) {
|
|
848
|
-
const body =
|
|
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(
|
|
985
|
-
const { network, recipientWallet, amount, asset = "native", memo } =
|
|
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: () => ({ ...
|
|
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
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
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
|
-
"
|
|
1055
|
-
|
|
1056
|
-
|
|
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(`
|
|
1084
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1062
1085
|
}
|
|
1063
1086
|
const data = await response.json();
|
|
1064
|
-
|
|
1065
|
-
|
|
1087
|
+
const price = provider.parse(data);
|
|
1088
|
+
if (!price || price <= 0) {
|
|
1089
|
+
throw new Error("Invalid price");
|
|
1066
1090
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
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
|
-
}
|
|
1074
|
-
|
|
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;
|