@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.
@@ -76,8 +76,33 @@ function toAtomicUnits(amount, decimals) {
76
76
  const multiplier = Math.pow(10, decimals);
77
77
  return Math.floor(amount * multiplier).toString();
78
78
  }
79
+ function safeBase64Encode(str) {
80
+ if (typeof Buffer !== "undefined") {
81
+ return Buffer.from(str, "utf-8").toString("base64");
82
+ }
83
+ const bytes = new TextEncoder().encode(str);
84
+ let binary = "";
85
+ for (let i = 0; i < bytes.length; i++) {
86
+ binary += String.fromCharCode(bytes[i]);
87
+ }
88
+ return btoa(binary);
89
+ }
90
+ function safeBase64Decode(encoded) {
91
+ if (typeof Buffer !== "undefined") {
92
+ return Buffer.from(encoded, "base64").toString("utf-8");
93
+ }
94
+ const binary = atob(encoded);
95
+ const bytes = new Uint8Array(binary.length);
96
+ for (let i = 0; i < binary.length; i++) {
97
+ bytes[i] = binary.charCodeAt(i);
98
+ }
99
+ return new TextDecoder().decode(bytes);
100
+ }
101
+ function encodeBase64Json(obj) {
102
+ return safeBase64Encode(JSON.stringify(obj));
103
+ }
79
104
  function decodeBase64Json(encoded) {
80
- return JSON.parse(atob(encoded));
105
+ return JSON.parse(safeBase64Decode(encoded));
81
106
  }
82
107
 
83
108
  // src/server/facilitator-client.ts
@@ -287,7 +312,7 @@ function createX402Server(config) {
287
312
  };
288
313
  }
289
314
  function encodeRequirements(requirements) {
290
- return btoa(JSON.stringify(requirements));
315
+ return encodeBase64Json(requirements);
291
316
  }
