@alleyboss/micropay-solana-x402-paywall 1.0.1 → 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/README.md +100 -167
- package/dist/client/index.cjs +99 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +112 -0
- package/dist/client/index.d.ts +112 -0
- package/dist/client/index.js +95 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client-CSZHI8o8.d.ts +32 -0
- package/dist/client-vRr48m2x.d.cts +32 -0
- package/dist/index.cjs +696 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +674 -16
- package/dist/index.js.map +1 -1
- package/dist/memory-Daxkczti.d.cts +29 -0
- package/dist/memory-Daxkczti.d.ts +29 -0
- package/dist/middleware/index.cjs +261 -0
- package/dist/middleware/index.cjs.map +1 -0
- package/dist/middleware/index.d.cts +90 -0
- package/dist/middleware/index.d.ts +90 -0
- package/dist/middleware/index.js +255 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/nextjs-BK0pVb9Y.d.ts +78 -0
- package/dist/nextjs-Bm272Jkj.d.cts +78 -0
- package/dist/{client-kfCr7G-P.d.cts → payment-CTxdtqmc.d.cts} +23 -34
- package/dist/{client-kfCr7G-P.d.ts → payment-CTxdtqmc.d.ts} +23 -34
- package/dist/pricing/index.cjs +142 -0
- package/dist/pricing/index.cjs.map +1 -0
- package/dist/pricing/index.d.cts +111 -0
- package/dist/pricing/index.d.ts +111 -0
- package/dist/pricing/index.js +133 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/session/index.d.cts +29 -1
- package/dist/session/index.d.ts +29 -1
- package/dist/{index-uxMb72hH.d.cts → session-D2IoWAWV.d.cts} +1 -27
- package/dist/{index-uxMb72hH.d.ts → session-D2IoWAWV.d.ts} +1 -27
- package/dist/solana/index.cjs +193 -0
- package/dist/solana/index.cjs.map +1 -1
- package/dist/solana/index.d.cts +60 -3
- package/dist/solana/index.d.ts +60 -3
- package/dist/solana/index.js +190 -1
- package/dist/solana/index.js.map +1 -1
- package/dist/store/index.cjs +99 -0
- package/dist/store/index.cjs.map +1 -0
- package/dist/store/index.d.cts +38 -0
- package/dist/store/index.d.ts +38 -0
- package/dist/store/index.js +96 -0
- package/dist/store/index.js.map +1 -0
- package/dist/utils/index.cjs +68 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +30 -0
- package/dist/utils/index.d.ts +30 -0
- package/dist/utils/index.js +65 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/x402/index.cjs +2 -1
- package/dist/x402/index.cjs.map +1 -1
- package/dist/x402/index.d.cts +2 -1
- package/dist/x402/index.d.ts +2 -1
- package/dist/x402/index.js +2 -1
- package/dist/x402/index.js.map +1 -1
- package/package.json +56 -3
package/dist/index.cjs
CHANGED
|
@@ -4,11 +4,19 @@ var web3_js = require('@solana/web3.js');
|
|
|
4
4
|
var jose = require('jose');
|
|
5
5
|
var uuid = require('uuid');
|
|
6
6
|
|
|
7
|
-
// src/
|
|
7
|
+
// src/types/payment.ts
|
|
8
|
+
var TOKEN_MINTS = {
|
|
9
|
+
/** USDC on mainnet */
|
|
10
|
+
USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
11
|
+
/** USDC on devnet */
|
|
12
|
+
USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
|
|
13
|
+
/** USDT on mainnet */
|
|
14
|
+
USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
|
15
|
+
};
|
|
8
16
|
var cachedConnection = null;
|
|
9
17
|
var cachedNetwork = null;
|
|
10
|
-
function buildRpcUrl(
|
|
11
|
-
const { network, rpcUrl, tatumApiKey } =
|
|
18
|
+
function buildRpcUrl(config2) {
|
|
19
|
+
const { network, rpcUrl, tatumApiKey } = config2;
|
|
12
20
|
if (rpcUrl) {
|
|
13
21
|
if (rpcUrl.includes("tatum.io") && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {
|
|
14
22
|
return rpcUrl.endsWith("/") ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;
|
|
@@ -21,12 +29,12 @@ function buildRpcUrl(config) {
|
|
|
21
29
|
}
|
|
22
30
|
return web3_js.clusterApiUrl(network);
|
|
23
31
|
}
|
|
24
|
-
function getConnection(
|
|
25
|
-
const { network } =
|
|
32
|
+
function getConnection(config2) {
|
|
33
|
+
const { network } = config2;
|
|
26
34
|
if (cachedConnection && cachedNetwork === network) {
|
|
27
35
|
return cachedConnection;
|
|
28
36
|
}
|
|
29
|
-
const rpcUrl = buildRpcUrl(
|
|
37
|
+
const rpcUrl = buildRpcUrl(config2);
|
|
30
38
|
cachedConnection = new web3_js.Connection(rpcUrl, {
|
|
31
39
|
commitment: "confirmed",
|
|
32
40
|
confirmTransactionInitialTimeout: 6e4
|
|
@@ -211,6 +219,185 @@ function solToLamports(sol) {
|
|
|
211
219
|
}
|
|
212
220
|
return BigInt(Math.floor(sol * web3_js.LAMPORTS_PER_SOL));
|
|
213
221
|
}
|
|
222
|
+
|
|
223
|
+
// src/solana/spl.ts
|
|
224
|
+
var SIGNATURE_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
|
|
225
|
+
var WALLET_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
226
|
+
function resolveMintAddress(asset, network) {
|
|
227
|
+
if (asset === "native") return null;
|
|
228
|
+
if (asset === "usdc") {
|
|
229
|
+
return network === "mainnet-beta" ? TOKEN_MINTS.USDC_MAINNET : TOKEN_MINTS.USDC_DEVNET;
|
|
230
|
+
}
|
|
231
|
+
if (asset === "usdt") {
|
|
232
|
+
return TOKEN_MINTS.USDT_MAINNET;
|
|
233
|
+
}
|
|
234
|
+
if (typeof asset === "object" && "mint" in asset) {
|
|
235
|
+
return asset.mint;
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
function getTokenDecimals(asset) {
|
|
240
|
+
if (asset === "native") return 9;
|
|
241
|
+
if (asset === "usdc" || asset === "usdt") return 6;
|
|
242
|
+
if (typeof asset === "object" && "decimals" in asset) {
|
|
243
|
+
return asset.decimals ?? 6;
|
|
244
|
+
}
|
|
245
|
+
return 6;
|
|
246
|
+
}
|
|
247
|
+
function parseSPLTransfer(transaction, expectedRecipient, expectedMint) {
|
|
248
|
+
const instructions = transaction.transaction.message.instructions;
|
|
249
|
+
for (const ix of instructions) {
|
|
250
|
+
if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
251
|
+
const parsed = ix.parsed;
|
|
252
|
+
if (parsed.type === "transfer" || parsed.type === "transferChecked") {
|
|
253
|
+
const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
|
|
254
|
+
if (amount && parsed.info.destination) {
|
|
255
|
+
return {
|
|
256
|
+
from: parsed.info.authority || parsed.info.source || "",
|
|
257
|
+
to: parsed.info.destination,
|
|
258
|
+
amount: BigInt(amount),
|
|
259
|
+
mint: parsed.info.mint || expectedMint
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (transaction.meta?.innerInstructions) {
|
|
266
|
+
for (const inner of transaction.meta.innerInstructions) {
|
|
267
|
+
for (const ix of inner.instructions) {
|
|
268
|
+
if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
|
|
269
|
+
const parsed = ix.parsed;
|
|
270
|
+
if (parsed.type === "transfer" || parsed.type === "transferChecked") {
|
|
271
|
+
const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
|
|
272
|
+
if (amount) {
|
|
273
|
+
return {
|
|
274
|
+
from: parsed.info.authority || parsed.info.source || "",
|
|
275
|
+
to: parsed.info.destination || "",
|
|
276
|
+
amount: BigInt(amount),
|
|
277
|
+
mint: parsed.info.mint || expectedMint
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (transaction.meta?.postTokenBalances && transaction.meta?.preTokenBalances) {
|
|
286
|
+
const preBalances = transaction.meta.preTokenBalances;
|
|
287
|
+
const postBalances = transaction.meta.postTokenBalances;
|
|
288
|
+
for (const post of postBalances) {
|
|
289
|
+
if (post.mint === expectedMint && post.owner === expectedRecipient) {
|
|
290
|
+
const pre = preBalances.find(
|
|
291
|
+
(p) => p.accountIndex === post.accountIndex
|
|
292
|
+
);
|
|
293
|
+
const preAmount = BigInt(pre?.uiTokenAmount?.amount || "0");
|
|
294
|
+
const postAmount = BigInt(post.uiTokenAmount?.amount || "0");
|
|
295
|
+
const transferred = postAmount - preAmount;
|
|
296
|
+
if (transferred > 0n) {
|
|
297
|
+
return {
|
|
298
|
+
from: "",
|
|
299
|
+
// Can't determine from balance changes
|
|
300
|
+
to: expectedRecipient,
|
|
301
|
+
amount: transferred,
|
|
302
|
+
mint: expectedMint
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
async function verifySPLPayment(params) {
|
|
311
|
+
const {
|
|
312
|
+
signature,
|
|
313
|
+
expectedRecipient,
|
|
314
|
+
expectedAmount,
|
|
315
|
+
asset,
|
|
316
|
+
clientConfig,
|
|
317
|
+
maxAgeSeconds = 300
|
|
318
|
+
} = params;
|
|
319
|
+
if (!SIGNATURE_REGEX2.test(signature)) {
|
|
320
|
+
return { valid: false, confirmed: false, signature, error: "Invalid signature format" };
|
|
321
|
+
}
|
|
322
|
+
if (!WALLET_REGEX2.test(expectedRecipient)) {
|
|
323
|
+
return { valid: false, confirmed: false, signature, error: "Invalid recipient address" };
|
|
324
|
+
}
|
|
325
|
+
const mintAddress = resolveMintAddress(asset, clientConfig.network);
|
|
326
|
+
if (!mintAddress) {
|
|
327
|
+
return { valid: false, confirmed: false, signature, error: "Invalid asset configuration" };
|
|
328
|
+
}
|
|
329
|
+
if (expectedAmount <= 0n) {
|
|
330
|
+
return { valid: false, confirmed: false, signature, error: "Invalid expected amount" };
|
|
331
|
+
}
|
|
332
|
+
const effectiveMaxAge = Math.min(Math.max(maxAgeSeconds, 60), 3600);
|
|
333
|
+
const connection = getConnection(clientConfig);
|
|
334
|
+
try {
|
|
335
|
+
const transaction = await connection.getParsedTransaction(signature, {
|
|
336
|
+
commitment: "confirmed",
|
|
337
|
+
maxSupportedTransactionVersion: 0
|
|
338
|
+
});
|
|
339
|
+
if (!transaction) {
|
|
340
|
+
return { valid: false, confirmed: false, signature, error: "Transaction not found" };
|
|
341
|
+
}
|
|
342
|
+
if (transaction.meta?.err) {
|
|
343
|
+
return { valid: false, confirmed: true, signature, error: "Transaction failed on-chain" };
|
|
344
|
+
}
|
|
345
|
+
if (transaction.blockTime) {
|
|
346
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
347
|
+
if (now - transaction.blockTime > effectiveMaxAge) {
|
|
348
|
+
return { valid: false, confirmed: true, signature, error: "Transaction too old" };
|
|
349
|
+
}
|
|
350
|
+
if (transaction.blockTime > now + 60) {
|
|
351
|
+
return { valid: false, confirmed: true, signature, error: "Invalid transaction time" };
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const transfer = parseSPLTransfer(transaction, expectedRecipient, mintAddress);
|
|
355
|
+
if (!transfer) {
|
|
356
|
+
return {
|
|
357
|
+
valid: false,
|
|
358
|
+
confirmed: true,
|
|
359
|
+
signature,
|
|
360
|
+
error: "No valid token transfer to recipient found"
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
if (transfer.mint !== mintAddress) {
|
|
364
|
+
return {
|
|
365
|
+
valid: false,
|
|
366
|
+
confirmed: true,
|
|
367
|
+
signature,
|
|
368
|
+
error: "Token mint mismatch"
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
if (transfer.amount < expectedAmount) {
|
|
372
|
+
return {
|
|
373
|
+
valid: false,
|
|
374
|
+
confirmed: true,
|
|
375
|
+
signature,
|
|
376
|
+
from: transfer.from,
|
|
377
|
+
to: transfer.to,
|
|
378
|
+
mint: transfer.mint,
|
|
379
|
+
amount: transfer.amount,
|
|
380
|
+
error: "Insufficient payment amount"
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
valid: true,
|
|
385
|
+
confirmed: true,
|
|
386
|
+
signature,
|
|
387
|
+
from: transfer.from,
|
|
388
|
+
to: transfer.to,
|
|
389
|
+
mint: transfer.mint,
|
|
390
|
+
amount: transfer.amount,
|
|
391
|
+
blockTime: transaction.blockTime ?? void 0,
|
|
392
|
+
slot: transaction.slot
|
|
393
|
+
};
|
|
394
|
+
} catch {
|
|
395
|
+
return { valid: false, confirmed: false, signature, error: "Verification failed" };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function isNativeAsset(asset) {
|
|
399
|
+
return asset === "native";
|
|
400
|
+
}
|
|
214
401
|
var MAX_ARTICLES_PER_SESSION = 100;
|
|
215
402
|
var MIN_SECRET_LENGTH = 32;
|
|
216
403
|
function getSecretKey(secret) {
|
|
@@ -233,19 +420,19 @@ function validateArticleId(articleId) {
|
|
|
233
420
|
const safeIdRegex = /^[a-zA-Z0-9_-]+$/;
|
|
234
421
|
return safeIdRegex.test(articleId);
|
|
235
422
|
}
|
|
236
|
-
async function createSession(walletAddress, articleId,
|
|
423
|
+
async function createSession(walletAddress, articleId, config2, siteWide = false) {
|
|
237
424
|
if (!validateWalletAddress(walletAddress)) {
|
|
238
425
|
throw new Error("Invalid wallet address format");
|
|
239
426
|
}
|
|
240
427
|
if (!validateArticleId(articleId)) {
|
|
241
428
|
throw new Error("Invalid article ID format");
|
|
242
429
|
}
|
|
243
|
-
if (!
|
|
430
|
+
if (!config2.durationHours || config2.durationHours <= 0 || config2.durationHours > 720) {
|
|
244
431
|
throw new Error("Session duration must be between 1 and 720 hours");
|
|
245
432
|
}
|
|
246
433
|
const sessionId = uuid.v4();
|
|
247
434
|
const now = Math.floor(Date.now() / 1e3);
|
|
248
|
-
const expiresAt = now +
|
|
435
|
+
const expiresAt = now + config2.durationHours * 3600;
|
|
249
436
|
const session = {
|
|
250
437
|
id: sessionId,
|
|
251
438
|
walletAddress,
|
|
@@ -262,7 +449,7 @@ async function createSession(walletAddress, articleId, config, siteWide = false)
|
|
|
262
449
|
iat: now,
|
|
263
450
|
exp: expiresAt
|
|
264
451
|
};
|
|
265
|
-
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));
|
|
266
453
|
return { token, session };
|
|
267
454
|
}
|
|
268
455
|
async function validateSession(token, secret) {
|
|
@@ -340,7 +527,7 @@ async function isArticleUnlocked(token, articleId, secret) {
|
|
|
340
527
|
}
|
|
341
528
|
|
|
342
529
|
// src/x402/config.ts
|
|
343
|
-
var
|
|
530
|
+
var WALLET_REGEX3 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
344
531
|
function sanitizeDisplayString(str, maxLength = 200) {
|
|
345
532
|
if (!str || typeof str !== "string") return "";
|
|
346
533
|
return str.slice(0, maxLength).replace(/[<>"'&]/g, "");
|
|
@@ -354,7 +541,7 @@ function isValidUrl(url) {
|
|
|
354
541
|
}
|
|
355
542
|
}
|
|
356
543
|
function buildPaymentRequirement(params) {
|
|
357
|
-
if (!
|
|
544
|
+
if (!WALLET_REGEX3.test(params.creatorWallet)) {
|
|
358
545
|
throw new Error("Invalid creator wallet address");
|
|
359
546
|
}
|
|
360
547
|
if (params.priceInLamports <= 0n) {
|
|
@@ -412,12 +599,13 @@ var X402_HEADERS = {
|
|
|
412
599
|
PAYMENT_RESPONSE: "X-Payment-Response"
|
|
413
600
|
};
|
|
414
601
|
function create402ResponseBody(requirement) {
|
|
602
|
+
const assetStr = typeof requirement.asset === "string" ? requirement.asset : requirement.asset.mint;
|
|
415
603
|
return {
|
|
416
604
|
error: "Payment Required",
|
|
417
605
|
message: requirement.description,
|
|
418
606
|
price: {
|
|
419
607
|
amount: requirement.maxAmountRequired,
|
|
420
|
-
asset:
|
|
608
|
+
asset: assetStr,
|
|
421
609
|
network: requirement.network
|
|
422
610
|
}
|
|
423
611
|
};
|
|
@@ -432,7 +620,7 @@ function create402Headers(requirement) {
|
|
|
432
620
|
}
|
|
433
621
|
|
|
434
622
|
// src/x402/verification.ts
|
|
435
|
-
var
|
|
623
|
+
var SIGNATURE_REGEX3 = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
|
|
436
624
|
async function verifyX402Payment(payload, requirement, clientConfig) {
|
|
437
625
|
if (!payload || typeof payload !== "object") {
|
|
438
626
|
return { valid: false, invalidReason: "Invalid payload" };
|
|
@@ -441,7 +629,7 @@ async function verifyX402Payment(payload, requirement, clientConfig) {
|
|
|
441
629
|
if (!signature || typeof signature !== "string") {
|
|
442
630
|
return { valid: false, invalidReason: "Missing transaction signature" };
|
|
443
631
|
}
|
|
444
|
-
if (!
|
|
632
|
+
if (!SIGNATURE_REGEX3.test(signature)) {
|
|
445
633
|
return { valid: false, invalidReason: "Invalid signature format" };
|
|
446
634
|
}
|
|
447
635
|
if (payload.x402Version !== 1) {
|
|
@@ -514,27 +702,520 @@ function encodePaymentResponse(response) {
|
|
|
514
702
|
return Buffer.from(JSON.stringify(response)).toString("base64");
|
|
515
703
|
}
|
|
516
704
|
|
|
705
|
+
// src/store/memory.ts
|
|
706
|
+
function createMemoryStore(options = {}) {
|
|
707
|
+
const { cleanupInterval = 6e4 } = options;
|
|
708
|
+
const store = /* @__PURE__ */ new Map();
|
|
709
|
+
const cleanupTimer = setInterval(() => {
|
|
710
|
+
const now = Date.now();
|
|
711
|
+
for (const [key, record] of store.entries()) {
|
|
712
|
+
if (record.expiresAt < now) {
|
|
713
|
+
store.delete(key);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}, cleanupInterval);
|
|
717
|
+
return {
|
|
718
|
+
async hasBeenUsed(signature) {
|
|
719
|
+
const record = store.get(signature);
|
|
720
|
+
if (!record) return false;
|
|
721
|
+
if (record.expiresAt < Date.now()) {
|
|
722
|
+
store.delete(signature);
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
return true;
|
|
726
|
+
},
|
|
727
|
+
async markAsUsed(signature, resourceId, expiresAt) {
|
|
728
|
+
store.set(signature, {
|
|
729
|
+
resourceId,
|
|
730
|
+
usedAt: Date.now(),
|
|
731
|
+
expiresAt: expiresAt.getTime()
|
|
732
|
+
});
|
|
733
|
+
},
|
|
734
|
+
async getUsage(signature) {
|
|
735
|
+
const record = store.get(signature);
|
|
736
|
+
if (!record) return null;
|
|
737
|
+
if (record.expiresAt < Date.now()) {
|
|
738
|
+
store.delete(signature);
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
signature,
|
|
743
|
+
resourceId: record.resourceId,
|
|
744
|
+
usedAt: new Date(record.usedAt),
|
|
745
|
+
expiresAt: new Date(record.expiresAt),
|
|
746
|
+
walletAddress: record.walletAddress
|
|
747
|
+
};
|
|
748
|
+
},
|
|
749
|
+
/** Stop cleanup timer (for graceful shutdown) */
|
|
750
|
+
close() {
|
|
751
|
+
clearInterval(cleanupTimer);
|
|
752
|
+
store.clear();
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/store/redis.ts
|
|
758
|
+
function createRedisStore(options) {
|
|
759
|
+
const { client, keyPrefix = "micropay:sig:" } = options;
|
|
760
|
+
const buildKey = (signature) => `${keyPrefix}${signature}`;
|
|
761
|
+
return {
|
|
762
|
+
async hasBeenUsed(signature) {
|
|
763
|
+
const exists = await client.exists(buildKey(signature));
|
|
764
|
+
return exists > 0;
|
|
765
|
+
},
|
|
766
|
+
async markAsUsed(signature, resourceId, expiresAt) {
|
|
767
|
+
const key = buildKey(signature);
|
|
768
|
+
const ttl = Math.max(1, Math.floor((expiresAt.getTime() - Date.now()) / 1e3));
|
|
769
|
+
const record = {
|
|
770
|
+
signature,
|
|
771
|
+
resourceId,
|
|
772
|
+
usedAt: /* @__PURE__ */ new Date(),
|
|
773
|
+
expiresAt
|
|
774
|
+
};
|
|
775
|
+
if (client.setex) {
|
|
776
|
+
await client.setex(key, ttl, JSON.stringify(record));
|
|
777
|
+
} else {
|
|
778
|
+
await client.set(key, JSON.stringify(record), { EX: ttl });
|
|
779
|
+
}
|
|
780
|
+
},
|
|
781
|
+
async getUsage(signature) {
|
|
782
|
+
const data = await client.get(buildKey(signature));
|
|
783
|
+
if (!data) return null;
|
|
784
|
+
try {
|
|
785
|
+
const record = JSON.parse(data);
|
|
786
|
+
return {
|
|
787
|
+
...record,
|
|
788
|
+
usedAt: new Date(record.usedAt),
|
|
789
|
+
expiresAt: new Date(record.expiresAt)
|
|
790
|
+
};
|
|
791
|
+
} catch {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/middleware/nextjs.ts
|
|
799
|
+
function matchesProtectedPath(path, patterns) {
|
|
800
|
+
for (const pattern of patterns) {
|
|
801
|
+
const regexPattern = pattern.replace(/\*\*/g, "{{DOUBLE_STAR}}").replace(/\*/g, "[^/]*").replace(/{{DOUBLE_STAR}}/g, ".*");
|
|
802
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
803
|
+
if (regex.test(path)) {
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
async function checkPaywallAccess(path, sessionToken, config2) {
|
|
810
|
+
if (!matchesProtectedPath(path, config2.protectedPaths)) {
|
|
811
|
+
return { allowed: true };
|
|
812
|
+
}
|
|
813
|
+
if (!sessionToken) {
|
|
814
|
+
return {
|
|
815
|
+
allowed: false,
|
|
816
|
+
reason: "No session token",
|
|
817
|
+
requiresPayment: true
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
const validation = await validateSession(sessionToken, config2.sessionSecret);
|
|
821
|
+
if (!validation.valid || !validation.session) {
|
|
822
|
+
return {
|
|
823
|
+
allowed: false,
|
|
824
|
+
reason: validation.reason || "Invalid session",
|
|
825
|
+
requiresPayment: true
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
return {
|
|
829
|
+
allowed: true,
|
|
830
|
+
session: validation.session
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function createPaywallMiddleware(config2) {
|
|
834
|
+
const { cookieName = "x402_session" } = config2;
|
|
835
|
+
return async function middleware(request) {
|
|
836
|
+
const url = new URL(request.url);
|
|
837
|
+
const path = url.pathname;
|
|
838
|
+
const cookieHeader = request.headers.get("cookie") || "";
|
|
839
|
+
const cookies = Object.fromEntries(
|
|
840
|
+
cookieHeader.split(";").map((c) => {
|
|
841
|
+
const [key, ...vals] = c.trim().split("=");
|
|
842
|
+
return [key, vals.join("=")];
|
|
843
|
+
})
|
|
844
|
+
);
|
|
845
|
+
const sessionToken = cookies[cookieName];
|
|
846
|
+
const result = await checkPaywallAccess(path, sessionToken, config2);
|
|
847
|
+
if (!result.allowed && result.requiresPayment) {
|
|
848
|
+
const body = config2.custom402Response ? config2.custom402Response(path) : {
|
|
849
|
+
error: "Payment Required",
|
|
850
|
+
message: "This resource requires payment to access",
|
|
851
|
+
path
|
|
852
|
+
};
|
|
853
|
+
return new Response(JSON.stringify(body), {
|
|
854
|
+
status: 402,
|
|
855
|
+
headers: {
|
|
856
|
+
"Content-Type": "application/json"
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
return null;
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function withPaywall(handler, options) {
|
|
864
|
+
const { sessionSecret, cookieName = "x402_session", articleId } = options;
|
|
865
|
+
return async function protectedHandler(request) {
|
|
866
|
+
const cookieHeader = request.headers.get("cookie") || "";
|
|
867
|
+
const cookies = Object.fromEntries(
|
|
868
|
+
cookieHeader.split(";").map((c) => {
|
|
869
|
+
const [key, ...vals] = c.trim().split("=");
|
|
870
|
+
return [key, vals.join("=")];
|
|
871
|
+
})
|
|
872
|
+
);
|
|
873
|
+
const sessionToken = cookies[cookieName];
|
|
874
|
+
if (!sessionToken) {
|
|
875
|
+
return new Response(
|
|
876
|
+
JSON.stringify({ error: "Payment Required", message: "No session token" }),
|
|
877
|
+
{ status: 402, headers: { "Content-Type": "application/json" } }
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
const validation = await validateSession(sessionToken, sessionSecret);
|
|
881
|
+
if (!validation.valid || !validation.session) {
|
|
882
|
+
return new Response(
|
|
883
|
+
JSON.stringify({ error: "Payment Required", message: validation.reason }),
|
|
884
|
+
{ status: 402, headers: { "Content-Type": "application/json" } }
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
if (articleId) {
|
|
888
|
+
const { session } = validation;
|
|
889
|
+
const hasAccess = session.siteWideUnlock || session.unlockedArticles.includes(articleId);
|
|
890
|
+
if (!hasAccess) {
|
|
891
|
+
return new Response(
|
|
892
|
+
JSON.stringify({ error: "Payment Required", message: "Article not unlocked" }),
|
|
893
|
+
{ status: 402, headers: { "Content-Type": "application/json" } }
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return handler(request, validation.session);
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// src/utils/retry.ts
|
|
902
|
+
function sleep(ms) {
|
|
903
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
904
|
+
}
|
|
905
|
+
function calculateDelay(attempt, options) {
|
|
906
|
+
const { baseDelay, maxDelay, jitter } = options;
|
|
907
|
+
let delay = baseDelay * Math.pow(2, attempt);
|
|
908
|
+
delay = Math.min(delay, maxDelay);
|
|
909
|
+
if (jitter) {
|
|
910
|
+
const jitterAmount = delay * 0.25;
|
|
911
|
+
delay += Math.random() * jitterAmount * 2 - jitterAmount;
|
|
912
|
+
}
|
|
913
|
+
return Math.floor(delay);
|
|
914
|
+
}
|
|
915
|
+
async function withRetry(fn, options = {}) {
|
|
916
|
+
const {
|
|
917
|
+
maxAttempts = 3,
|
|
918
|
+
baseDelay = 500,
|
|
919
|
+
maxDelay = 1e4,
|
|
920
|
+
jitter = true,
|
|
921
|
+
retryOn = () => true
|
|
922
|
+
} = options;
|
|
923
|
+
let lastError;
|
|
924
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
925
|
+
try {
|
|
926
|
+
return await fn();
|
|
927
|
+
} catch (error) {
|
|
928
|
+
lastError = error;
|
|
929
|
+
if (!retryOn(error)) {
|
|
930
|
+
throw error;
|
|
931
|
+
}
|
|
932
|
+
if (attempt < maxAttempts - 1) {
|
|
933
|
+
const delay = calculateDelay(attempt, {
|
|
934
|
+
baseDelay,
|
|
935
|
+
maxDelay,
|
|
936
|
+
jitter
|
|
937
|
+
});
|
|
938
|
+
await sleep(delay);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
throw lastError;
|
|
943
|
+
}
|
|
944
|
+
function isRetryableRPCError(error) {
|
|
945
|
+
if (error instanceof Error) {
|
|
946
|
+
const message = error.message.toLowerCase();
|
|
947
|
+
if (message.includes("429") || message.includes("rate limit")) {
|
|
948
|
+
return true;
|
|
949
|
+
}
|
|
950
|
+
if (message.includes("timeout") || message.includes("econnreset")) {
|
|
951
|
+
return true;
|
|
952
|
+
}
|
|
953
|
+
if (message.includes("503") || message.includes("502") || message.includes("500")) {
|
|
954
|
+
return true;
|
|
955
|
+
}
|
|
956
|
+
if (message.includes("blockhash not found") || message.includes("slot skipped")) {
|
|
957
|
+
return true;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// src/client/payment.ts
|
|
964
|
+
function buildSolanaPayUrl(params) {
|
|
965
|
+
const { recipient, amount, splToken, reference, label, message } = params;
|
|
966
|
+
const url = new URL(`solana:${recipient}`);
|
|
967
|
+
if (amount !== void 0) {
|
|
968
|
+
url.searchParams.set("amount", amount.toString());
|
|
969
|
+
}
|
|
970
|
+
if (splToken) {
|
|
971
|
+
url.searchParams.set("spl-token", splToken);
|
|
972
|
+
}
|
|
973
|
+
if (reference) {
|
|
974
|
+
url.searchParams.set("reference", reference);
|
|
975
|
+
}
|
|
976
|
+
if (label) {
|
|
977
|
+
url.searchParams.set("label", label);
|
|
978
|
+
}
|
|
979
|
+
if (message) {
|
|
980
|
+
url.searchParams.set("message", message);
|
|
981
|
+
}
|
|
982
|
+
return url.toString();
|
|
983
|
+
}
|
|
984
|
+
function createPaymentFlow(config2) {
|
|
985
|
+
const { network, recipientWallet, amount, asset = "native", memo } = config2;
|
|
986
|
+
let decimals = 9;
|
|
987
|
+
let mintAddress;
|
|
988
|
+
if (asset === "usdc") {
|
|
989
|
+
decimals = 6;
|
|
990
|
+
mintAddress = network === "mainnet-beta" ? TOKEN_MINTS.USDC_MAINNET : TOKEN_MINTS.USDC_DEVNET;
|
|
991
|
+
} else if (asset === "usdt") {
|
|
992
|
+
decimals = 6;
|
|
993
|
+
mintAddress = TOKEN_MINTS.USDT_MAINNET;
|
|
994
|
+
} else if (typeof asset === "object" && "mint" in asset) {
|
|
995
|
+
decimals = asset.decimals ?? 6;
|
|
996
|
+
mintAddress = asset.mint;
|
|
997
|
+
}
|
|
998
|
+
const naturalAmount = Number(amount) / Math.pow(10, decimals);
|
|
999
|
+
return {
|
|
1000
|
+
/** Get the payment configuration */
|
|
1001
|
+
getConfig: () => ({ ...config2 }),
|
|
1002
|
+
/** Get amount in natural display units (e.g., 0.01 SOL) */
|
|
1003
|
+
getDisplayAmount: () => naturalAmount,
|
|
1004
|
+
/** Get amount formatted with symbol */
|
|
1005
|
+
getFormattedAmount: () => {
|
|
1006
|
+
const symbol = asset === "native" ? "SOL" : asset === "usdc" ? "USDC" : asset === "usdt" ? "USDT" : "tokens";
|
|
1007
|
+
return `${naturalAmount.toFixed(decimals > 6 ? 4 : 2)} ${symbol}`;
|
|
1008
|
+
},
|
|
1009
|
+
/** Generate Solana Pay URL for QR codes */
|
|
1010
|
+
getSolanaPayUrl: (options = {}) => {
|
|
1011
|
+
return buildSolanaPayUrl({
|
|
1012
|
+
recipient: recipientWallet,
|
|
1013
|
+
amount: naturalAmount,
|
|
1014
|
+
splToken: mintAddress,
|
|
1015
|
+
label: options.label,
|
|
1016
|
+
reference: options.reference,
|
|
1017
|
+
message: memo
|
|
1018
|
+
});
|
|
1019
|
+
},
|
|
1020
|
+
/** Get the token mint address (undefined for native SOL) */
|
|
1021
|
+
getMintAddress: () => mintAddress,
|
|
1022
|
+
/** Check if this is a native SOL payment */
|
|
1023
|
+
isNativePayment: () => asset === "native",
|
|
1024
|
+
/** Get network information */
|
|
1025
|
+
getNetworkInfo: () => ({
|
|
1026
|
+
network,
|
|
1027
|
+
isMainnet: network === "mainnet-beta",
|
|
1028
|
+
explorerUrl: network === "mainnet-beta" ? "https://explorer.solana.com" : "https://explorer.solana.com?cluster=devnet"
|
|
1029
|
+
}),
|
|
1030
|
+
/** Build explorer URL for a transaction */
|
|
1031
|
+
getExplorerUrl: (signature) => {
|
|
1032
|
+
const baseUrl = "https://explorer.solana.com/tx";
|
|
1033
|
+
const cluster = network === "mainnet-beta" ? "" : "?cluster=devnet";
|
|
1034
|
+
return `${baseUrl}/${signature}${cluster}`;
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
function createPaymentReference() {
|
|
1039
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
1040
|
+
return crypto.randomUUID();
|
|
1041
|
+
}
|
|
1042
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// src/pricing/index.ts
|
|
1046
|
+
var cachedPrice = null;
|
|
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);
|
|
1078
|
+
try {
|
|
1079
|
+
const response = await fetch(provider.url, {
|
|
1080
|
+
headers: { "Accept": "application/json" },
|
|
1081
|
+
signal: controller.signal
|
|
1082
|
+
});
|
|
1083
|
+
if (!response.ok) {
|
|
1084
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1085
|
+
}
|
|
1086
|
+
const data = await response.json();
|
|
1087
|
+
const price = provider.parse(data);
|
|
1088
|
+
if (!price || price <= 0) {
|
|
1089
|
+
throw new Error("Invalid price");
|
|
1090
|
+
}
|
|
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) {
|
|
1100
|
+
return 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
|
+
};
|
|
1127
|
+
return cachedPrice;
|
|
1128
|
+
} catch {
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
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
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
async function lamportsToUsd(lamports) {
|
|
1143
|
+
const { solPrice } = await getSolPrice();
|
|
1144
|
+
const sol = Number(lamports) / 1e9;
|
|
1145
|
+
return sol * solPrice;
|
|
1146
|
+
}
|
|
1147
|
+
async function usdToLamports(usd) {
|
|
1148
|
+
const { solPrice } = await getSolPrice();
|
|
1149
|
+
const sol = usd / solPrice;
|
|
1150
|
+
return BigInt(Math.floor(sol * 1e9));
|
|
1151
|
+
}
|
|
1152
|
+
async function formatPriceDisplay(lamports) {
|
|
1153
|
+
const { solPrice } = await getSolPrice();
|
|
1154
|
+
const sol = Number(lamports) / 1e9;
|
|
1155
|
+
const usd = sol * solPrice;
|
|
1156
|
+
return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
|
|
1157
|
+
}
|
|
1158
|
+
function formatPriceSync(lamports, solPrice) {
|
|
1159
|
+
const sol = Number(lamports) / 1e9;
|
|
1160
|
+
const usd = sol * solPrice;
|
|
1161
|
+
return {
|
|
1162
|
+
sol,
|
|
1163
|
+
usd,
|
|
1164
|
+
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
function clearPriceCache() {
|
|
1168
|
+
cachedPrice = null;
|
|
1169
|
+
lastProviderIndex = -1;
|
|
1170
|
+
}
|
|
1171
|
+
function getProviders() {
|
|
1172
|
+
return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
exports.TOKEN_MINTS = TOKEN_MINTS;
|
|
517
1176
|
exports.X402_HEADERS = X402_HEADERS;
|
|
518
1177
|
exports.addArticleToSession = addArticleToSession;
|
|
519
1178
|
exports.buildPaymentRequirement = buildPaymentRequirement;
|
|
1179
|
+
exports.buildSolanaPayUrl = buildSolanaPayUrl;
|
|
1180
|
+
exports.checkPaywallAccess = checkPaywallAccess;
|
|
1181
|
+
exports.clearPriceCache = clearPriceCache;
|
|
1182
|
+
exports.configurePricing = configurePricing;
|
|
520
1183
|
exports.create402Headers = create402Headers;
|
|
521
1184
|
exports.create402ResponseBody = create402ResponseBody;
|
|
1185
|
+
exports.createMemoryStore = createMemoryStore;
|
|
1186
|
+
exports.createPaymentFlow = createPaymentFlow;
|
|
1187
|
+
exports.createPaymentReference = createPaymentReference;
|
|
1188
|
+
exports.createPaywallMiddleware = createPaywallMiddleware;
|
|
1189
|
+
exports.createRedisStore = createRedisStore;
|
|
522
1190
|
exports.createSession = createSession;
|
|
523
1191
|
exports.decodePaymentRequired = decodePaymentRequired;
|
|
524
1192
|
exports.encodePaymentRequired = encodePaymentRequired;
|
|
525
1193
|
exports.encodePaymentResponse = encodePaymentResponse;
|
|
1194
|
+
exports.formatPriceDisplay = formatPriceDisplay;
|
|
1195
|
+
exports.formatPriceSync = formatPriceSync;
|
|
526
1196
|
exports.getConnection = getConnection;
|
|
1197
|
+
exports.getProviders = getProviders;
|
|
1198
|
+
exports.getSolPrice = getSolPrice;
|
|
1199
|
+
exports.getTokenDecimals = getTokenDecimals;
|
|
527
1200
|
exports.getWalletTransactions = getWalletTransactions;
|
|
528
1201
|
exports.isArticleUnlocked = isArticleUnlocked;
|
|
529
1202
|
exports.isMainnet = isMainnet;
|
|
1203
|
+
exports.isNativeAsset = isNativeAsset;
|
|
1204
|
+
exports.isRetryableRPCError = isRetryableRPCError;
|
|
530
1205
|
exports.lamportsToSol = lamportsToSol;
|
|
1206
|
+
exports.lamportsToUsd = lamportsToUsd;
|
|
531
1207
|
exports.parsePaymentHeader = parsePaymentHeader;
|
|
532
1208
|
exports.resetConnection = resetConnection;
|
|
1209
|
+
exports.resolveMintAddress = resolveMintAddress;
|
|
533
1210
|
exports.solToLamports = solToLamports;
|
|
534
1211
|
exports.toX402Network = toX402Network;
|
|
1212
|
+
exports.usdToLamports = usdToLamports;
|
|
535
1213
|
exports.validateSession = validateSession;
|
|
536
1214
|
exports.verifyPayment = verifyPayment;
|
|
1215
|
+
exports.verifySPLPayment = verifySPLPayment;
|
|
537
1216
|
exports.verifyX402Payment = verifyX402Payment;
|
|
538
1217
|
exports.waitForConfirmation = waitForConfirmation;
|
|
1218
|
+
exports.withPaywall = withPaywall;
|
|
1219
|
+
exports.withRetry = withRetry;
|
|
539
1220
|
//# sourceMappingURL=index.cjs.map
|
|
540
1221
|
//# sourceMappingURL=index.cjs.map
|