@kwespay/widget 1.0.6 → 1.0.8

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.
@@ -14,7 +14,7 @@
14
14
  const STABLECOIN_SYMBOLS = ["USDT", "USDC", "DAI", "BUSD", "USDc"];
15
15
 
16
16
  const DEFAULT_CONFIG = {
17
- graphqlEndpoint: "https://8284-154-161-98-26.ngrok-free.app",
17
+ graphqlEndpoint: "https://api.kwespay.xyz/",
18
18
  currency: SUPPORTED_CURRENCIES.USD,
19
19
  };
20
20
 
@@ -16952,6 +16952,84 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
16952
16952
  }
16953
16953
  };
16954
16954
 
16955
+ function isMobileDevice() {
16956
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
16957
+ navigator.userAgent
16958
+ );
16959
+ }
16960
+
16961
+ function dispatchWidgetEvent(eventName, detail = {}) {
16962
+ const event = new CustomEvent(`kwespay:${eventName}`, {
16963
+ detail,
16964
+ bubbles: true,
16965
+ cancelable: true,
16966
+ });
16967
+ window.dispatchEvent(event);
16968
+ }
16969
+
16970
+ function truncateHash(hash, startChars = 10, endChars = 8) {
16971
+ if (!hash) return "";
16972
+ return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
16973
+ }
16974
+
16975
+ function getErrorType(error) {
16976
+ const msg = error?.message ?? "";
16977
+ if (
16978
+ error?.code === 4001 ||
16979
+ error?.code === "ACTION_REJECTED" ||
16980
+ msg.includes("rejected") ||
16981
+ msg.includes("denied") ||
16982
+ msg.includes("cancelled")
16983
+ )
16984
+ return "USER_REJECTED";
16985
+ if (msg.toLowerCase().includes("insufficient")) return "INSUFFICIENT_BALANCE";
16986
+ if (
16987
+ msg.includes("User rejected") ||
16988
+ msg.includes("User closed modal") ||
16989
+ msg.includes("Connection request reset")
16990
+ )
16991
+ return "CONNECTION_REJECTED";
16992
+ if (msg.includes("timeout")) return "TIMEOUT";
16993
+ return "UNKNOWN";
16994
+ }
16995
+
16996
+ function getErrorMessage(error, context = {}) {
16997
+ const type = getErrorType(error);
16998
+
16999
+ console.error("[KwesPay] Payment error breakdown:", {
17000
+ errorType: type,
17001
+ message: error?.message,
17002
+ code: error?.code,
17003
+ data: error?.data,
17004
+ reason: error?.reason,
17005
+ stack: error?.stack,
17006
+ raw: error,
17007
+ context,
17008
+ });
17009
+
17010
+ if (error?.data) console.error("[KwesPay] Contract revert data:", error.data);
17011
+ if (error?.transaction)
17012
+ console.error("[KwesPay] Failed tx details:", error.transaction);
17013
+ if (error?.receipt) console.error("[KwesPay] Tx receipt:", error.receipt);
17014
+
17015
+ switch (type) {
17016
+ case "USER_REJECTED":
17017
+ return "You cancelled the transaction in your wallet.";
17018
+ case "INSUFFICIENT_BALANCE":
17019
+ return `Not enough ${context.token || "funds"} to complete this payment.`;
17020
+ case "CONNECTION_REJECTED":
17021
+ return "Wallet connection was cancelled.";
17022
+ case "TIMEOUT":
17023
+ return "Connection timed out. Please try again.";
17024
+ default:
17025
+ return (
17026
+ error?.reason ||
17027
+ error?.message ||
17028
+ "Something went wrong while processing your payment."
17029
+ );
17030
+ }
17031
+ }
17032
+
16955
17033
  const WIDGET_STYLES = `
16956
17034
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
16957
17035
 
@@ -18141,180 +18219,12 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18141
18219
  `;
18142
18220
  }
18143
18221
 