292
317
  function create402Response(requirements) {
293
318
  return {
@@ -411,7 +436,7 @@ function x402Middleware(config) {
411
436
  network,
412
437
  payer: verifyResult.payer ?? ""
413
438
  };
414
- res.setHeader("PAYMENT-RESPONSE", btoa(JSON.stringify(paymentResponseData)));
439
+ res.setHeader("PAYMENT-RESPONSE", encodeBase64Json(paymentResponseData));
415
440
  next();
416
441
  } catch (error) {
417
442
  log("Middleware error:", error);
@@ -424,6 +449,7 @@ function x402Middleware(config) {
424
449
  }
425
450
 
426
451
  // src/server/browser-support.ts
452
+ 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>`;
427
453
  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
454
  var DEXTER_STYLES = `
429
455
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap');
@@ -433,7 +459,8 @@ body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:#0a0a0a;c
433
459
  .crest{margin:0 auto .75rem}
434
460
  h1{font-family:'Orbitron',sans-serif;font-size:1.15rem;font-weight:700;color:#f1f5f9;letter-spacing:.04em;margin-bottom:.35rem}
435
461
  .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}
462
+ .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}
463
+ .price svg{width:1.3em;height:1.3em;flex-shrink:0}
437
464
  .chain{color:#525252;font-size:.75rem;margin-bottom:1.25rem;letter-spacing:.03em}
438
465
  .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
466
  .endpoint code{font-family:'SF Mono',Monaco,Consolas,monospace;font-size:.8rem;color:#F26B1A}
@@ -628,13 +655,15 @@ if (btn) {
628
655
  setBtnState('Verifying...', true, true);
629
656
  setStatus('Payment submitted, verifying...');
630
657
 
658
+ // Use the original request body if available
659
+ const originalBody = dataEl.dataset.body ? atob(dataEl.dataset.body) : '{}';
631
660
  const response = await fetch(requestUrl, {
632
661
  method: requestMethod,
633
662
  headers: {
634
663
  'Content-Type': 'application/json',
635
664
  'PAYMENT-SIGNATURE': paymentHeader,
636
665
  },
637
- body: requestMethod !== 'GET' ? '{}' : undefined,
666
+ body: requestMethod !== 'GET' ? originalBody : undefined,
638
667
  });
639
668
 
640
669
  if (response.ok) {
@@ -652,14 +681,14 @@ if (btn) {
652
681
  }
653
682
  } catch (err) {
654
683
  console.error('[x402] Payment error:', err);
655
- setBtnState('Pay $' + document.getElementById('price-value').textContent, false, false);
684
+ setBtnState('Pay ' + document.getElementById('price-value').textContent, false, false);
656
685
  setStatus(err.message || 'Payment failed', 'error');
657
686
  }
658
687
  });
659
688
  }
660
689
  </script>
661
690
  `;
662
- function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config, rpcUrl) {
691
+ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config, rpcUrl, requestBody) {
663
692
  let price = "?";
664
693
  let description = "This resource requires payment";
665
694
  let network = "";
@@ -684,7 +713,7 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
684
713
  <head>
685
714
  <meta charset="utf-8">
686
715
  <meta name="viewport" content="width=device-width,initial-scale=1">
687
- <title>${config.title} \u2014 $${price} USDC</title>
716
+ <title>${config.title} \u2014 ${price} USDC</title>
688
717
  <style>${DEXTER_STYLES}${PAY_BUTTON_STYLES}</style>
689
718
  </head>
690
719
  <body>
@@ -692,12 +721,12 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
692
721
  <div class="crest">${DEXTER_CREST_SVG}</div>
693
722
  <h1>${config.title}</h1>
694
723
  <p class="desc">${description}</p>
695
- <div class="price">$<span id="price-value">${price}</span> USDC</div>
724
+ <div class="price">${USDC_ICON_SVG}<span id="price-value">${price}</span></div>
696
725
  <div class="chain">${chainName}${chainName ? " network" : ""}</div>
697
726
  ${endpointSection}
698
727
 
699
728
  <div id="pay-section" class="pay-section" style="display:none">
700
- <button id="pay-btn" class="pay-btn">Pay $${price}</button>
729
+ <button id="pay-btn" class="pay-btn">Pay ${price}</button>
701
730
  <div id="pay-status" class="pay-status"></div>
702
731
  <div class="pay-alt">or use <a href="${config.sdkUrl}">x402 SDK</a> for programmatic access</div>
703
732
  </div>
@@ -712,7 +741,7 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
712
741
  </div>
713
742
 
714
743
  <div class="footer">
715
- <a href="https://x402.org">x402</a>
744
+ <a href="https://docs.dexter.cash/docs/sdk/">x402</a>
716
745
  <span class="sep"></span>
717
746
  <a href="https://dexter.cash">Dexter</a>
718
747
  </div>
@@ -723,6 +752,7 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
723
752
  data-method="${method}"
724
753
  data-url="${requestUrl}"
725
754
  data-rpc="${rpcUrl}"
755
+ data-body="${requestBody ? Buffer.from(requestBody).toString("base64") : ""}"
726
756
  ></div>
727
757
  ${PAY_SCRIPT}
728
758
  </body>
@@ -731,8 +761,8 @@ ${PAY_SCRIPT}
731
761
  function x402BrowserSupport(config = {}) {
732
762
  const resolvedConfig = {
733
763
  title: config.title ?? "Payment Required",
734
- branding: config.branding ?? 'Powered by <a href="https://x402.org">x402</a>',
735
- sdkUrl: config.sdkUrl ?? "https://x402.org",
764
+ branding: config.branding ?? 'Powered by <a href="https://docs.dexter.cash/docs/sdk/">Dexter x402</a>',
765
+ sdkUrl: config.sdkUrl ?? "https://docs.dexter.cash/docs/sdk/",
736
766
  showEndpoint: config.showEndpoint ?? true
737
767
  };
738
768
  const rpcUrl = config.rpcUrl ?? "https://api.dexter.cash/api/solana/rpc";
@@ -742,12 +772,20 @@ function x402BrowserSupport(config = {}) {
742
772
  if (res.statusCode === 402 && req.accepts("html") && !req.headers["payment-signature"]) {
743
773
  const paymentRequired = res.getHeader("PAYMENT-REQUIRED") || res.getHeader("payment-required");
744
774
  if (paymentRequired && typeof paymentRequired === "string") {
775
+ let bodyStr;
776
+ if (req.body && typeof req.body === "object" && Object.keys(req.body).length > 0) {
777
+ try {
778
+ bodyStr = JSON.stringify(req.body);
779
+ } catch {
780
+ }
781
+ }
745
782
  const html = generatePaywallHtml(
746
783
  paymentRequired,
747
784
  req.originalUrl,
748
785
  req.method,
749
786
  resolvedConfig,
750
- rpcUrl
787
+ rpcUrl,
788
+ bodyStr
751
789
  );
752
790
  res.status(402).type("html").send(html);
753
791
  return res;
@@ -858,7 +896,7 @@ function x402AccessPass(config) {
858
896
  ratePerHour: ratePerHour || void 0,
859
897
  issuer
860
898
  };
861
- const passInfoEncoded = btoa(JSON.stringify(passInfo));
899
+ const passInfoEncoded = encodeBase64Json(passInfo);
862
900
  function calculateCustomPrice(durationSeconds) {
863
901
  if (!ratePerHour) {
864
902
  throw new Error("Custom durations not supported \u2014 no ratePerHour configured");
@@ -948,7 +986,7 @@ function x402AccessPass(config) {
948
986
  network,
949
987
  payer: verifyResult.payer ?? ""
950
988
  };
951
- res.setHeader("PAYMENT-RESPONSE", btoa(JSON.stringify(paymentResponseData)));
989
+ res.setHeader("PAYMENT-RESPONSE", encodeBase64Json(paymentResponseData));
952
990
  res.setHeader("ACCESS-PASS", jwt);
953
991
  res.json({
954
992
  accessPass: {