@dexterai/x402 1.6.0 → 1.6.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/server/index.cjs +273 -33
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +10 -23
- package/dist/server/index.d.ts +10 -23
- package/dist/server/index.js +273 -33
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -424,7 +424,242 @@ function x402Middleware(config) {
|
|
|
424
424
|
}
|
|
425
425
|
|
|
426
426
|
// src/server/browser-support.ts
|
|
427
|
-
|
|
427
|
+
var DEXTER_CREST_SVG = `<svg width="36" height="36" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg"><g><path fill="#F2681A" d="m324.93,313.11c-115.5,0-231,0-350,0l350,0z"/><path fill="#FDFAF5" d="m230.43,50.62c1.1.85 2.19 1.7 3.32 2.57 6.02 4.8 11.77 9.88 17.46 15.07.92.84.92.84 1.86 1.69 1.82 1.69 3.59 3.42 5.35 5.16.61.56 1.22 1.13 1.84 1.71 5.66 5.76 6.18 10.43 6.13 18.3.02 1.16.04 2.32.06 3.52.06 3.83.06 7.65.07 11.48.02 2.68.05 5.35.08 8.03.05 5.6.09 11.21.1 16.81.02 7.15.09 14.31.17 21.46.06 5.53.1 11.05.13 16.58.02 2.64.04 5.27.07 7.91.18 17.58.12 32.82-11.24 47.32-7.35 7.27-16.54 12.06-25.42 17.22-1.97 1.16-3.94 2.33-5.91 3.49-7.16 4.24-14.34 8.44-21.53 12.62-4.8 2.79-9.59 5.6-14.38 8.42-1.25.73-2.5 1.47-3.79 2.23-2.32 1.36-4.64 2.73-6.96 4.1-27.47 16.09-27.47 16.09-42.16 12.93-8.06-2.28-14.94-5.82-22.16-10.02-1.17-.67-2.34-1.34-3.54-2.04-24.55-14.25-43.58-27.03-51.9-55.58-1.07-4.58-1.54-8.92-1.52-13.61.28-9.5.28-9.5-3.3-17.97-1.81-1.49-3.68-2.92-5.59-4.28-9.19-7.06-12.7-20.03-14.18-31.06-.54-5.77-.55-11.56-.6-17.35-.03-1.32-.07-2.63-.1-3.99-.01-1.26-.02-2.53-.03-3.83-.02-1.15-.03-2.29-.05-3.47.72-4.02 1.94-5.36 5.21-7.74 2.89-.53 2.89-.53 6.07-.46 1.71.02 1.71.02 3.46.05 1.19.04 2.37.08 3.59.12 1.2.02 2.41.04 3.65.06 2.97.05 5.93.13 8.9.23.14-1.35.29-2.7.43-4.08.63-5 1.78-9.74 3.14-14.58.22-.79.43-1.59.66-2.4.53-1.92 1.06-3.84 1.6-5.76-1.55-.45-1.55-.45-3.13-.9-9.52-3.52-17.1-10.95-21.37-20.1-3.81-9.26-3.87-20.34-.29-29.68 6.49-13.99 16.36-23.23 30.66-29.01 49.81-17.69 115.79 8.35 155.13 38.85z"/><path fill="#F2671A" d="m142.93,22.62c.86.19 1.73.39 2.62.59 36.12 8.21 68.79 24.98 95.38 50.75 1.02.98 2.03 1.97 3.08 2.98 10.84 10.66 10.84 10.66 11.05 14.62-2.06 3.55-5.44 4.18-9.17 5.3-.79.25-1.59.49-2.41.75-28.13 8.43-60.95 6.37-87.13-7.16-.86-.49-1.71-.97-2.6-1.48-7.37-4.05-12.59-3.36-20.59-1.54-22.76 4-48.47 1.53-68.69-9.74-4.88-3.88-8.23-8.29-10.21-14.22-.93-10.38-.67-18.44 5.83-26.83 19.57-23.38 55.99-20.36 82.83-14z"/><path fill="#F16619" d="m44.93,129.12c27.36-.03 54.72-.05 82.08-.06 12.7-.01 25.41-.01 38.11-.03 11.07-.01 22.14-.02 33.2-.02 5.86 0 11.73-.01 17.59-.01 5.51-.01 11.03-.01 16.54-.01 2.03 0 4.06 0 6.09-.01 2.76-.01 5.52 0 8.28 0 .81 0 1.63-.01 2.47-.01 5.51.02 5.51.02 6.81 1.32.22 3.43.22 3.43 0 7-2.75 2.75-3.42 2.66-7.15 2.82-1.41.07-1.41.07-2.85.14-1.47.05-1.47.05-2.98.11-1.49.07-1.49.07-3 .14-2.45.11-4.9.21-7.35.3-.2 1.3-.4 2.59-.6 3.93-2.57 16.08-5.93 29.89-18.89 40.86-10.35 7.28-21.87 8.49-34.17 7.71-13.11-2.33-22.52-9.19-30.33-19.83-4.49-7.64-4.8-17.05-5.83-25.67-4.24.39-8.47.77-12.83 1.17-.28 1.84-.28 1.84-.56 3.71-2.32 14.39-5.63 23.35-16.95 33.11-2.32 1.67-2.32 1.67-4.65 1.67 4 4.67 9.06 6.59 14.87 8.24 3.79 1.09 3.79 1.09 6.12 3.43-.65 5.31-.65 5.31-2.33 7-8.42-.27-15.13-2.29-22.17-7-1.09-1.21-2.17-2.43-3.25-3.65-2.72-2.81-4.45-3.84-8.36-4.16-1.67-.02-3.34-.02-5.01.01-1.77-.04-3.54-.09-5.3-.15-1.27-.04-1.27-.04-2.56-.08-9.26-.54-17.6-4.56-24.51-10.64-9.58-11.11-11.03-22.56-10.72-36.82.02-1.4.03-2.8.05-4.24.04-3.42.1-6.85.17-10.27z"/><path fill="#F26117" d="m172.68,203.08c7.27.09 13.23 1.97 18.87 6.65 2.88 3.07 3.86 5.12 4.25 9.32-.12 1.01-.24 2.02-.36 3.06-2.55.95-2.55.95-5.83 1.17-3.28-2.84-3.28-2.84-5.83-5.83-.36.58-.71 1.16-1.08 1.75-7.6 11.29-20.06 17.74-33.05 21.09-20.36 3.1-36.81-1.66-53.37-13.73-2.33-2.11-2.33-2.11-4.67-5.61.42-3.45.99-4.49 3.5-7 4.07.37 5.95 2.13 8.75 4.96 9.81 8.93 22.53 11.87 35.51 11.69 11.74-1.05 22.38-5.85 31.57-13.15 2.06-2.45 2.06-2.45 3.5-4.67-1.66.07-1.66.07-3.35.15-3.65-.15-3.65-.15-5.98-2.48.75-6.18 1.46-7.19 7.58-7.36z"/></g></svg>`;
|
|
428
|
+
var DEXTER_STYLES = `
|
|
429
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap');
|
|
430
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
431
|
+
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:#0a0a0a;color:#e2e8f0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem}
|
|
432
|
+
.card{max-width:460px;width:100%;background:rgba(20,20,20,.85);border:1px solid rgba(242,107,26,.12);border-radius:8px;padding:2rem 2rem 1.75rem;text-align:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
|
|
433
|
+
.crest{margin:0 auto .75rem}
|
|
434
|
+
h1{font-family:'Orbitron',sans-serif;font-size:1.15rem;font-weight:700;color:#f1f5f9;letter-spacing:.04em;margin-bottom:.35rem}
|
|
435
|
+
.desc{color:#94a3b8;font-size:.9rem;margin-bottom:1.25rem;line-height:1.5}
|
|
436
|
+
.price{font-family:'Orbitron',sans-serif;font-size:1.6rem;font-weight:700;color:#F26B1A;margin:.75rem 0 .25rem}
|
|
437
|
+
.chain{color:#525252;font-size:.75rem;margin-bottom:1.25rem;letter-spacing:.03em}
|
|
438
|
+
.endpoint{background:rgba(242,107,26,.06);border:1px solid rgba(242,107,26,.12);border-radius:6px;padding:.5rem .75rem;margin-bottom:1.25rem}
|
|
439
|
+
.endpoint code{font-family:'SF Mono',Monaco,Consolas,monospace;font-size:.8rem;color:#F26B1A}
|
|
440
|
+
.info{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.06);border-radius:6px;padding:.85rem 1rem;font-size:.82rem;color:#737373;line-height:1.6;text-align:left}
|
|
441
|
+
.info strong{color:#a3a3a3}
|
|
442
|
+
.info code{background:rgba(242,107,26,.08);padding:2px 5px;border-radius:3px;font-size:.78rem;color:#F26B1A;font-family:'SF Mono',Monaco,Consolas,monospace}
|
|
443
|
+
.info a{color:#F26B1A;text-decoration:none;font-weight:600}
|
|
444
|
+
.info a:hover{text-decoration:underline}
|
|
445
|
+
.footer{margin-top:1.25rem;display:flex;align-items:center;justify-content:center;gap:.75rem;font-size:.7rem;color:#404040}
|
|
446
|
+
.footer a{color:#525252;text-decoration:none}
|
|
447
|
+
.footer a:hover{color:#737373}
|
|
448
|
+
.sep{width:3px;height:3px;border-radius:50%;background:#333}
|
|
449
|
+
`;
|
|
450
|
+
var PAY_BUTTON_STYLES = `
|
|
451
|
+
.pay-section{margin:1.25rem 0}
|
|
452
|
+
.pay-btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;background:linear-gradient(135deg,#F26B1A,#D13F00);color:#fff;border:none;padding:.65rem 2rem;border-radius:6px;font-family:'Inter',sans-serif;font-size:.95rem;font-weight:600;cursor:pointer;transition:opacity .15s,transform .1s;min-width:180px}
|
|
453
|
+
.pay-btn:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}
|
|
454
|
+
.pay-btn:active:not(:disabled){transform:translateY(0)}
|
|
455
|
+
.pay-btn:disabled{opacity:.6;cursor:not-allowed}
|
|
456
|
+
.pay-btn .spinner{width:16px;height:16px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite}
|
|
457
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
458
|
+
.pay-status{font-size:.8rem;color:#737373;margin-top:.5rem;min-height:1.2em}
|
|
459
|
+
.pay-status.error{color:#ef4444}
|
|
460
|
+
.pay-status.success{color:#22c55e}
|
|
461
|
+
.pay-alt{font-size:.78rem;color:#404040;margin-top:.75rem}
|
|
462
|
+
.pay-alt a{color:#F26B1A;text-decoration:none}
|
|
463
|
+
.result-box{background:rgba(34,197,94,.06);border:1px solid rgba(34,197,94,.15);border-radius:6px;padding:.75rem;margin-top:.75rem;text-align:left;font-size:.78rem;max-height:200px;overflow:auto}
|
|
464
|
+
.result-box pre{color:#94a3b8;font-family:'SF Mono',Monaco,Consolas,monospace;white-space:pre-wrap;word-break:break-all}
|
|
465
|
+
.no-wallet{font-size:.82rem;color:#737373;margin:1rem 0}
|
|
466
|
+
`;
|
|
467
|
+
var PAY_SCRIPT = `
|
|
468
|
+
<script type="module">
|
|
469
|
+
// Payment data is embedded in #x402-data attributes
|
|
470
|
+
const dataEl = document.getElementById('x402-data');
|
|
471
|
+
if (!dataEl) throw new Error('Missing payment data');
|
|
472
|
+
|
|
473
|
+
const requirements = JSON.parse(atob(dataEl.dataset.requirements));
|
|
474
|
+
const requestMethod = dataEl.dataset.method;
|
|
475
|
+
const requestUrl = dataEl.dataset.url;
|
|
476
|
+
const rpcUrl = dataEl.dataset.rpc || 'https://api.dexter.cash/api/solana/rpc';
|
|
477
|
+
|
|
478
|
+
// Detect wallet provider
|
|
479
|
+
function getWalletProvider() {
|
|
480
|
+
if (window.phantom?.solana?.isPhantom) return { name: 'Phantom', provider: window.phantom.solana };
|
|
481
|
+
if (window.solflare?.isSolflare) return { name: 'Solflare', provider: window.solflare };
|
|
482
|
+
if (window.backpack) return { name: 'Backpack', provider: window.backpack };
|
|
483
|
+
// Generic wallet-standard fallback
|
|
484
|
+
if (window.solana) return { name: 'Wallet', provider: window.solana };
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const walletInfo = getWalletProvider();
|
|
489
|
+
const btn = document.getElementById('pay-btn');
|
|
490
|
+
const status = document.getElementById('pay-status');
|
|
491
|
+
const section = document.getElementById('pay-section');
|
|
492
|
+
const noWallet = document.getElementById('no-wallet');
|
|
493
|
+
|
|
494
|
+
if (walletInfo && btn) {
|
|
495
|
+
section.style.display = 'block';
|
|
496
|
+
if (noWallet) noWallet.style.display = 'none';
|
|
497
|
+
} else if (noWallet) {
|
|
498
|
+
noWallet.style.display = 'block';
|
|
499
|
+
if (section) section.style.display = 'none';
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Preload Solana libraries in background
|
|
503
|
+
let solanaLibs = null;
|
|
504
|
+
const preload = (async () => {
|
|
505
|
+
try {
|
|
506
|
+
const [web3, spl] = await Promise.all([
|
|
507
|
+
import('https://esm.sh/@solana/web3.js@1.98.0'),
|
|
508
|
+
import('https://esm.sh/@solana/spl-token@0.4.9'),
|
|
509
|
+
]);
|
|
510
|
+
solanaLibs = { web3, spl };
|
|
511
|
+
} catch (e) {
|
|
512
|
+
console.warn('[x402] Failed to preload Solana libraries:', e);
|
|
513
|
+
}
|
|
514
|
+
})();
|
|
515
|
+
|
|
516
|
+
function setStatus(msg, type) {
|
|
517
|
+
if (!status) return;
|
|
518
|
+
status.textContent = msg;
|
|
519
|
+
status.className = 'pay-status' + (type ? ' ' + type : '');
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function setBtnState(text, disabled, loading) {
|
|
523
|
+
if (!btn) return;
|
|
524
|
+
btn.disabled = disabled;
|
|
525
|
+
btn.innerHTML = loading
|
|
526
|
+
? '<span class="spinner"></span>' + text
|
|
527
|
+
: text;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (btn) {
|
|
531
|
+
btn.addEventListener('click', async () => {
|
|
532
|
+
if (!walletInfo) return;
|
|
533
|
+
const { provider } = walletInfo;
|
|
534
|
+
|
|
535
|
+
try {
|
|
536
|
+
// 1. Connect wallet
|
|
537
|
+
setBtnState('Connecting...', true, true);
|
|
538
|
+
setStatus('');
|
|
539
|
+
await provider.connect();
|
|
540
|
+
|
|
541
|
+
if (!provider.publicKey) {
|
|
542
|
+
throw new Error('Wallet did not provide a public key');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// 2. Load Solana libraries (should already be cached from preload)
|
|
546
|
+
setBtnState('Preparing...', true, true);
|
|
547
|
+
await preload;
|
|
548
|
+
if (!solanaLibs) {
|
|
549
|
+
// Retry once
|
|
550
|
+
const [web3, spl] = await Promise.all([
|
|
551
|
+
import('https://esm.sh/@solana/web3.js@1.98.0'),
|
|
552
|
+
import('https://esm.sh/@solana/spl-token@0.4.9'),
|
|
553
|
+
]);
|
|
554
|
+
solanaLibs = { web3, spl };
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const { web3, spl } = solanaLibs;
|
|
558
|
+
const { PublicKey, Connection, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } = web3;
|
|
559
|
+
const { getAssociatedTokenAddress, createTransferCheckedInstruction, getMint, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } = spl;
|
|
560
|
+
|
|
561
|
+
// 3. Parse payment requirements
|
|
562
|
+
const accept = requirements.accepts[0];
|
|
563
|
+
if (!accept) throw new Error('No payment method available');
|
|
564
|
+
|
|
565
|
+
const payTo = new PublicKey(accept.payTo);
|
|
566
|
+
const amount = BigInt(accept.amount || accept.maxAmountRequired);
|
|
567
|
+
const mintPubkey = new PublicKey(accept.asset);
|
|
568
|
+
const feePayer = accept.extra?.feePayer ? new PublicKey(accept.extra.feePayer) : provider.publicKey;
|
|
569
|
+
const userPubkey = provider.publicKey;
|
|
570
|
+
|
|
571
|
+
// 4. Build transaction
|
|
572
|
+
setBtnState('Building tx...', true, true);
|
|
573
|
+
const connection = new Connection(rpcUrl, 'confirmed');
|
|
574
|
+
|
|
575
|
+
const instructions = [];
|
|
576
|
+
|
|
577
|
+
// ComputeBudget
|
|
578
|
+
instructions.push(ComputeBudgetProgram.setComputeUnitLimit({ units: 12000 }));
|
|
579
|
+
instructions.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }));
|
|
580
|
+
|
|
581
|
+
// Determine token program
|
|
582
|
+
const mintInfo = await connection.getAccountInfo(mintPubkey, 'confirmed');
|
|
583
|
+
if (!mintInfo) throw new Error('Token mint not found');
|
|
584
|
+
const programId = mintInfo.owner.toBase58() === TOKEN_2022_PROGRAM_ID.toBase58() ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
|
|
585
|
+
|
|
586
|
+
const mint = await getMint(connection, mintPubkey, undefined, programId);
|
|
587
|
+
|
|
588
|
+
// ATAs
|
|
589
|
+
const sourceAta = await getAssociatedTokenAddress(mintPubkey, userPubkey, false, programId);
|
|
590
|
+
const destAta = await getAssociatedTokenAddress(mintPubkey, payTo, false, programId);
|
|
591
|
+
|
|
592
|
+
// Verify source exists
|
|
593
|
+
const sourceInfo = await connection.getAccountInfo(sourceAta, 'confirmed');
|
|
594
|
+
if (!sourceInfo) throw new Error('No USDC token account found. Make sure you have USDC in your wallet.');
|
|
595
|
+
|
|
596
|
+
// TransferChecked
|
|
597
|
+
instructions.push(createTransferCheckedInstruction(sourceAta, mintPubkey, destAta, userPubkey, amount, mint.decimals, [], programId));
|
|
598
|
+
|
|
599
|
+
const { blockhash } = await connection.getLatestBlockhash('confirmed');
|
|
600
|
+
const message = new TransactionMessage({ payerKey: feePayer, recentBlockhash: blockhash, instructions }).compileToV0Message();
|
|
601
|
+
const transaction = new VersionedTransaction(message);
|
|
602
|
+
|
|
603
|
+
// 5. Sign
|
|
604
|
+
setBtnState('Sign in wallet...', true, true);
|
|
605
|
+
setStatus('Approve the transaction in your wallet');
|
|
606
|
+
const signed = await provider.signTransaction(transaction);
|
|
607
|
+
const serialized = signed.serialize();
|
|
608
|
+
|
|
609
|
+
// Convert Uint8Array to base64
|
|
610
|
+
let payload = '';
|
|
611
|
+
const bytes = new Uint8Array(serialized);
|
|
612
|
+
const chunk = 8192;
|
|
613
|
+
for (let i = 0; i < bytes.length; i += chunk) {
|
|
614
|
+
payload += String.fromCharCode.apply(null, bytes.slice(i, i + chunk));
|
|
615
|
+
}
|
|
616
|
+
payload = btoa(payload);
|
|
617
|
+
|
|
618
|
+
// 6. Build payment-signature header (x402 v2 format)
|
|
619
|
+
const paymentSignature = {
|
|
620
|
+
x402Version: accept.x402Version ?? 2,
|
|
621
|
+
resource: requirements.resource,
|
|
622
|
+
accepted: accept,
|
|
623
|
+
payload,
|
|
624
|
+
};
|
|
625
|
+
const paymentHeader = btoa(JSON.stringify(paymentSignature));
|
|
626
|
+
|
|
627
|
+
// 7. Submit payment
|
|
628
|
+
setBtnState('Verifying...', true, true);
|
|
629
|
+
setStatus('Payment submitted, verifying...');
|
|
630
|
+
|
|
631
|
+
const response = await fetch(requestUrl, {
|
|
632
|
+
method: requestMethod,
|
|
633
|
+
headers: {
|
|
634
|
+
'Content-Type': 'application/json',
|
|
635
|
+
'PAYMENT-SIGNATURE': paymentHeader,
|
|
636
|
+
},
|
|
637
|
+
body: requestMethod !== 'GET' ? '{}' : undefined,
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
if (response.ok) {
|
|
641
|
+
const data = await response.json();
|
|
642
|
+
setBtnState('Paid', true, false);
|
|
643
|
+
setStatus('Payment successful', 'success');
|
|
644
|
+
// Show response
|
|
645
|
+
const resultBox = document.createElement('div');
|
|
646
|
+
resultBox.className = 'result-box';
|
|
647
|
+
resultBox.innerHTML = '<pre>' + JSON.stringify(data, null, 2).replace(/</g, '<') + '</pre>';
|
|
648
|
+
section.appendChild(resultBox);
|
|
649
|
+
} else {
|
|
650
|
+
const err = await response.json().catch(() => ({ error: 'Payment verification failed' }));
|
|
651
|
+
throw new Error(err.error || err.reason || 'Payment failed');
|
|
652
|
+
}
|
|
653
|
+
} catch (err) {
|
|
654
|
+
console.error('[x402] Payment error:', err);
|
|
655
|
+
setBtnState('Pay $' + document.getElementById('price-value').textContent, false, false);
|
|
656
|
+
setStatus(err.message || 'Payment failed', 'error');
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
</script>
|
|
661
|
+
`;
|
|
662
|
+
function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config, rpcUrl) {
|
|
428
663
|
let price = "?";
|
|
429
664
|
let description = "This resource requires payment";
|
|
430
665
|
let network = "";
|
|
@@ -442,7 +677,7 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config)
|
|
|
442
677
|
}
|
|
443
678
|
} catch {
|
|
444
679
|
}
|
|
445
|
-
const chainName = network.includes("solana") ? "Solana" : network.includes("eip155") ? "Base" : "
|
|
680
|
+
const chainName = network.includes("solana") ? "Solana" : network.includes("eip155") ? "Base" : "";
|
|
446
681
|
const endpointSection = config.showEndpoint ? `<div class="endpoint"><code>${method} ${requestUrl}</code></div>` : "";
|
|
447
682
|
return `<!DOCTYPE html>
|
|
448
683
|
<html lang="en">
|
|
@@ -450,43 +685,46 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config)
|
|
|
450
685
|
<meta charset="utf-8">
|
|
451
686
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
452
687
|
<title>${config.title} \u2014 $${price} USDC</title>
|
|
453
|
-
<style>
|
|
454
|
-
*{margin:0;padding:0;box-sizing:border-box}
|
|
455
|
-
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#0a0a0a;color:#e2e8f0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem}
|
|
456
|
-
.paywall{max-width:460px;width:100%;background:#141414;border:1px solid #2a2a2a;border-radius:16px;padding:2.5rem;text-align:center}
|
|
457
|
-
.paywall h1{font-size:1.35rem;margin-bottom:.35rem;color:#f1f5f9;font-weight:600}
|
|
458
|
-
.desc{color:#94a3b8;font-size:.95rem;margin-bottom:1.5rem;line-height:1.5}
|
|
459
|
-
.price-badge{display:inline-block;background:linear-gradient(135deg,#22c55e,#16a34a);color:#fff;padding:.6rem 2rem;border-radius:999px;font-size:1.75rem;font-weight:700;margin:1rem 0;letter-spacing:-.02em}
|
|
460
|
-
.chain{color:#64748b;font-size:.8rem;margin-bottom:1.5rem}
|
|
461
|
-
.endpoint{background:#1e1e1e;border:1px solid #333;border-radius:8px;padding:.6rem 1rem;margin-bottom:1.5rem}
|
|
462
|
-
.endpoint code{font-family:'SF Mono',Monaco,Consolas,monospace;font-size:.85rem;color:#60a5fa}
|
|
463
|
-
.info{background:#1a1a2e;border:1px solid #2d2d5e;border-radius:10px;padding:1rem 1.25rem;font-size:.85rem;color:#a0aec0;line-height:1.6;text-align:left}
|
|
464
|
-
.info strong{color:#e2e8f0}
|
|
465
|
-
.info code{background:#2a2a3e;padding:2px 6px;border-radius:4px;font-size:.8rem;color:#818cf8;font-family:'SF Mono',Monaco,Consolas,monospace}
|
|
466
|
-
.info a{color:#60a5fa;text-decoration:none;font-weight:600}
|
|
467
|
-
.info a:hover{text-decoration:underline}
|
|
468
|
-
.powered{margin-top:1.5rem;font-size:.75rem;color:#475569}
|
|
469
|
-
.powered a{color:#64748b;text-decoration:none}
|
|
470
|
-
.powered a:hover{color:#94a3b8}
|
|
471
|
-
.x402-badge{display:inline-flex;align-items:center;gap:.35rem;background:#1a1a2e;border:1px solid #2d2d5e;padding:.25rem .75rem;border-radius:999px;font-size:.7rem;color:#818cf8;margin-top:.75rem;font-weight:500}
|
|
472
|
-
</style>
|
|
688
|
+
<style>${DEXTER_STYLES}${PAY_BUTTON_STYLES}</style>
|
|
473
689
|
</head>
|
|
474
690
|
<body>
|
|
475
|
-
<div class="
|
|
691
|
+
<div class="card">
|
|
692
|
+
<div class="crest">${DEXTER_CREST_SVG}</div>
|
|
476
693
|
<h1>${config.title}</h1>
|
|
477
694
|
<p class="desc">${description}</p>
|
|
478
|
-
<div class="price-
|
|
479
|
-
<div class="chain">${chainName} network</div>
|
|
695
|
+
<div class="price">$<span id="price-value">${price}</span> USDC</div>
|
|
696
|
+
<div class="chain">${chainName}${chainName ? " network" : ""}</div>
|
|
480
697
|
${endpointSection}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
<
|
|
485
|
-
<a href="${config.sdkUrl}">
|
|
698
|
+
|
|
699
|
+
<div id="pay-section" class="pay-section" style="display:none">
|
|
700
|
+
<button id="pay-btn" class="pay-btn">Pay $${price}</button>
|
|
701
|
+
<div id="pay-status" class="pay-status"></div>
|
|
702
|
+
<div class="pay-alt">or use <a href="${config.sdkUrl}">x402 SDK</a> for programmatic access</div>
|
|
703
|
+
</div>
|
|
704
|
+
|
|
705
|
+
<div id="no-wallet" class="no-wallet" style="display:none">
|
|
706
|
+
<div class="info">
|
|
707
|
+
<strong>Access this endpoint:</strong><br><br>
|
|
708
|
+
Use any x402-compatible client or a browser with a Solana wallet extension (Phantom, Solflare, Backpack).<br><br>
|
|
709
|
+
<code>npm install @dexterai/x402</code><br><br>
|
|
710
|
+
<a href="${config.sdkUrl}">x402 SDK docs →</a>
|
|
711
|
+
</div>
|
|
712
|
+
</div>
|
|
713
|
+
|
|
714
|
+
<div class="footer">
|
|
715
|
+
<a href="https://x402.org">x402</a>
|
|
716
|
+
<span class="sep"></span>
|
|
717
|
+
<a href="https://dexter.cash">Dexter</a>
|
|
486
718
|
</div>
|
|
487
|
-
<div class="powered">${config.branding}</div>
|
|
488
|
-
<div class="x402-badge">x402 protocol</div>
|
|
489
719
|
</div>
|
|
720
|
+
|
|
721
|
+
<div id="x402-data" style="display:none"
|
|
722
|
+
data-requirements="${paymentRequiredHeader}"
|
|
723
|
+
data-method="${method}"
|
|
724
|
+
data-url="${requestUrl}"
|
|
725
|
+
data-rpc="${rpcUrl}"
|
|
726
|
+
></div>
|
|
727
|
+
${PAY_SCRIPT}
|
|
490
728
|
</body>
|
|
491
729
|
</html>`;
|
|
492
730
|
}
|
|
@@ -497,6 +735,7 @@ function x402BrowserSupport(config = {}) {
|
|
|
497
735
|
sdkUrl: config.sdkUrl ?? "https://x402.org",
|
|
498
736
|
showEndpoint: config.showEndpoint ?? true
|
|
499
737
|
};
|
|
738
|
+
const rpcUrl = config.rpcUrl ?? "https://api.dexter.cash/api/solana/rpc";
|
|
500
739
|
return (req, res, next) => {
|
|
501
740
|
const originalJson = res.json.bind(res);
|
|
502
741
|
res.json = function(body) {
|
|
@@ -507,7 +746,8 @@ function x402BrowserSupport(config = {}) {
|
|
|
507
746
|
paymentRequired,
|
|
508
747
|
req.originalUrl,
|
|
509
748
|
req.method,
|
|
510
|
-
resolvedConfig
|
|
749
|
+
resolvedConfig,
|
|
750
|
+
rpcUrl
|
|
511
751
|
);
|
|
512
752
|
res.status(402).type("html").send(html);
|
|
513
753
|
return res;
|