18144
- function dispatchWidgetEvent(eventName, detail = {}) {
18145
- const event = new CustomEvent(`kwespay:${eventName}`, {
18146
- detail,
18147
- bubbles: true,
18148
- cancelable: true,
18149
- });
18150
- window.dispatchEvent(event);
18151
- }
18152
-
18153
- function truncateHash(hash, startChars = 10, endChars = 8) {
18154
- if (!hash) return "";
18155
- return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
18156
- }
18157
-
18158
- function getErrorType(error) {
18159
- const msg = error?.message ?? "";
18160
- if (
18161
- error?.code === 4001 ||
18162
- error?.code === "ACTION_REJECTED" ||
18163
- msg.includes("rejected") ||
18164
- msg.includes("denied") ||
18165
- msg.includes("cancelled")
18166
- )
18167
- return "USER_REJECTED";
18168
- if (msg.toLowerCase().includes("insufficient")) return "INSUFFICIENT_BALANCE";
18169
- if (
18170
- msg.includes("User rejected") ||
18171
- msg.includes("User closed modal") ||
18172
- msg.includes("Connection request reset")
18173
- )
18174
- return "CONNECTION_REJECTED";
18175
- if (msg.includes("timeout")) return "TIMEOUT";
18176
- return "UNKNOWN";
18177
- }
18178
-
18179
- function getErrorMessage(error, context = {}) {
18180
- const type = getErrorType(error);
18181
-
18182
- console.error("[KwesPay] Payment error breakdown:", {
18183
- errorType: type,
18184
- message: error?.message,
18185
- code: error?.code,
18186
- data: error?.data,
18187
- reason: error?.reason,
18188
- stack: error?.stack,
18189
- raw: error,
18190
- context,
18191
- });
18192
-
18193
- if (error?.data) console.error("[KwesPay] Contract revert data:", error.data);
18194
- if (error?.transaction)
18195
- console.error("[KwesPay] Failed tx details:", error.transaction);
18196
- if (error?.receipt) console.error("[KwesPay] Tx receipt:", error.receipt);
18197
-
18198
- switch (type) {
18199
- case "USER_REJECTED":
18200
- return "You cancelled the transaction in your wallet.";
18201
- case "INSUFFICIENT_BALANCE":
18202
- return `Not enough ${context.token || "funds"} to complete this payment.`;
18203
- case "CONNECTION_REJECTED":
18204
- return "Wallet connection was cancelled.";
18205
- case "TIMEOUT":
18206
- return "Connection timed out. Please try again.";
18207
- default:
18208
- return (
18209
- error?.reason ||
18210
- error?.message ||
18211
- "Something went wrong while processing your payment."
18212
- );
18213
- }
18214
- }
18215
-
18216
- const PLATFORM_FEE_BPS = 25;
18217
-
18218
- // ── Utilities ─────────────────────────────────────────────────────────────────
18219
-
18220
- function formatUnits$1(rawBigInt, decimals) {
18221
- const divisor = BigInt(10 ** decimals);
18222
- const whole = rawBigInt / divisor;
18223
- const remainder = rawBigInt % divisor;
18224
-
18225
- if (remainder === 0n) return `${whole}`;
18226
-
18227
- const fracFull = remainder.toString().padStart(decimals, "0");
18228
-
18229
- if (whole > 0n) {
18230
- const frac = fracFull.slice(0, 4).replace(/0+$/, "");
18231
- return frac ? `${whole}.${frac}` : `${whole}`;
18232
- }
18233
-
18234
- let firstSig = -1;
18235
- for (let i = 0; i < fracFull.length; i++) {
18236
- if (fracFull[i] !== "0") {
18237
- firstSig = i;
18238
- break;
18239
- }
18240
- }
18241
- if (firstSig === -1) return `${whole}`;
18242
-
18243
- const sigSlice = fracFull.slice(firstSig, firstSig + 4).replace(/0+$/, "");
18244
- return `0.${fracFull.slice(0, firstSig) + sigSlice}`;
18245
- }
18246
-
18247
- function resolveAcceptedTokens(input) {
18248
- if (!input) return null;
18249
- if (input === "stablecoins") return STABLECOIN_SYMBOLS;
18250
- if (Array.isArray(input) && input.length)
18251
- return input.map((t) => t.toUpperCase());
18252
- return null;
18253
- }
18254
-
18255
- function isMobileDevice() {
18256
- return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
18257
- }
18258
-
18259
- // ── Widget ────────────────────────────────────────────────────────────────────
18260
-
18261
- class KwesPayWidget {
18262
- constructor(config) {
18263
- if (!config.apiKey) throw new Error("[KwesPayWidget] apiKey is required");
18264
- if (!config.vendorId)
18265
- throw new Error("[KwesPayWidget] vendorId is required");
18266
- if (!config.amount || parseFloat(config.amount) <= 0)
18267
- throw new Error("[KwesPayWidget] Valid amount is required");
18268
-
18269
- this.config = {
18270
- apiKey: config.apiKey,
18271
- vendorId: config.vendorId,
18272
- amount: parseFloat(config.amount),
18273
- currency: config.currency || DEFAULT_CONFIG.currency,
18274
- graphqlEndpoint: DEFAULT_CONFIG.graphqlEndpoint,
18275
- acceptedTokens: resolveAcceptedTokens(config.acceptedTokens),
18276
- };
18277
-
18278
- if (!Object.values(SUPPORTED_CURRENCIES).includes(this.config.currency)) {
18279
- throw new Error(
18280
- `[KwesPayWidget] Unsupported currency: ${this.config.currency}`
18281
- );
18282
- }
18283
-
18284
- this.walletService = new WalletService();
18285
- this.paymentService = new PaymentService$1(
18286
- this.config.apiKey,
18287
- this.config.graphqlEndpoint
18288
- );
18289
-
18290
- this.state = {
18291
- isOpen: false,
18292
- currentStep: 0,
18293
- selectedNetwork: null,
18294
- selectedNetworkName: "",
18295
- selectedChainId: null,
18296
- selectedRpcUrl: null,
18297
- selectedContractAddress: null,
18298
- selectedToken: null,
18299
- selectedTokenConfig: null,
18300
- vendorInfo: null,
18301
- keyAllowedNetworks: null,
18302
- keyAllowedTokens: null,
18303
- currentPayload: null,
18304
- quoteTimerInterval: null,
18305
- wcUri: null,
18306
- };
18307
-
18308
- this._init();
18309
- }
18310
-
18311
- // ── Bootstrap ───────────────────────────────────────────────────────────────
18312
-
18222
+ const DomMethods = {
18313
18223
  _init() {
18314
18224
  this._injectFonts();
18315
18225
  this._injectStyles();
18316
18226
  this._createWidgetDOM();
18317
- }
18227
+ },
18318
18228
 
18319
18229
  _injectFonts() {
18320
18230
  if (
@@ -18328,7 +18238,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18328
18238
  link.rel = "stylesheet";
18329
18239
  document.head.appendChild(link);
18330
18240
  }
18331
- }
18241
+ },
18332
18242
 
18333
18243
  _injectStyles() {
18334
18244
  if (!document.getElementById("kwespay-widget-styles")) {
@@ -18337,7 +18247,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18337
18247
  style.textContent = WIDGET_STYLES;
18338
18248
  document.head.appendChild(style);
18339
18249
  }
18340
- }
18250
+ },
18341
18251
 
