@alleyboss/micropay-solana-x402-paywall 3.2.2 → 3.3.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 +45 -0
- package/dist/agent/index.cjs +2 -2
- package/dist/agent/index.d.cts +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +2 -2
- package/dist/client/index.cjs +229 -5
- package/dist/client/index.d.cts +101 -1
- package/dist/client/index.d.ts +101 -1
- package/dist/client/index.js +227 -7
- package/dist/index.cjs +287 -159
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +285 -161
- package/dist/next/index.cjs +91 -28
- package/dist/next/index.js +91 -28
- package/dist/pricing/index.cjs +54 -38
- package/dist/pricing/index.d.cts +8 -9
- package/dist/pricing/index.d.ts +8 -9
- package/dist/pricing/index.js +54 -38
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -47,8 +47,8 @@ function buildSolanaPayUrl(params) {
|
|
|
47
47
|
}
|
|
48
48
|
return url.toString();
|
|
49
49
|
}
|
|
50
|
-
function createPaymentFlow(
|
|
51
|
-
const { network, recipientWallet, amount, asset = "native", memo } =
|
|
50
|
+
function createPaymentFlow(config) {
|
|
51
|
+
const { network, recipientWallet, amount, asset = "native", memo } = config;
|
|
52
52
|
let decimals = 9;
|
|
53
53
|
let mintAddress;
|
|
54
54
|
if (asset === "usdc") {
|
|
@@ -64,7 +64,7 @@ function createPaymentFlow(config2) {
|
|
|
64
64
|
const naturalAmount = Number(amount) / Math.pow(10, decimals);
|
|
65
65
|
return {
|
|
66
66
|
/** Get the payment configuration */
|
|
67
|
-
getConfig: () => ({ ...
|
|
67
|
+
getConfig: () => ({ ...config }),
|
|
68
68
|
/** Get amount in natural display units (e.g., 0.01 SOL) */
|
|
69
69
|
getDisplayAmount: () => naturalAmount,
|
|
70
70
|
/** Get amount formatted with symbol */
|
|
@@ -194,13 +194,17 @@ function usePaywallResource({
|
|
|
194
194
|
if (wwwAuth) {
|
|
195
195
|
setPaymentHeader(wwwAuth);
|
|
196
196
|
try {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
const { decodePaymentRequiredHeader: decodePaymentRequiredHeader2 } = await import('@x402/core/http');
|
|
198
|
+
const cleanHeader = wwwAuth.replace(/^[Xx]402\s+/, "");
|
|
199
|
+
const decoded = decodePaymentRequiredHeader2(cleanHeader);
|
|
200
|
+
const accepts = Array.isArray(decoded.accepts) ? decoded.accepts[0] : decoded.accepts;
|
|
201
|
+
if (accepts) {
|
|
202
|
+
const amountStr = accepts.amount || accepts.maxAmountRequired || "0";
|
|
203
|
+
setPrice(BigInt(amountStr));
|
|
204
|
+
setRecipient(accepts.payTo);
|
|
201
205
|
}
|
|
202
206
|
} catch (e) {
|
|
203
|
-
console.warn("Failed to parse
|
|
207
|
+
console.warn("[usePaywallResource] Failed to parse x402 header:", e);
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
210
|
setIsLocked(true);
|
|
@@ -252,10 +256,265 @@ function usePaywallResource({
|
|
|
252
256
|
unlock
|
|
253
257
|
};
|
|
254
258
|
}
|
|
259
|
+
|
|
260
|
+
// src/pricing/utils.ts
|
|
261
|
+
function lamportsToSol(lamports) {
|
|
262
|
+
return Number(lamports) / 1e9;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/pricing/index.ts
|
|
266
|
+
var priceCache = null;
|
|
267
|
+
var currentConfig = {};
|
|
268
|
+
function configurePricing(newConfig) {
|
|
269
|
+
currentConfig = { ...currentConfig, ...newConfig };
|
|
270
|
+
}
|
|
271
|
+
var PROVIDERS = [
|
|
272
|
+
{
|
|
273
|
+
name: "coincap",
|
|
274
|
+
url: "https://api.coincap.io/v2/assets/solana",
|
|
275
|
+
parse: (data) => parseFloat(data.data?.priceUsd || "0")
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: "binance",
|
|
279
|
+
url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
|
|
280
|
+
parse: (data) => parseFloat(data.price || "0")
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: "coingecko",
|
|
284
|
+
url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
|
|
285
|
+
parse: (data) => data.solana?.usd || 0
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: "kraken",
|
|
289
|
+
url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
|
|
290
|
+
parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
|
|
291
|
+
}
|
|
292
|
+
];
|
|
293
|
+
async function fetchFromProvider(provider, timeout) {
|
|
294
|
+
const controller = new AbortController();
|
|
295
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
296
|
+
try {
|
|
297
|
+
const response = await fetch(provider.url, {
|
|
298
|
+
headers: { "Accept": "application/json" },
|
|
299
|
+
signal: controller.signal
|
|
300
|
+
});
|
|
301
|
+
if (!response.ok) {
|
|
302
|
+
throw new Error(`HTTP ${response.status}`);
|
|
303
|
+
}
|
|
304
|
+
const data = await response.json();
|
|
305
|
+
const price = provider.parse(data);
|
|
306
|
+
if (!price || price <= 0) {
|
|
307
|
+
throw new Error("Invalid price");
|
|
308
|
+
}
|
|
309
|
+
return { price, source: provider.name };
|
|
310
|
+
} finally {
|
|
311
|
+
clearTimeout(timeoutId);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async function fetchPriceParallel(timeout) {
|
|
315
|
+
const promises = PROVIDERS.map(
|
|
316
|
+
(provider) => fetchFromProvider(provider, timeout).catch(() => null)
|
|
317
|
+
);
|
|
318
|
+
const results = await Promise.all(promises);
|
|
319
|
+
const validResult = results.find((r) => r !== null);
|
|
320
|
+
if (validResult) {
|
|
321
|
+
return validResult;
|
|
322
|
+
}
|
|
323
|
+
throw new Error("All providers failed");
|
|
324
|
+
}
|
|
325
|
+
async function fetchPriceSequential(timeout) {
|
|
326
|
+
for (const provider of PROVIDERS) {
|
|
327
|
+
try {
|
|
328
|
+
return await fetchFromProvider(provider, timeout);
|
|
329
|
+
} catch {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
throw new Error("All providers failed");
|
|
334
|
+
}
|
|
335
|
+
async function getSolPrice() {
|
|
336
|
+
const cacheTTL = currentConfig.cacheTTL ?? 6e4;
|
|
337
|
+
const timeout = currentConfig.timeout ?? 3e3;
|
|
338
|
+
const useParallel = currentConfig.parallelFetch ?? true;
|
|
339
|
+
const now = Date.now();
|
|
340
|
+
if (priceCache && now - priceCache.timestamp < cacheTTL) {
|
|
341
|
+
return priceCache.data;
|
|
342
|
+
}
|
|
343
|
+
if (currentConfig.customProvider) {
|
|
344
|
+
try {
|
|
345
|
+
const price = await currentConfig.customProvider();
|
|
346
|
+
if (price > 0) {
|
|
347
|
+
const data = {
|
|
348
|
+
solPrice: price,
|
|
349
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
350
|
+
source: "custom"
|
|
351
|
+
};
|
|
352
|
+
priceCache = { data, timestamp: now };
|
|
353
|
+
return data;
|
|
354
|
+
}
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
const result = useParallel ? await fetchPriceParallel(timeout) : await fetchPriceSequential(timeout);
|
|
360
|
+
const data = {
|
|
361
|
+
solPrice: result.price,
|
|
362
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
363
|
+
source: result.source
|
|
364
|
+
};
|
|
365
|
+
priceCache = { data, timestamp: now };
|
|
366
|
+
return data;
|
|
367
|
+
} catch {
|
|
368
|
+
if (priceCache) {
|
|
369
|
+
return {
|
|
370
|
+
...priceCache.data,
|
|
371
|
+
source: `${priceCache.data.source} (stale)`
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
throw new Error(
|
|
375
|
+
"Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async function lamportsToUsd(lamports) {
|
|
380
|
+
const { solPrice } = await getSolPrice();
|
|
381
|
+
const sol = Number(lamports) / 1e9;
|
|
382
|
+
return sol * solPrice;
|
|
383
|
+
}
|
|
384
|
+
async function usdToLamports(usd) {
|
|
385
|
+
const { solPrice } = await getSolPrice();
|
|
386
|
+
const sol = usd / solPrice;
|
|
387
|
+
return BigInt(Math.floor(sol * 1e9));
|
|
388
|
+
}
|
|
389
|
+
async function formatPriceDisplay(lamports) {
|
|
390
|
+
const { solPrice } = await getSolPrice();
|
|
391
|
+
const sol = Number(lamports) / 1e9;
|
|
392
|
+
const usd = sol * solPrice;
|
|
393
|
+
return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
|
|
394
|
+
}
|
|
395
|
+
function formatPriceSync(lamports, solPrice) {
|
|
396
|
+
const sol = Number(lamports) / 1e9;
|
|
397
|
+
const usd = sol * solPrice;
|
|
398
|
+
return {
|
|
399
|
+
sol,
|
|
400
|
+
usd,
|
|
401
|
+
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function clearPriceCache() {
|
|
405
|
+
priceCache = null;
|
|
406
|
+
}
|
|
407
|
+
function getProviders() {
|
|
408
|
+
return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/client/hooks.ts
|
|
412
|
+
function usePricing(refreshIntervalMs = 6e4) {
|
|
413
|
+
const [priceData, setPriceData] = react.useState(null);
|
|
414
|
+
const [isLoading, setIsLoading] = react.useState(true);
|
|
415
|
+
const [error, setError] = react.useState(null);
|
|
416
|
+
const intervalRef = react.useRef(null);
|
|
417
|
+
const fetchPrice = react.useCallback(async () => {
|
|
418
|
+
try {
|
|
419
|
+
setIsLoading(true);
|
|
420
|
+
setError(null);
|
|
421
|
+
const data = await getSolPrice();
|
|
422
|
+
setPriceData(data);
|
|
423
|
+
} catch (err) {
|
|
424
|
+
setError(err instanceof Error ? err.message : "Failed to fetch price");
|
|
425
|
+
} finally {
|
|
426
|
+
setIsLoading(false);
|
|
427
|
+
}
|
|
428
|
+
}, []);
|
|
429
|
+
react.useEffect(() => {
|
|
430
|
+
fetchPrice();
|
|
431
|
+
if (refreshIntervalMs > 0) {
|
|
432
|
+
intervalRef.current = setInterval(fetchPrice, refreshIntervalMs);
|
|
433
|
+
}
|
|
434
|
+
return () => {
|
|
435
|
+
if (intervalRef.current) {
|
|
436
|
+
clearInterval(intervalRef.current);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
}, [fetchPrice, refreshIntervalMs]);
|
|
440
|
+
return {
|
|
441
|
+
solPrice: priceData?.solPrice ?? null,
|
|
442
|
+
source: priceData?.source ?? null,
|
|
443
|
+
fetchedAt: priceData?.fetchedAt ?? null,
|
|
444
|
+
isLoading,
|
|
445
|
+
error,
|
|
446
|
+
refresh: fetchPrice
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function useLamportsToUsd(lamports) {
|
|
450
|
+
const { solPrice, isLoading } = usePricing();
|
|
451
|
+
if (isLoading || !solPrice || lamports === null) {
|
|
452
|
+
return { usd: null, formatted: null, isLoading };
|
|
453
|
+
}
|
|
454
|
+
const lamportsBigInt = typeof lamports === "number" ? BigInt(lamports) : lamports;
|
|
455
|
+
const sol = Number(lamportsBigInt) / 1e9;
|
|
456
|
+
const usd = sol * solPrice;
|
|
457
|
+
return {
|
|
458
|
+
usd,
|
|
459
|
+
formatted: `$${usd.toFixed(2)}`,
|
|
460
|
+
isLoading: false
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function useMicropay() {
|
|
464
|
+
const [status, setStatus] = react.useState("idle");
|
|
465
|
+
const [error, setError] = react.useState(null);
|
|
466
|
+
const [signature, setSignature] = react.useState(null);
|
|
467
|
+
const reset = react.useCallback(() => {
|
|
468
|
+
setStatus("idle");
|
|
469
|
+
setError(null);
|
|
470
|
+
setSignature(null);
|
|
471
|
+
}, []);
|
|
472
|
+
const pay = react.useCallback(async (_options) => {
|
|
473
|
+
setStatus("pending");
|
|
474
|
+
setError(null);
|
|
475
|
+
try {
|
|
476
|
+
throw new Error(
|
|
477
|
+
"useMicropay requires implementation of onSign/onSend callbacks. See documentation for wallet adapter integration."
|
|
478
|
+
);
|
|
479
|
+
} catch (err) {
|
|
480
|
+
const errorMessage = err instanceof Error ? err.message : "Payment failed";
|
|
481
|
+
setError(errorMessage);
|
|
482
|
+
setStatus("error");
|
|
483
|
+
return { success: false, error: errorMessage };
|
|
484
|
+
}
|
|
485
|
+
}, []);
|
|
486
|
+
return {
|
|
487
|
+
status,
|
|
488
|
+
error,
|
|
489
|
+
signature,
|
|
490
|
+
pay,
|
|
491
|
+
reset
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function useFormatPrice(lamports) {
|
|
495
|
+
const { solPrice, isLoading } = usePricing();
|
|
496
|
+
if (isLoading || !solPrice || lamports === null) {
|
|
497
|
+
return {
|
|
498
|
+
sol: null,
|
|
499
|
+
usd: null,
|
|
500
|
+
formatted: "Loading...",
|
|
501
|
+
isLoading
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
const lamportsBigInt = typeof lamports === "number" ? BigInt(lamports) : lamports;
|
|
505
|
+
const sol = Number(lamportsBigInt) / 1e9;
|
|
506
|
+
const usd = sol * solPrice;
|
|
507
|
+
return {
|
|
508
|
+
sol,
|
|
509
|
+
usd,
|
|
510
|
+
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,
|
|
511
|
+
isLoading: false
|
|
512
|
+
};
|
|
513
|
+
}
|
|
255
514
|
var DEFAULT_COMPUTE_UNITS = 2e5;
|
|
256
515
|
var DEFAULT_MICRO_LAMPORTS = 1e3;
|
|
257
|
-
function createPriorityFeeInstructions(
|
|
258
|
-
const { enabled = false, microLamports, computeUnits } =
|
|
516
|
+
function createPriorityFeeInstructions(config = {}) {
|
|
517
|
+
const { enabled = false, microLamports, computeUnits } = config;
|
|
259
518
|
if (!enabled) {
|
|
260
519
|
return [];
|
|
261
520
|
}
|
|
@@ -266,14 +525,14 @@ function createPriorityFeeInstructions(config2 = {}) {
|
|
|
266
525
|
instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
|
|
267
526
|
return instructions;
|
|
268
527
|
}
|
|
269
|
-
async function buildVersionedTransaction(
|
|
528
|
+
async function buildVersionedTransaction(config) {
|
|
270
529
|
const {
|
|
271
530
|
connection,
|
|
272
531
|
payer,
|
|
273
532
|
instructions,
|
|
274
533
|
priorityFee,
|
|
275
534
|
recentBlockhash
|
|
276
|
-
} =
|
|
535
|
+
} = config;
|
|
277
536
|
const priorityIxs = createPriorityFeeInstructions(priorityFee);
|
|
278
537
|
const allInstructions = [...priorityIxs, ...instructions];
|
|
279
538
|
let blockhash;
|
|
@@ -389,9 +648,9 @@ async function getAgentBalance(connection, agentKeypair) {
|
|
|
389
648
|
balanceSol: balance / web3_js.LAMPORTS_PER_SOL
|
|
390
649
|
};
|
|
391
650
|
}
|
|
392
|
-
async function hasAgentSufficientBalance(connection, agentKeypair, requiredLamports) {
|
|
651
|
+
async function hasAgentSufficientBalance(connection, agentKeypair, requiredLamports, feeBufferLamports = 5000000n) {
|
|
393
652
|
const { balance } = await getAgentBalance(connection, agentKeypair);
|
|
394
|
-
const totalRequired = requiredLamports +
|
|
653
|
+
const totalRequired = requiredLamports + feeBufferLamports;
|
|
395
654
|
return {
|
|
396
655
|
sufficient: balance >= totalRequired,
|
|
397
656
|
balance,
|
|
@@ -440,20 +699,20 @@ function validateWalletAddress(address) {
|
|
|
440
699
|
const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
441
700
|
return base58Regex.test(address);
|
|
442
701
|
}
|
|
443
|
-
async function createCreditSession(walletAddress, purchaseId,
|
|
702
|
+
async function createCreditSession(walletAddress, purchaseId, config) {
|
|
444
703
|
if (!validateWalletAddress(walletAddress)) {
|
|
445
704
|
throw new Error("Invalid wallet address format");
|
|
446
705
|
}
|
|
447
|
-
if (
|
|
706
|
+
if (config.initialCredits <= 0 || config.initialCredits > MAX_CREDITS) {
|
|
448
707
|
throw new Error(`Credits must be between 1 and ${MAX_CREDITS}`);
|
|
449
708
|
}
|
|
450
|
-
if (!
|
|
709
|
+
if (!config.durationHours || config.durationHours <= 0 || config.durationHours > 8760) {
|
|
451
710
|
throw new Error("Session duration must be between 1 and 8760 hours (1 year)");
|
|
452
711
|
}
|
|
453
712
|
const sessionId = uuidv4();
|
|
454
713
|
const now = Math.floor(Date.now() / 1e3);
|
|
455
|
-
const expiresAt = now +
|
|
456
|
-
const bundleExpiry =
|
|
714
|
+
const expiresAt = now + config.durationHours * 3600;
|
|
715
|
+
const bundleExpiry = config.bundleExpiryHours ? now + config.bundleExpiryHours * 3600 : expiresAt;
|
|
457
716
|
const session = {
|
|
458
717
|
id: sessionId,
|
|
459
718
|
walletAddress,
|
|
@@ -461,22 +720,22 @@ async function createCreditSession(walletAddress, purchaseId, config2) {
|
|
|
461
720
|
siteWideUnlock: false,
|
|
462
721
|
createdAt: now,
|
|
463
722
|
expiresAt,
|
|
464
|
-
credits:
|
|
723
|
+
credits: config.initialCredits,
|
|
465
724
|
bundleExpiry,
|
|
466
|
-
bundleType:
|
|
725
|
+
bundleType: config.bundleType
|
|
467
726
|
};
|
|
468
727
|
const payload = {
|
|
469
728
|
sub: walletAddress,
|
|
470
729
|
sid: sessionId,
|
|
471
730
|
articles: session.unlockedArticles,
|
|
472
731
|
siteWide: false,
|
|
473
|
-
credits:
|
|
732
|
+
credits: config.initialCredits,
|
|
474
733
|
bundleExpiry,
|
|
475
|
-
bundleType:
|
|
734
|
+
bundleType: config.bundleType,
|
|
476
735
|
iat: now,
|
|
477
736
|
exp: expiresAt
|
|
478
737
|
};
|
|
479
|
-
const token = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${
|
|
738
|
+
const token = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config.durationHours}h`).sign(getSecretKey(config.secret));
|
|
480
739
|
return { token, session };
|
|
481
740
|
}
|
|
482
741
|
async function validateCreditSession(token, secret) {
|
|
@@ -594,141 +853,6 @@ async function getRemainingCredits(token, secret) {
|
|
|
594
853
|
};
|
|
595
854
|
}
|
|
596
855
|
|
|
597
|
-
// src/pricing/utils.ts
|
|
598
|
-
function lamportsToSol(lamports) {
|
|
599
|
-
return Number(lamports) / 1e9;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// src/pricing/index.ts
|
|
603
|
-
var cachedPrice = null;
|
|
604
|
-
var config = {};
|
|
605
|
-
var lastProviderIndex = -1;
|
|
606
|
-
function configurePricing(newConfig) {
|
|
607
|
-
config = { ...config, ...newConfig };
|
|
608
|
-
cachedPrice = null;
|
|
609
|
-
}
|
|
610
|
-
var PROVIDERS = [
|
|
611
|
-
{
|
|
612
|
-
name: "coincap",
|
|
613
|
-
url: "https://api.coincap.io/v2/assets/solana",
|
|
614
|
-
parse: (data) => parseFloat(data.data?.priceUsd || "0")
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
name: "binance",
|
|
618
|
-
url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
|
|
619
|
-
parse: (data) => parseFloat(data.price || "0")
|
|
620
|
-
},
|
|
621
|
-
{
|
|
622
|
-
name: "coingecko",
|
|
623
|
-
url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
|
|
624
|
-
parse: (data) => data.solana?.usd || 0
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
name: "kraken",
|
|
628
|
-
url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
|
|
629
|
-
parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
|
|
630
|
-
}
|
|
631
|
-
];
|
|
632
|
-
async function fetchFromProvider(provider, timeout) {
|
|
633
|
-
const controller = new AbortController();
|
|
634
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
635
|
-
try {
|
|
636
|
-
const response = await fetch(provider.url, {
|
|
637
|
-
headers: { "Accept": "application/json" },
|
|
638
|
-
signal: controller.signal
|
|
639
|
-
});
|
|
640
|
-
if (!response.ok) {
|
|
641
|
-
throw new Error(`HTTP ${response.status}`);
|
|
642
|
-
}
|
|
643
|
-
const data = await response.json();
|
|
644
|
-
const price = provider.parse(data);
|
|
645
|
-
if (!price || price <= 0) {
|
|
646
|
-
throw new Error("Invalid price");
|
|
647
|
-
}
|
|
648
|
-
return price;
|
|
649
|
-
} finally {
|
|
650
|
-
clearTimeout(timeoutId);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
async function getSolPrice() {
|
|
654
|
-
const cacheTTL = config.cacheTTL ?? 6e4;
|
|
655
|
-
const timeout = config.timeout ?? 5e3;
|
|
656
|
-
if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
|
|
657
|
-
return cachedPrice;
|
|
658
|
-
}
|
|
659
|
-
if (config.customProvider) {
|
|
660
|
-
try {
|
|
661
|
-
const price = await config.customProvider();
|
|
662
|
-
if (price > 0) {
|
|
663
|
-
cachedPrice = {
|
|
664
|
-
solPrice: price,
|
|
665
|
-
fetchedAt: /* @__PURE__ */ new Date(),
|
|
666
|
-
source: "custom"
|
|
667
|
-
};
|
|
668
|
-
return cachedPrice;
|
|
669
|
-
}
|
|
670
|
-
} catch {
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
for (let i = 0; i < PROVIDERS.length; i++) {
|
|
674
|
-
const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
|
|
675
|
-
const provider = PROVIDERS[idx];
|
|
676
|
-
try {
|
|
677
|
-
const price = await fetchFromProvider(provider, timeout);
|
|
678
|
-
lastProviderIndex = idx;
|
|
679
|
-
cachedPrice = {
|
|
680
|
-
solPrice: price,
|
|
681
|
-
fetchedAt: /* @__PURE__ */ new Date(),
|
|
682
|
-
source: provider.name
|
|
683
|
-
};
|
|
684
|
-
return cachedPrice;
|
|
685
|
-
} catch {
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
if (cachedPrice) {
|
|
690
|
-
return {
|
|
691
|
-
...cachedPrice,
|
|
692
|
-
source: `${cachedPrice.source} (stale)`
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
throw new Error(
|
|
696
|
-
"Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
async function lamportsToUsd(lamports) {
|
|
700
|
-
const { solPrice } = await getSolPrice();
|
|
701
|
-
const sol = Number(lamports) / 1e9;
|
|
702
|
-
return sol * solPrice;
|
|
703
|
-
}
|
|
704
|
-
async function usdToLamports(usd) {
|
|
705
|
-
const { solPrice } = await getSolPrice();
|
|
706
|
-
const sol = usd / solPrice;
|
|
707
|
-
return BigInt(Math.floor(sol * 1e9));
|
|
708
|
-
}
|
|
709
|
-
async function formatPriceDisplay(lamports) {
|
|
710
|
-
const { solPrice } = await getSolPrice();
|
|
711
|
-
const sol = Number(lamports) / 1e9;
|
|
712
|
-
const usd = sol * solPrice;
|
|
713
|
-
return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
|
|
714
|
-
}
|
|
715
|
-
function formatPriceSync(lamports, solPrice) {
|
|
716
|
-
const sol = Number(lamports) / 1e9;
|
|
717
|
-
const usd = sol * solPrice;
|
|
718
|
-
return {
|
|
719
|
-
sol,
|
|
720
|
-
usd,
|
|
721
|
-
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
function clearPriceCache() {
|
|
725
|
-
cachedPrice = null;
|
|
726
|
-
lastProviderIndex = -1;
|
|
727
|
-
}
|
|
728
|
-
function getProviders() {
|
|
729
|
-
return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
|
|
730
|
-
}
|
|
731
|
-
|
|
732
856
|
exports.addCredits = addCredits;
|
|
733
857
|
exports.buildSolanaPayUrl = buildSolanaPayUrl;
|
|
734
858
|
exports.clearPriceCache = clearPriceCache;
|
|
@@ -752,7 +876,11 @@ exports.lamportsToUsd = lamportsToUsd;
|
|
|
752
876
|
exports.sendSolanaPayment = sendSolanaPayment;
|
|
753
877
|
exports.usdToLamports = usdToLamports;
|
|
754
878
|
exports.useCredit = useCredit;
|
|
879
|
+
exports.useFormatPrice = useFormatPrice;
|
|
880
|
+
exports.useLamportsToUsd = useLamportsToUsd;
|
|
881
|
+
exports.useMicropay = useMicropay;
|
|
755
882
|
exports.usePaywallResource = usePaywallResource;
|
|
883
|
+
exports.usePricing = usePricing;
|
|
756
884
|
exports.validateCreditSession = validateCreditSession;
|
|
757
885
|
Object.keys(core).forEach(function (k) {
|
|
758
886
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ export * from '@x402/core';
|
|
|
2
2
|
export * from '@x402/core/types';
|
|
3
3
|
export * from '@x402/core/client';
|
|
4
4
|
export * from '@x402/svm';
|
|
5
|
-
export { PaymentFlowConfig, PaymentResult, PaywallConfig, PaywallState, SendPaymentParams, SolanaPayUrlParams, WalletAdapterInterface, buildSolanaPayUrl, createPaymentFlow, createPaymentReference, createX402AuthorizationHeader, sendSolanaPayment, usePaywallResource } from './client/index.cjs';
|
|
5
|
+
export { PaymentFlowConfig, PaymentResult, PaymentStatus, PaywallConfig, PaywallState, SendPaymentParams, SolanaPayUrlParams, WalletAdapterInterface, buildSolanaPayUrl, createPaymentFlow, createPaymentReference, createX402AuthorizationHeader, sendSolanaPayment, useFormatPrice, useLamportsToUsd, useMicropay, usePaywallResource, usePricing } from './client/index.cjs';
|
|
6
6
|
export { AgentPaymentResult, CreditSessionClaims, CreditSessionConfig, CreditSessionData, CreditValidation, ExecuteAgentPaymentParams, UseCreditResult, addCredits, createCreditSession, executeAgentPayment, generateAgentKeypair, getAgentBalance, getRemainingCredits, hasAgentSufficientBalance, keypairFromBase58, useCredit, validateCreditSession } from './agent/index.cjs';
|
|
7
7
|
export { CustomPriceProvider, PriceConfig, PriceData, clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToSol, lamportsToUsd, usdToLamports } from './pricing/index.cjs';
|
|
8
8
|
import '@solana/web3.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export * from '@x402/core';
|
|
|
2
2
|
export * from '@x402/core/types';
|
|
3
3
|
export * from '@x402/core/client';
|
|
4
4
|
export * from '@x402/svm';
|
|
5
|
-
export { PaymentFlowConfig, PaymentResult, PaywallConfig, PaywallState, SendPaymentParams, SolanaPayUrlParams, WalletAdapterInterface, buildSolanaPayUrl, createPaymentFlow, createPaymentReference, createX402AuthorizationHeader, sendSolanaPayment, usePaywallResource } from './client/index.js';
|
|
5
|
+
export { PaymentFlowConfig, PaymentResult, PaymentStatus, PaywallConfig, PaywallState, SendPaymentParams, SolanaPayUrlParams, WalletAdapterInterface, buildSolanaPayUrl, createPaymentFlow, createPaymentReference, createX402AuthorizationHeader, sendSolanaPayment, useFormatPrice, useLamportsToUsd, useMicropay, usePaywallResource, usePricing } from './client/index.js';
|
|
6
6
|
export { AgentPaymentResult, CreditSessionClaims, CreditSessionConfig, CreditSessionData, CreditValidation, ExecuteAgentPaymentParams, UseCreditResult, addCredits, createCreditSession, executeAgentPayment, generateAgentKeypair, getAgentBalance, getRemainingCredits, hasAgentSufficientBalance, keypairFromBase58, useCredit, validateCreditSession } from './agent/index.js';
|
|
7
7
|
export { CustomPriceProvider, PriceConfig, PriceData, clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToSol, lamportsToUsd, usdToLamports } from './pricing/index.js';
|
|
8
8
|
import '@solana/web3.js';
|