@dexterai/x402 1.6.1 → 1.6.3

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.
@@ -343,7 +343,7 @@ interface X402BrowserSupportConfig {
343
343
  title?: string;
344
344
  /** Custom branding text. @default 'Powered by x402' */
345
345
  branding?: string;
346
- /** URL to link for SDK/documentation. @default 'https://x402.org' */
346
+ /** URL to link for SDK/documentation. @default 'https://docs.dexter.cash/docs/sdk/' */
347
347
  sdkUrl?: string;
348
348
  /** Whether to include the request method and path. @default true */
349
349
  showEndpoint?: boolean;
@@ -343,7 +343,7 @@ interface X402BrowserSupportConfig {
343
343
  title?: string;
344
344
  /** Custom branding text. @default 'Powered by x402' */
345
345
  branding?: string;
346
- /** URL to link for SDK/documentation. @default 'https://x402.org' */
346
+ /** URL to link for SDK/documentation. @default 'https://docs.dexter.cash/docs/sdk/' */
347
347
  sdkUrl?: string;
348
348
  /** Whether to include the request method and path. @default true */
349
349
  showEndpoint?: boolean;
@@ -10,8 +10,33 @@ function toAtomicUnits(amount, decimals) {
10
10
  const multiplier = Math.pow(10, decimals);
11
11
  return Math.floor(amount * multiplier).toString();
12
12
  }
13
+ function safeBase64Encode(str) {
14
+ if (typeof Buffer !== "undefined") {
15
+ return Buffer.from(str, "utf-8").toString("base64");
16
+ }
17
+ const bytes = new TextEncoder().encode(str);
18
+ let binary = "";
19
+ for (let i = 0; i < bytes.length; i++) {
20
+ binary += String.fromCharCode(bytes[i]);
21
+ }
22
+ return btoa(binary);
23
+ }
24
+ function safeBase64Decode(encoded) {
25
+ if (typeof Buffer !== "undefined") {
26
+ return Buffer.from(encoded, "base64").toString("utf-8");
27
+ }
28
+ const binary = atob(encoded);
29
+ const bytes = new Uint8Array(binary.length);
30
+ for (let i = 0; i < binary.length; i++) {
31
+ bytes[i] = binary.charCodeAt(i);
32
+ }
33
+ return new TextDecoder().decode(bytes);
34
+ }
35
+ function encodeBase64Json(obj) {
36
+ return safeBase64Encode(JSON.stringify(obj));
37
+ }
13
38
  function decodeBase64Json(encoded) {
14
- return JSON.parse(atob(encoded));
39
+ return JSON.parse(safeBase64Decode(encoded));
15
40
  }
16
41
 
17
42
  // src/server/facilitator-client.ts
@@ -221,7 +246,7 @@ function createX402Server(config) {
221
246
  };
222
247
  }
223
248
  function encodeRequirements(requirements) {
224
- return btoa(JSON.stringify(requirements));
249
+ return encodeBase64Json(requirements);
225
250
  }
226
251
  function create402Response(requirements) {
227
252
  return {
@@ -345,7 +370,7 @@ function x402Middleware(config) {
345
370
  network,
346
371
  payer: verifyResult.payer ?? ""
347
372
  };
348
- res.setHeader("PAYMENT-RESPONSE", btoa(JSON.stringify(paymentResponseData)));
373
+ res.setHeader("PAYMENT-RESPONSE", encodeBase64Json(paymentResponseData));
349
374
  next();
350
375
  } catch (error) {
351
376
  log("Middleware error:", error);
@@ -358,6 +383,7 @@ function x402Middleware(config) {
358
383
  }
359
384
 
360
385
  // src/server/browser-support.ts
386
+ var USDC_ICON_SVG = `<svg width="18" height="18" viewBox="0 0 2000 2000" xmlns="http://www.w3.org/2000/svg"><path d="M1000 2000c554.17 0 1000-445.83 1000-1000S1554.17 0 1000 0 0 445.83 0 1000s445.83 1000 1000 1000z" fill="#2775ca"/><path d="M1275 1158.33c0-145.83-87.5-195.83-262.5-216.66-125-16.67-150-50-150-108.34s41.67-95.83 125-95.83c75 0 116.67 25 137.5 87.5 4.17 12.5 16.67 20.83 29.17 20.83h66.66c16.67 0 29.17-12.5 29.17-29.16v-4.17c-16.67-91.67-91.67-162.5-187.5-170.83v-100c0-16.67-12.5-29.17-33.33-33.34h-62.5c-16.67 0-29.17 12.5-33.34 33.34v95.83c-125 16.67-204.16 100-204.16 204.17 0 137.5 83.33 191.66 258.33 212.5 116.67 20.83 154.17 45.83 154.17 112.5s-58.34 112.5-137.5 112.5c-108.34 0-145.84-45.84-158.34-108.34-4.16-16.66-16.66-25-29.16-25h-70.84c-16.66 0-29.16 12.5-29.16 29.17v4.17c16.66 104.16 83.33 179.16 220.83 200v100c0 16.66 12.5 29.16 33.33 33.33h62.5c16.67 0 29.17-12.5 33.34-33.33v-100c125-20.84 208.33-108.34 208.33-220.84z" fill="#fff"/><path d="M787.5 1595.83c-325-116.66-491.67-479.16-370.83-800 62.5-175 200-308.33 370.83-370.83 16.67-8.33 25-20.83 25-41.67V325c0-16.67-8.33-29.17-25-33.33-4.17 0-12.5 0-16.67 4.16-395.83 125-612.5 545.84-487.5 941.67 75 233.33 254.17 412.5 487.5 487.5 16.67 8.33 33.34 0 37.5-16.67 4.17-4.16 4.17-8.33 4.17-16.66v-58.34c0-12.5-12.5-29.16-25-37.5zM1229.17 295.83c-16.67-8.33-33.34 0-37.5 16.67-4.17 4.17-4.17 8.33-4.17 16.67v58.33c0 16.67 12.5 33.33 25 41.67 325 116.66 491.67 479.16 370.83 800-62.5 175-200 308.33-370.83 370.83-16.67 8.33-25 20.83-25 41.67V1700c0 16.67 8.33 29.17 25 33.33 4.17 0 12.5 0 16.67-4.16 395.83-125 612.5-545.84 487.5-941.67-75-237.5-258.34-416.67-487.5-491.67z" fill="#fff"/></svg>`;
361
387
  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>`;
362
388
  var DEXTER_STYLES = `
363
389
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap');
@@ -367,7 +393,8 @@ body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:#0a0a0a;c
367
393
  .crest{margin:0 auto .75rem}
368
394
  h1{font-family:'Orbitron',sans-serif;font-size:1.15rem;font-weight:700;color:#f1f5f9;letter-spacing:.04em;margin-bottom:.35rem}
369
395
  .desc{color:#94a3b8;font-size:.9rem;margin-bottom:1.25rem;line-height:1.5}
370
- .price{font-family:'Orbitron',sans-serif;font-size:1.6rem;font-weight:700;color:#F26B1A;margin:.75rem 0 .25rem}
396
+ .price{font-family:'Orbitron',sans-serif;font-size:1.6rem;font-weight:700;color:#F26B1A;margin:.75rem 0 .25rem;display:inline-flex;align-items:center;gap:.35rem}
397
+ .price svg{width:1.3em;height:1.3em;flex-shrink:0}
371
398
  .chain{color:#525252;font-size:.75rem;margin-bottom:1.25rem;letter-spacing:.03em}
372
399
  .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}
373
400
  .endpoint code{font-family:'SF Mono',Monaco,Consolas,monospace;font-size:.8rem;color:#F26B1A}
@@ -562,13 +589,15 @@ if (btn) {
562
589
  setBtnState('Verifying...', true, true);
563
590
  setStatus('Payment submitted, verifying...');
564
591
 
592
+ // Use the original request body if available
593
+ const originalBody = dataEl.dataset.body ? atob(dataEl.dataset.body) : '{}';
565
594
  const response = await fetch(requestUrl, {
566
595
  method: requestMethod,
567
596
  headers: {
568
597
  'Content-Type': 'application/json',
569
598
  'PAYMENT-SIGNATURE': paymentHeader,
570
599
  },
571
- body: requestMethod !== 'GET' ? '{}' : undefined,
600
+ body: requestMethod !== 'GET' ? originalBody : undefined,
572
601
  });
573
602
 
574
603
  if (response.ok) {
@@ -586,14 +615,14 @@ if (btn) {
586
615
  }
587
616
  } catch (err) {
588
617
  console.error('[x402] Payment error:', err);
589
- setBtnState('Pay $' + document.getElementById('price-value').textContent, false, false);
618
+ setBtnState('Pay ' + document.getElementById('price-value').textContent, false, false);
590
619
  setStatus(err.message || 'Payment failed', 'error');
591
620
  }
592
621
  });
593
622
  }
594
623
  </script>
595
624
  `;
596
- function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config, rpcUrl) {
625
+ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config, rpcUrl, requestBody) {
597
626
  let price = "?";
598
627
  let description = "This resource requires payment";
599
628
  let network = "";
@@ -618,7 +647,7 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
618
647
  <head>
619
648
  <meta charset="utf-8">
620
649
  <meta name="viewport" content="width=device-width,initial-scale=1">
621
- <title>${config.title} \u2014 $${price} USDC</title>
650
+ <title>${config.title} \u2014 ${price} USDC</title>
622
651
  <style>${DEXTER_STYLES}${PAY_BUTTON_STYLES}</style>
623
652
  </head>
624
653
  <body>
@@ -626,12 +655,12 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
626
655
  <div class="crest">${DEXTER_CREST_SVG}</div>
627
656
  <h1>${config.title}</h1>
628
657
  <p class="desc">${description}</p>
629
- <div class="price">$<span id="price-value">${price}</span> USDC</div>
658
+ <div class="price">${USDC_ICON_SVG}<span id="price-value">${price}</span></div>
630
659
  <div class="chain">${chainName}${chainName ? " network" : ""}</div>
631
660
  ${endpointSection}
632
661
 
633
662
  <div id="pay-section" class="pay-section" style="display:none">
634
- <button id="pay-btn" class="pay-btn">Pay $${price}</button>
663
+ <button id="pay-btn" class="pay-btn">Pay ${price}</button>
635
664
  <div id="pay-status" class="pay-status"></div>
636
665
  <div class="pay-alt">or use <a href="${config.sdkUrl}">x402 SDK</a> for programmatic access</div>
637
666
  </div>
@@ -646,7 +675,7 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
646
675
  </div>
647
676
 
648
677
  <div class="footer">
649
- <a href="https://x402.org">x402</a>
678
+ <a href="https://docs.dexter.cash/docs/sdk/">x402</a>
650
679
  <span class="sep"></span>
651
680
  <a href="https://dexter.cash">Dexter</a>
652
681
  </div>
@@ -657,6 +686,7 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
657
686
  data-method="${method}"
658
687
  data-url="${requestUrl}"
659
688
  data-rpc="${rpcUrl}"
689
+ data-body="${requestBody ? Buffer.from(requestBody).toString("base64") : ""}"
660
690
  ></div>
661
691
  ${PAY_SCRIPT}
662
692
  </body>
@@ -665,8 +695,8 @@ ${PAY_SCRIPT}
665
695
  function x402BrowserSupport(config = {}) {
666
696
  const resolvedConfig = {
667
697
  title: config.title ?? "Payment Required",
668
- branding: config.branding ?? 'Powered by <a href="https://x402.org">x402</a>',
669
- sdkUrl: config.sdkUrl ?? "https://x402.org",
698
+ branding: config.branding ?? 'Powered by <a href="https://docs.dexter.cash/docs/sdk/">Dexter x402</a>',
699
+ sdkUrl: config.sdkUrl ?? "https://docs.dexter.cash/docs/sdk/",
670
700
  showEndpoint: config.showEndpoint ?? true
671
701
  };
672
702
  const rpcUrl = config.rpcUrl ?? "https://api.dexter.cash/api/solana/rpc";
@@ -676,12 +706,20 @@ function x402BrowserSupport(config = {}) {
676
706
  if (res.statusCode === 402 && req.accepts("html") && !req.headers["payment-signature"]) {
677
707
  const paymentRequired = res.getHeader("PAYMENT-REQUIRED") || res.getHeader("payment-required");
678
708
  if (paymentRequired && typeof paymentRequired === "string") {
709
+ let bodyStr;
710
+ if (req.body && typeof req.body === "object" && Object.keys(req.body).length > 0) {
711
+ try {
712
+ bodyStr = JSON.stringify(req.body);
713
+ } catch {
714
+ }
715
+ }
679
716
  const html = generatePaywallHtml(
680
717
  paymentRequired,
681
718
  req.originalUrl,
682
719
  req.method,
683
720
  resolvedConfig,
684
- rpcUrl
721
+ rpcUrl,
722
+ bodyStr
685
723
  );
686
724
  res.status(402).type("html").send(html);
687
725
  return res;
@@ -792,7 +830,7 @@ function x402AccessPass(config) {
792
830
  ratePerHour: ratePerHour || void 0,
793
831
  issuer
794
832
  };
795
- const passInfoEncoded = btoa(JSON.stringify(passInfo));
833
+ const passInfoEncoded = encodeBase64Json(passInfo);
796
834
  function calculateCustomPrice(durationSeconds) {
797
835
  if (!ratePerHour) {
798
836
  throw new Error("Custom durations not supported \u2014 no ratePerHour configured");
@@ -882,7 +920,7 @@ function x402AccessPass(config) {
882
920
  network,
883
921
  payer: verifyResult.payer ?? ""
884
922
  };
885
- res.setHeader("PAYMENT-RESPONSE", btoa(JSON.stringify(paymentResponseData)));
923
+ res.setHeader("PAYMENT-RESPONSE", encodeBase64Json(paymentResponseData));
886
924
  res.setHeader("ACCESS-PASS", jwt);
887
925
  res.json({
888
926
  accessPass: {