18342
18252
  _createWidgetDOM() {
18343
18253
  document.getElementById("kwespay-widget-overlay")?.remove();
@@ -18364,15 +18274,16 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18364
18274
  document.body.appendChild(overlay);
18365
18275
 
18366
18276
  this._setupEventListeners();
18277
+ this._setupSwipeToClose(container);
18367
18278
 
18368
- overlay.addEventListener("click", (e) => {
18369
- if (e.target === overlay) this.close();
18370
- });
18279
+ // Clicking outside the container does NOT close the widget (intentional)
18280
+ },
18371
18281
 
18372
- // Swipe-down to close on mobile
18282
+ _setupSwipeToClose(container) {
18373
18283
  let _touchStartY = 0,
18374
18284
  _touchCurrentY = 0,
18375
18285
  _isDragging = false;
18286
+
18376
18287
  container.addEventListener(
18377
18288
  "touchstart",
18378
18289
  (e) => {
@@ -18387,6 +18298,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18387
18298
  },
18388
18299
  { passive: true }
18389
18300
  );
18301
+
18390
18302
  container.addEventListener(
18391
18303
  "touchmove",
18392
18304
  (e) => {
@@ -18397,6 +18309,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18397
18309
  },
18398
18310
  { passive: true }
18399
18311
  );
18312
+
18400
18313
  container.addEventListener("touchend", () => {
18401
18314
  if (!_isDragging) return;
18402
18315
  _isDragging = false;
@@ -18405,7 +18318,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18405
18318
  if (delta > 120) this.close();
18406
18319
  else container.style.transform = "translateY(0)";
18407
18320
  });
18408
- }
18321
+ },
18409
18322
 
18410
18323
  _setupEventListeners() {
18411
18324
  document
@@ -18514,10 +18427,83 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18514
18427
  });
18515
18428
  });
18516
18429
  }
18517
- }
18518
-
18519
- // ── Network / token list rendering ──────────────────────────────────────────
18430
+ },
18431
+ };
18432
+
18433
+ const NavMethods = {
18434
+ _goToStep(stepNumber) {
18435
+ document
18436
+ .querySelectorAll(".kwespay-container .step")
18437
+ .forEach((s) => s.classList.remove("active", "exiting"));
18438
+ this._activateStep(stepNumber);
18439
+ this.state.currentStep = stepNumber;
18440
+ },
18441
+
18442
+ _activateStep(stepNumber) {
18443
+ let targetStep;
18444
+ if (stepNumber === 0.5)
18445
+ targetStep = document.getElementById("kwespay-step0-invalid");
18446
+ else if (typeof stepNumber === "string")
18447
+ targetStep = document.getElementById(`kwespay-step-${stepNumber}`);
18448
+ else targetStep = document.getElementById(`kwespay-step${stepNumber}`);
18449
+
18450
+ if (targetStep) targetStep.classList.add("active");
18451
+ else console.warn(`[KwesPayWidget] Step not found: ${stepNumber}`);
18452
+ },
18453
+
18454
+ _removeCustomStep(id) {
18455
+ document.getElementById(id)?.remove();
18456
+ },
18457
+
18458
+ _showError(title, message) {
18459
+ const titleEl = document.getElementById("kwespay-errorTitle");
18460
+ const msgEl = document.getElementById("kwespay-errorMessage");
18461
+ if (titleEl) titleEl.textContent = title;
18462
+ if (msgEl) msgEl.textContent = message;
18463
+ this._goToStep(6);
18464
+ },
18465
+
18466
+ _reset() {
18467
+ this._clearQuoteTimer();
18468
+ this._removeCustomStep("kwespay-step-wallet-picker");
18469
+ this._removeCustomStep("kwespay-step-wc");
18470
+ this.state.selectedNetwork = null;
18471
+ this.state.selectedNetworkName = "";
18472
+ this.state.selectedChainId = null;
18473
+ this.state.selectedRpcUrl = null;
18474
+ this.state.selectedContractAddress = null;
18475
+ this.state.selectedToken = null;
18476
+ this.state.selectedTokenConfig = null;
18477
+ this.state.currentPayload = null;
18478
+ this.state.wcUri = null;
18479
+ },
18480
+ };
18520
18481
 
18482
+ const APIKeyMethods = {
18483
+ async _validateAPIKey() {
18484
+ try {
18485
+ const validation = await this.paymentService.validateAPIKey();
18486
+ if (validation.valid) {
18487
+ this.state.vendorInfo = validation.vendorInfo;
18488
+ this.state.keyAllowedNetworks = validation.allowedNetworks ?? null;
18489
+ this.state.keyAllowedTokens = validation.allowedTokens ?? null;
18490
+ this._goToStep(1);
18491
+ dispatchWidgetEvent("apiKeyValidated", {
18492
+ vendorInfo: this.state.vendorInfo,
18493
+ });
18494
+ } else {
18495
+ this._goToStep(0.5);
18496
+ dispatchWidgetEvent("apiKeyInvalid", {});
18497
+ }
18498
+ } catch (err) {
18499
+ console.error("[KwesPayWidget] API key validation error:", err.message);
18500
+ this._goToStep(0.5);
18501
+ dispatchWidgetEvent("apiKeyError", { error: err.message });
18502
+ }
18503
+ },
18504
+ };
18505
+
18506
+ const NetworkMethods = {
18521
18507
  _renderNetworkList() {
18522
18508
  const mainnetList = document.getElementById("kwespay-mainnetList");
18523
18509
  const testnetList = document.getElementById("kwespay-testnetList");
@@ -18579,7 +18565,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18579
18565
 
18580
18566
  if (testnetSection)
18581
18567
  testnetSection.style.display = hasTestnet ? "block" : "none";
18582
- }
18568
+ },
18583
18569
 
18584
18570
  _renderTokenList() {
18585
18571
  if (!this.state.selectedNetwork) return;
@@ -18618,7 +18604,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18618
18604
  );
18619
18605
  tokenList.appendChild(tokenItem);
18620
18606
  });
18621
- }
18607
+ },
18622
18608
 
18623
18609
  _effectiveAllowedTokens() {
18624
18610
  const keyTokens =
@@ -18628,17 +18614,64 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18628
18614
  if (!keyTokens) return configTokens;
18629
18615
  if (!configTokens) return keyTokens;
18630
18616
  return keyTokens.filter((t) => configTokens.includes(t));
18631
- }
18617
+ },
18618
+
18619
+ async _handleNetworkSelection(key, network) {
18620
+ this.state.selectedNetwork = key;
18621
+ this.state.selectedNetworkName = network.name;
18622
+ this.state.selectedChainId = network.chainId;
18623
+ this.state.selectedRpcUrl = network.rpcUrl;
18624
+ this.state.selectedContractAddress = network.contractAddress;
18625
+ this.state.selectedToken = null;
18626
+ this.state.selectedTokenConfig = null;
18627
+
18628
+ const nameEl = document.getElementById("kwespay-selectedNetworkName");
18629
+ if (nameEl)
18630
+ nameEl.textContent = `${network.name} ${
18631
+ network.type === "mainnet" ? "Mainnet" : "Testnet"
18632
+ }`;
18633
+
18634
+ const iconEl = document.getElementById("kwespay-selectedNetworkIcon");
18635
+ if (iconEl)
18636
+ iconEl.innerHTML = `<img src="${network.logo}" alt="${network.name}" />`;
18632
18637
 
18633
- // ── Wallet connection flow ───────────────────────────────────────────────────
18638
+ const continueBtn = document.getElementById(
18639
+ "kwespay-continueToWalletConnect"
18640
+ );
18641
+ if (continueBtn) continueBtn.disabled = true;
18634
18642
 
18643
+ this._renderTokenList();
18644
+ this._goToStep(2);
18645
+ },
18646
+
18647
+ _handleTokenSelection(token) {
18648
+ document
18649
+ .querySelectorAll("#kwespay-tokenList .token-item")
18650
+ .forEach((item) => item.classList.remove("selected"));
18651
+ document
18652
+ .querySelector(
18653
+ `#kwespay-tokenList .token-item[data-token-symbol="${token.symbol}"]`
18654
+ )
18655
+ ?.classList.add("selected");
18656
+
18657
+ this.state.selectedToken = token.symbol;
18658
+ this.state.selectedTokenConfig = token;
18659
+
18660
+ const continueBtn = document.getElementById(
18661
+ "kwespay-continueToWalletConnect"
18662
+ );
18663
+ if (continueBtn) continueBtn.disabled = false;
18664
+ },
18665
+ };
18666
+
18667
+ const WalletMethods = {
18635
18668
  async _handleWalletConnection() {
18636
18669
  if (!this.state.selectedToken || !this.state.selectedTokenConfig) {
18637
18670
  alert("Please select a token first");
18638
18671
  return;
18639
18672
  }
18640
18673
  this._renderWalletPickerStep();
18641
- }
18674
+ },
18642
18675
 
18643
18676
  _renderWalletPickerStep() {
18644
18677
  this._removeCustomStep("kwespay-step-wallet-picker");
@@ -18736,7 +18769,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18736
18769
  list.appendChild(item);
18737
18770
  });
18738
18771
  });
18739
- }
18772
+ },
18740
18773
 
18741
18774
  async _connectInjectedProvider(index) {
18742
18775
  try {
@@ -18751,7 +18784,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18751
18784
  );
18752
18785
  dispatchWidgetEvent("walletConnectionError", { error: err.message });
18753
18786
  }
18754
- }
18787
+ },
18755
18788
 
18756
18789
  async _startWalletConnect() {
18757
18790
  this._renderWCStep();
@@ -18767,7 +18800,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18767
18800
  this._renderWCMobileStep();
18768
18801
  }
18769
18802
 
18770
- // Pass the target chain so MetaMask starts the WC session on the right
18803
+ // Pass the target chain so the wallet starts the WC session on the right
18771
18804
  // network — without this it defaults to its last-used WC chain (Mainnet).
18772
18805
  await this.walletService.connectWalletConnect(this.state.selectedChainId);
18773
18806
  } catch (err) {
@@ -18782,9 +18815,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18782
18815
  err.message || "WalletConnect failed."
18783
18816
  );
18784
18817
  }
18785
- }
18786
-
18787
- // ── WalletConnect UI ─────────────────────────────────────────────────────────
18818
+ },
18788
18819
 
18789
18820
  _renderWCStep() {
18790
18821
  this._removeCustomStep("kwespay-step-wc");
@@ -18817,7 +18848,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18817
18848
  });
18818
18849
 
18819
18850
  this._goToStep("wc");
18820
- }
18851
+ },
18821
18852
 
18822
18853
  _wcDesktopHTML() {
18823
18854
  return `
@@ -18839,7 +18870,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18839
18870
  <p style="font-size:11px;color:var(--kp-muted);font-family:var(--kp-mono);text-align:center">Waiting for wallet connection…</p>
18840
18871
  </div>
18841
18872
  `;
18842
- }
18873
+ },
18843
18874
 
18844
18875
  _wcMobileHTML() {
18845
18876
  const wallets = this.walletService.getMobileWallets();
@@ -18887,7 +18918,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18887
18918
  </p>
18888
18919
  </div>
18889
18920
  `;
18890
- }
18921
+ },
18891
18922
 
18892
18923
  _renderWCMobileStep() {
18893
18924
  const list = document.getElementById("kwespay-mobile-wallet-list");
@@ -18911,7 +18942,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18911
18942
  }
18912
18943
  });
18913
18944
  });
18914
- }
18945
+ },
18915
18946
 
18916
18947
  _onWCUri(uri) {
18917
18948
  this.state.wcUri = uri;
@@ -18928,7 +18959,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18928
18959
  } else {
18929
18960
  this._renderQRCode(uri);
18930
18961
  }
18931
- }
18962
+ },
18932
18963
 
18933
18964
  _renderQRCode(uri) {
18934
18965
  const canvas = document.getElementById("kwespay-qr-canvas");
@@ -18963,18 +18994,18 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18963
18994
  <p style="font-size:10px;color:#888;font-family:monospace">Use Copy URI button</p>
18964
18995
  </div>
18965
18996
  `;
18966
- }
18997
+ },
18967
18998
 
18968
18999
  _onWCConnected() {
18969
19000
  const connection = this.walletService._connectionResult();
18970
19001
  this._removeCustomStep("kwespay-step-wc");
18971
19002
  this._onWalletConnected(connection);
18972
- }
19003
+ },
18973
19004
 
18974
19005
  _onWCDisconnected() {
18975
19006
  this._removeCustomStep("kwespay-step-wc");
18976
19007
  this._goToStep(2);
18977
- }
19008
+ },
18978
19009
 
18979
19010
  async _onWalletConnected(connection) {
18980
19011
  const addressEl = document.getElementById("kwespay-connectedWalletAddress");
@@ -18991,7 +19022,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
18991
19022
  const proceedBtn = document.getElementById("kwespay-proceedToPayment");
18992
19023
 
18993
19024
  if (cryptoLine) {
18994
- cryptoLine.textContent = "Getting the best rate for you…";
19025
+ cryptoLine.textContent = "loading…";
18995
19026
  cryptoLine.classList.add("loading");
18996
19027
  }
18997
19028
  if (timerEl) timerEl.style.display = "none";
@@ -19013,83 +19044,39 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19013
19044
  }
19014
19045
 
19015
19046
  dispatchWidgetEvent("walletConnected", { address: connection.address });
19016
- }
19017
-
19018
- // ── API key validation ───────────────────────────────────────────────────────
19019
-
19020
- async _validateAPIKey() {
19021
- try {
19022
- const validation = await this.paymentService.validateAPIKey();
19023
- if (validation.valid) {
19024
- this.state.vendorInfo = validation.vendorInfo;
19025
- this.state.keyAllowedNetworks = validation.allowedNetworks ?? null;
19026
- this.state.keyAllowedTokens = validation.allowedTokens ?? null;
19027
- this._goToStep(1);
19028
- dispatchWidgetEvent("apiKeyValidated", {
19029
- vendorInfo: this.state.vendorInfo,
19030
- });
19031
- } else {
19032
- this._goToStep(0.5);
19033
- dispatchWidgetEvent("apiKeyInvalid", {});
19034
- }
19035
- } catch (err) {
19036
- console.error("[KwesPayWidget] API key validation error:", err.message);
19037
- this._goToStep(0.5);
19038
- dispatchWidgetEvent("apiKeyError", { error: err.message });
19039
- }
19040
- }
19041
-
19042
- // ── Network / token selection ────────────────────────────────────────────────
19047
+ },
19048
+ };
19043
19049
 
19044
- async _handleNetworkSelection(key, network) {
19045
- this.state.selectedNetwork = key;
19046
- this.state.selectedNetworkName = network.name;
19047
- this.state.selectedChainId = network.chainId;
19048
- this.state.selectedRpcUrl = network.rpcUrl;
19049
- this.state.selectedContractAddress = network.contractAddress;
19050
- this.state.selectedToken = null;
19051
- this.state.selectedTokenConfig = null;
19050
+ const PLATFORM_FEE_BPS = 25;
19052
19051
 
19053
- const nameEl = document.getElementById("kwespay-selectedNetworkName");
19054
- if (nameEl)
19055
- nameEl.textContent = `${network.name} ${
19056
- network.type === "mainnet" ? "Mainnet" : "Testnet"
19057
- }`;
19052
+ function formatUnits$1(rawBigInt, decimals) {
19053
+ const divisor = BigInt(10 ** decimals);
19054
+ const whole = rawBigInt / divisor;
19055
+ const remainder = rawBigInt % divisor;
19058
19056
 
19059
- const iconEl = document.getElementById("kwespay-selectedNetworkIcon");
19060
- if (iconEl)
19061
- iconEl.innerHTML = `<img src="${network.logo}" alt="${network.name}" />`;
19057
+ if (remainder === 0n) return `${whole}`;
19062
19058
 
19063
- const continueBtn = document.getElementById(
19064
- "kwespay-continueToWalletConnect"
19065
- );
19066
- if (continueBtn) continueBtn.disabled = true;
19059
+ const fracFull = remainder.toString().padStart(decimals, "0");
19067
19060
 
19068
- this._renderTokenList();
19069
- this._goToStep(2);
19061
+ if (whole > 0n) {
19062
+ const frac = fracFull.slice(0, 4).replace(/0+$/, "");
19063
+ return frac ? `${whole}.${frac}` : `${whole}`;
19070
19064
  }
19071
19065
 
19072
- _handleTokenSelection(token) {
19073
- document
19074
- .querySelectorAll("#kwespay-tokenList .token-item")
19075
- .forEach((item) => item.classList.remove("selected"));
19076
- document
19077
- .querySelector(
19078
- `#kwespay-tokenList .token-item[data-token-symbol="${token.symbol}"]`
19079
- )
19080
- ?.classList.add("selected");
19081
-
19082
- this.state.selectedToken = token.symbol;
19083
- this.state.selectedTokenConfig = token;
19084
-
19085
- const continueBtn = document.getElementById(
19086
- "kwespay-continueToWalletConnect"
19087
- );
19088
- if (continueBtn) continueBtn.disabled = false;
19066
+ let firstSig = -1;
19067
+ for (let i = 0; i < fracFull.length; i++) {
19068
+ if (fracFull[i] !== "0") {
19069
+ firstSig = i;
19070
+ break;
19071
+ }
19089
19072
  }
19073
+ if (firstSig === -1) return `${whole}`;
19090
19074
 
19091
- // ── Quote / review step ──────────────────────────────────────────────────────
19075
+ const sigSlice = fracFull.slice(firstSig, firstSig + 4).replace(/0+$/, "");
19076
+ return `0.${fracFull.slice(0, firstSig) + sigSlice}`;
19077
+ }
19092
19078
 
19079
+ const QuoteMethods = {
19093
19080
  async _loadReviewStep() {
19094
19081
  this._clearQuoteTimer();
19095
19082
 
@@ -19101,7 +19088,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19101
19088
  const proceedBtn = document.getElementById("kwespay-proceedToPayment");
19102
19089
 
19103
19090
  if (cryptoLine) {
19104
- cryptoLine.textContent = "Getting the best rate for you…";
19091
+ cryptoLine.textContent = "loading…";
19105
19092
  cryptoLine.classList.add("loading");
19106
19093
  }
19107
19094
  if (timerEl) timerEl.style.display = "none";
@@ -19141,12 +19128,12 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19141
19128
  } catch (err) {
19142
19129
  console.error("[KwesPayWidget] Quote fetch failed:", err.message);
19143
19130
  if (cryptoLine) {
19144
- cryptoLine.textContent = "Could not load rate — please try again.";
19131
+ cryptoLine.textContent = "Could not load please try again.";
19145
19132
  cryptoLine.classList.remove("loading");
19146
19133
  }
19147
19134
  if (proceedBtn) proceedBtn.disabled = true;
19148
19135
  }
19149
- }
19136
+ },
19150
19137
 
19151
19138
  _startQuoteTimer(expiresAt) {
19152
19139
  const timerEl = document.getElementById("kwespay-quoteTimer");
@@ -19167,6 +19154,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19167
19154
  "kp-quote-timer" +
19168
19155
  (secs <= 30 ? " urgent" : "") +
19169
19156
  (secs <= 0 ? " expired" : "");
19157
+
19170
19158
  if (secs <= 0) {
19171
19159
  timerText.textContent = "Refreshing your rate…";
19172
19160
  const proceedBtn = document.getElementById("kwespay-proceedToPayment");
@@ -19175,19 +19163,20 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19175
19163
  this._loadReviewStep();
19176
19164
  }
19177
19165
  };
19166
+
19178
19167
  update();
19179
19168
  this.state.quoteTimerInterval = setInterval(update, 1000);
19180
- }
19169
+ },
19181
19170
 
19182
19171
  _clearQuoteTimer() {
19183
19172
  if (this.state.quoteTimerInterval) {
19184
19173
  clearInterval(this.state.quoteTimerInterval);
19185
19174
  this.state.quoteTimerInterval = null;
19186
19175
  }
19187
- }
19188
-
19189
- // ── Payment processing ───────────────────────────────────────────────────────
19176
+ },
19177
+ };
19190
19178
 
19179
+ const PaymentMethods = {
19191
19180
  async _handlePaymentProcessing() {
19192
19181
  if (!this.state.currentPayload) {
19193
19182
  this._showError(
@@ -19216,9 +19205,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19216
19205
 
19217
19206
  if (!provider) throw new Error("No wallet provider");
19218
19207
 
19219
- // ── Session liveness check ───────────────────────────────────────────────
19220
- // Must come first — a stale WC session throws on any RPC call, and we want
19221
- // a clean "reconnect" error rather than a cryptic provider error downstream.
19208
+
19222
19209
  const alive = await this.walletService.isSessionAlive();
19223
19210
  if (!alive) {
19224
19211
  console.error(
@@ -19232,22 +19219,17 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19232
19219
  throw err;
19233
19220
  }
19234
19221
 
19235
- // ── Show mobile helper banner ────────────────────────────────────────────
19222
+
19236
19223
  if (isMobile) {
19237
19224
  document
19238
19225
  .getElementById("kwespay-mobileTransactionInstruction")
19239
19226
  ?.style.setProperty("display", "flex");
19240
19227
  }
19241
19228
 
19242
- // ── Chain validation ─────────────────────────────────────────────────────
19243
19229
  if (strictMobile) {
19244
- // On mobile WC, eth_chainId goes to the wallet via the WC relay and
19245
- // reflects the wallet's actual active chain — always call it live.
19246
- // Never rely on session namespace account ordering (MetaMask puts
19247
- // Mainnet first regardless of which chain the user is on).
19230
+
19248
19231
  await this._assertMobileChain(provider, targetChainId);
19249
19232
  } else {
19250
- // Desktop / injected — eth_chainId is reliable, switch if needed
19251
19233
  const rawChain = await provider.request({ method: "eth_chainId" });
19252
19234
  const currentChainId = parseInt(rawChain, 16);
19253
19235
 
@@ -19268,21 +19250,17 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19268
19250
  this.state.selectedToken,
19269
19251
  this.state.selectedTokenConfig.decimals
19270
19252
  );
19271
- console.log("[KwesPay] Network switched");
19253
+ console.log("[KwesPay] Network switched");
19272
19254
  }
19273
19255
  }
19274
19256
 
19275
- // ── Open wallet BEFORE sending tx (mobile WC only) ──────────────────────
19276
- // We deep-link to the wallet before the JSON-RPC request lands so the
19277
- // approval prompt appears immediately without the user having to manually
19278
- // switch apps.
19257
+
19279
19258
  if (strictMobile) {
19280
19259
  setStatus(
19281
19260
  "Opening your wallet…",
19282
19261
  "Approve the payment in your wallet."
19283
19262
  );
19284
19263
  this.walletService._openWalletForApproval();
19285
- // Give the OS time to foreground the wallet app before the RPC lands
19286
19264
  await new Promise((r) => setTimeout(r, 700));
19287
19265
  } else {
19288
19266
  setStatus(
@@ -19291,19 +19269,18 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19291
19269
  );
19292
19270
  }
19293
19271
 
19294
- // ── Send transaction ─────────────────────────────────────────────────────
19295
19272
  const receipt = await this.paymentService.createPayment({
19296
19273
  payload: this.state.currentPayload,
19297
19274
  walletProvider: provider,
19298
19275
  onStatusUpdate: setStatus,
19299
19276
  });
19300
19277
 
19301
- // ── Cleanup ──────────────────────────────────────────────────────────────
19278
+
19302
19279
  document
19303
19280
  .getElementById("kwespay-mobileTransactionInstruction")
19304
19281
  ?.style.setProperty("display", "none");
19305
19282
 
19306
- // ── Success UI ───────────────────────────────────────────────────────────
19283
+
19307
19284
  const decimals = this.state.selectedTokenConfig?.decimals ?? 6;
19308
19285
  const amountBig = BigInt(this.state.currentPayload.amountBaseUnits);
19309
19286
  const cryptoDisplay = `${formatUnits$1(amountBig, decimals)} ${
@@ -19366,7 +19343,7 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19366
19343
  this._showError(title, message);
19367
19344
  dispatchWidgetEvent("paymentError", { error: message, errorType });
19368
19345
  }
19369
- }
19346
+ },
19370
19347
 
19371
19348
  /**
19372
19349
  * Confirm the wallet is on the target chain before sending a transaction.
@@ -19374,9 +19351,6 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19374
19351
  * Throws WRONG_NETWORK if the chain never matches.
19375
19352
  *
19376
19353
  * MOBILE WC ONLY — never call this on desktop/injected.
19377
- *
19378
- * @param {object} provider
19379
- * @param {number} targetChainId
19380
19354
  */
19381
19355
  async _assertMobileChain(provider, targetChainId) {
19382
19356
  const MAX_ATTEMPTS = 3;
@@ -19410,25 +19384,14 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19410
19384
  }
19411
19385
  }
19412
19386
 
19413
- // All attempts failed — wallet is on wrong chain
19414
19387
  const err = new Error(
19415
19388
  `Please switch to ${this.state.selectedNetworkName} in your wallet and try again.`
19416
19389
  );
19417
19390
  err.code = "WRONG_NETWORK";
19418
19391
  throw err;
19419
- }
19392
+ },
19393
+
19420
19394
 
19421
- /**
19422
- * Switch network for desktop/injected providers and poll until confirmed.
19423
- * MUST NOT be called in strictMobile mode (mobile WC doesn't support
19424
- * wallet_switchEthereumChain reliably).
19425
- *
19426
- * @param {number} chainId
19427
- * @param {string} networkName
19428
- * @param {string} rpcUrl
19429
- * @param {string} tokenSymbol
19430
- * @param {number} tokenDecimals
19431
- */
19432
19395
  async _switchNetworkSafe(
19433
19396
  chainId,
19434
19397
  networkName,
@@ -19436,7 +19399,6 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19436
19399
  tokenSymbol,
19437
19400
  tokenDecimals
19438
19401
  ) {
19439
- // Capture before any async boundary — prevents `this` loss in callbacks
19440
19402
  const switchNetwork = this.walletService.switchNetwork;
19441
19403
  const provider = this.walletService.getProvider();
19442
19404
 
@@ -19459,12 +19421,11 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19459
19421
 
19460
19422
  const targetHex = toHex(chainId);
19461
19423
 
19462
- // Check current chain — skip if already correct
19463
19424
  try {
19464
19425
  const currentHex = toHex(
19465
19426
  await provider.request({ method: "eth_chainId" })
19466
19427
  );
19467
- if (currentHex && currentHex === targetHex) return; // already correct, silent return
19428
+ if (currentHex && currentHex === targetHex) return;
19468
19429
  } catch (err) {
19469
19430
  console.warn(
19470
19431
  "[KwesPay] Could not read chainId before switch:",
@@ -19472,7 +19433,6 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19472
19433
  );
19473
19434
  }
19474
19435
 
19475
- // Issue the switch
19476
19436
  try {
19477
19437
  await switchNetwork(
19478
19438
  chainId,
@@ -19518,44 +19478,67 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19518
19478
  `Could not confirm network switch to ${networkName} after 15s. ` +
19519
19479
  `Please switch manually in your wallet and try again.`
19520
19480
  );
19521
- }
19481
+ },
19482
+ };
19483
+
19484
+ function resolveAcceptedTokens(input) {
19485
+ if (!input) return null;
19486
+ if (input === "stablecoins") return STABLECOIN_SYMBOLS;
19487
+ if (Array.isArray(input) && input.length)
19488
+ return input.map((t) => t.toUpperCase());
19489
+ return null;
19490
+ }
19522
19491
 
19523
- // ── Step navigation ──────────────────────────────────────────────────────────
19492
+ class KwesPayWidget {
19493
+ constructor(config) {
19494
+ if (!config.apiKey) throw new Error("[KwesPayWidget] apiKey is required");
19495
+ if (!config.vendorId)
19496
+ throw new Error("[KwesPayWidget] vendorId is required");
19497
+ if (!config.amount || parseFloat(config.amount) <= 0)
19498
+ throw new Error("[KwesPayWidget] Valid amount is required");
19524
19499
 
19525
- _goToStep(stepNumber) {
19526
- document
19527
- .querySelectorAll(".kwespay-container .step")
19528
- .forEach((s) => s.classList.remove("active", "exiting"));
19529
- this._activateStep(stepNumber);
19530
- this.state.currentStep = stepNumber;
19531
- }
19500
+ this.config = {
19501
+ apiKey: config.apiKey,
19502
+ vendorId: config.vendorId,
19503
+ amount: parseFloat(config.amount),
19504
+ currency: config.currency || DEFAULT_CONFIG.currency,
19505
+ graphqlEndpoint: DEFAULT_CONFIG.graphqlEndpoint,
19506
+ acceptedTokens: resolveAcceptedTokens(config.acceptedTokens),
19507
+ };
19532
19508
 
19533
- _activateStep(stepNumber) {
19534
- let targetStep;
19535
- if (stepNumber === 0.5)
19536
- targetStep = document.getElementById("kwespay-step0-invalid");
19537
- else if (typeof stepNumber === "string")
19538
- targetStep = document.getElementById(`kwespay-step-${stepNumber}`);
19539
- else targetStep = document.getElementById(`kwespay-step${stepNumber}`);
19509
+ if (!Object.values(SUPPORTED_CURRENCIES).includes(this.config.currency)) {
19510
+ throw new Error(
19511
+ `[KwesPayWidget] Unsupported currency: ${this.config.currency}`
19512
+ );
19513
+ }
19540
19514
 
19541
- if (targetStep) targetStep.classList.add("active");
19542
- else console.warn(`[KwesPayWidget] Step not found: ${stepNumber}`);
19543
- }
19515
+ this.walletService = new WalletService();
19516
+ this.paymentService = new PaymentService$1(
19517
+ this.config.apiKey,
19518
+ this.config.graphqlEndpoint
19519
+ );
19544
19520
 
19545
- _removeCustomStep(id) {
19546
- document.getElementById(id)?.remove();
19547
- }
19521
+ this.state = {
19522
+ isOpen: false,
19523
+ currentStep: 0,
19524
+ selectedNetwork: null,
19525
+ selectedNetworkName: "",
19526
+ selectedChainId: null,
19527
+ selectedRpcUrl: null,
19528
+ selectedContractAddress: null,
19529
+ selectedToken: null,
19530
+ selectedTokenConfig: null,
19531
+ vendorInfo: null,
19532
+ keyAllowedNetworks: null,
19533
+ keyAllowedTokens: null,
19534
+ currentPayload: null,
19535
+ quoteTimerInterval: null,
19536
+ wcUri: null,
19537
+ };
19548
19538
 
19549
- _showError(title, message) {
19550
- const titleEl = document.getElementById("kwespay-errorTitle");
19551
- const msgEl = document.getElementById("kwespay-errorMessage");
19552
- if (titleEl) titleEl.textContent = title;
19553
- if (msgEl) msgEl.textContent = message;
19554
- this._goToStep(6);
19539
+ this._init();
19555
19540
  }
19556
19541
 
19557
- // ── Public API ───────────────────────────────────────────────────────────────
19558
-
19559
19542
  async open() {
19560
19543
  const overlay = document.getElementById("kwespay-widget-overlay");
19561
19544
  const container = document.getElementById("kwespay-widget-container");
@@ -19631,21 +19614,6 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19631
19614
  return { ...this.state, config: { ...this.config } };
19632
19615
  }
19633
19616
 
19634
- _reset() {
19635
- this._clearQuoteTimer();
19636
- this._removeCustomStep("kwespay-step-wallet-picker");
19637
- this._removeCustomStep("kwespay-step-wc");
19638
- this.state.selectedNetwork = null;
19639
- this.state.selectedNetworkName = "";
19640
- this.state.selectedChainId = null;
19641
- this.state.selectedRpcUrl = null;
19642
- this.state.selectedContractAddress = null;
19643
- this.state.selectedToken = null;
19644
- this.state.selectedTokenConfig = null;
19645
- this.state.currentPayload = null;
19646
- this.state.wcUri = null;
19647
- }
19648
-
19649
19617
  destroy() {
19650
19618
  this._clearQuoteTimer();
19651
19619
  document.body.classList.remove("kwespay-open");
@@ -19660,6 +19628,17 @@ ${e.length}`,n=new TextEncoder().encode(t+e);return "0x"+toString$1(keccak_256$3
19660
19628
  }
19661
19629
  }
19662
19630
 
19631
+ Object.assign(
19632
+ KwesPayWidget.prototype,
19633
+ DomMethods,
19634
+ NavMethods,
19635
+ APIKeyMethods,
19636
+ NetworkMethods,
19637
+ WalletMethods,
19638
+ QuoteMethods,
19639
+ PaymentMethods
19640
+ );
19641
+
19663
19642
  /**
19664
19643
  * KwesPay Widget - Main entry point
19665
19644
  * @module @kwespay/widget