@kwespay/widget 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.MD +159 -0
- package/dist/esm/index.js +337 -357
- package/dist/kwespay-widget.js +337 -357
- package/dist/kwespay-widget.min.js +550 -550
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -97,6 +97,7 @@ const TOKEN_CONFIGS = {
|
|
|
97
97
|
decimals: 18,
|
|
98
98
|
coingeckoId: "ethereum",
|
|
99
99
|
binanceSymbol: "ETHUSDT",
|
|
100
|
+
|
|
100
101
|
},
|
|
101
102
|
{
|
|
102
103
|
symbol: "USDT",
|
|
@@ -1006,6 +1007,84 @@ class PaymentService {
|
|
|
1006
1007
|
}
|
|
1007
1008
|
}
|
|
1008
1009
|
|
|
1010
|
+
function isMobileDevice() {
|
|
1011
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
1012
|
+
navigator.userAgent
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function dispatchWidgetEvent(eventName, detail = {}) {
|
|
1017
|
+
const event = new CustomEvent(`kwespay:${eventName}`, {
|
|
1018
|
+
detail,
|
|
1019
|
+
bubbles: true,
|
|
1020
|
+
cancelable: true,
|
|
1021
|
+
});
|
|
1022
|
+
window.dispatchEvent(event);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function truncateHash(hash, startChars = 10, endChars = 8) {
|
|
1026
|
+
if (!hash) return "";
|
|
1027
|
+
return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
function getErrorType(error) {
|
|
1031
|
+
const msg = error?.message ?? "";
|
|
1032
|
+
if (
|
|
1033
|
+
error?.code === 4001 ||
|
|
1034
|
+
error?.code === "ACTION_REJECTED" ||
|
|
1035
|
+
msg.includes("rejected") ||
|
|
1036
|
+
msg.includes("denied") ||
|
|
1037
|
+
msg.includes("cancelled")
|
|
1038
|
+
)
|
|
1039
|
+
return "USER_REJECTED";
|
|
1040
|
+
if (msg.toLowerCase().includes("insufficient")) return "INSUFFICIENT_BALANCE";
|
|
1041
|
+
if (
|
|
1042
|
+
msg.includes("User rejected") ||
|
|
1043
|
+
msg.includes("User closed modal") ||
|
|
1044
|
+
msg.includes("Connection request reset")
|
|
1045
|
+
)
|
|
1046
|
+
return "CONNECTION_REJECTED";
|
|
1047
|
+
if (msg.includes("timeout")) return "TIMEOUT";
|
|
1048
|
+
return "UNKNOWN";
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function getErrorMessage(error, context = {}) {
|
|
1052
|
+
const type = getErrorType(error);
|
|
1053
|
+
|
|
1054
|
+
console.error("[KwesPay] Payment error breakdown:", {
|
|
1055
|
+
errorType: type,
|
|
1056
|
+
message: error?.message,
|
|
1057
|
+
code: error?.code,
|
|
1058
|
+
data: error?.data,
|
|
1059
|
+
reason: error?.reason,
|
|
1060
|
+
stack: error?.stack,
|
|
1061
|
+
raw: error,
|
|
1062
|
+
context,
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
if (error?.data) console.error("[KwesPay] Contract revert data:", error.data);
|
|
1066
|
+
if (error?.transaction)
|
|
1067
|
+
console.error("[KwesPay] Failed tx details:", error.transaction);
|
|
1068
|
+
if (error?.receipt) console.error("[KwesPay] Tx receipt:", error.receipt);
|
|
1069
|
+
|
|
1070
|
+
switch (type) {
|
|
1071
|
+
case "USER_REJECTED":
|
|
1072
|
+
return "You cancelled the transaction in your wallet.";
|
|
1073
|
+
case "INSUFFICIENT_BALANCE":
|
|
1074
|
+
return `Not enough ${context.token || "funds"} to complete this payment.`;
|
|
1075
|
+
case "CONNECTION_REJECTED":
|
|
1076
|
+
return "Wallet connection was cancelled.";
|
|
1077
|
+
case "TIMEOUT":
|
|
1078
|
+
return "Connection timed out. Please try again.";
|
|
1079
|
+
default:
|
|
1080
|
+
return (
|
|
1081
|
+
error?.reason ||
|
|
1082
|
+
error?.message ||
|
|
1083
|
+
"Something went wrong while processing your payment."
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1009
1088
|
const WIDGET_STYLES = `
|
|
1010
1089
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
1011
1090
|
|
|
@@ -2195,180 +2274,12 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2195
2274
|
`;
|
|
2196
2275
|
}
|
|
2197
2276
|
|
|
2198
|
-
|
|
2199
|
-
const event = new CustomEvent(`kwespay:${eventName}`, {
|
|
2200
|
-
detail,
|
|
2201
|
-
bubbles: true,
|
|
2202
|
-
cancelable: true,
|
|
2203
|
-
});
|
|
2204
|
-
window.dispatchEvent(event);
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
|
-
function truncateHash(hash, startChars = 10, endChars = 8) {
|
|
2208
|
-
if (!hash) return "";
|
|
2209
|
-
return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
|
|
2210
|
-
}
|
|
2211
|
-
|
|
2212
|
-
function getErrorType(error) {
|
|
2213
|
-
const msg = error?.message ?? "";
|
|
2214
|
-
if (
|
|
2215
|
-
error?.code === 4001 ||
|
|
2216
|
-
error?.code === "ACTION_REJECTED" ||
|
|
2217
|
-
msg.includes("rejected") ||
|
|
2218
|
-
msg.includes("denied") ||
|
|
2219
|
-
msg.includes("cancelled")
|
|
2220
|
-
)
|
|
2221
|
-
return "USER_REJECTED";
|
|
2222
|
-
if (msg.toLowerCase().includes("insufficient")) return "INSUFFICIENT_BALANCE";
|
|
2223
|
-
if (
|
|
2224
|
-
msg.includes("User rejected") ||
|
|
2225
|
-
msg.includes("User closed modal") ||
|
|
2226
|
-
msg.includes("Connection request reset")
|
|
2227
|
-
)
|
|
2228
|
-
return "CONNECTION_REJECTED";
|
|
2229
|
-
if (msg.includes("timeout")) return "TIMEOUT";
|
|
2230
|
-
return "UNKNOWN";
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
function getErrorMessage(error, context = {}) {
|
|
2234
|
-
const type = getErrorType(error);
|
|
2235
|
-
|
|
2236
|
-
console.error("[KwesPay] Payment error breakdown:", {
|
|
2237
|
-
errorType: type,
|
|
2238
|
-
message: error?.message,
|
|
2239
|
-
code: error?.code,
|
|
2240
|
-
data: error?.data,
|
|
2241
|
-
reason: error?.reason,
|
|
2242
|
-
stack: error?.stack,
|
|
2243
|
-
raw: error,
|
|
2244
|
-
context,
|
|
2245
|
-
});
|
|
2246
|
-
|
|
2247
|
-
if (error?.data) console.error("[KwesPay] Contract revert data:", error.data);
|
|
2248
|
-
if (error?.transaction)
|
|
2249
|
-
console.error("[KwesPay] Failed tx details:", error.transaction);
|
|
2250
|
-
if (error?.receipt) console.error("[KwesPay] Tx receipt:", error.receipt);
|
|
2251
|
-
|
|
2252
|
-
switch (type) {
|
|
2253
|
-
case "USER_REJECTED":
|
|
2254
|
-
return "You cancelled the transaction in your wallet.";
|
|
2255
|
-
case "INSUFFICIENT_BALANCE":
|
|
2256
|
-
return `Not enough ${context.token || "funds"} to complete this payment.`;
|
|
2257
|
-
case "CONNECTION_REJECTED":
|
|
2258
|
-
return "Wallet connection was cancelled.";
|
|
2259
|
-
case "TIMEOUT":
|
|
2260
|
-
return "Connection timed out. Please try again.";
|
|
2261
|
-
default:
|
|
2262
|
-
return (
|
|
2263
|
-
error?.reason ||
|
|
2264
|
-
error?.message ||
|
|
2265
|
-
"Something went wrong while processing your payment."
|
|
2266
|
-
);
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
|
-
const PLATFORM_FEE_BPS = 25;
|
|
2271
|
-
|
|
2272
|
-
// ── Utilities ─────────────────────────────────────────────────────────────────
|
|
2273
|
-
|
|
2274
|
-
function formatUnits(rawBigInt, decimals) {
|
|
2275
|
-
const divisor = BigInt(10 ** decimals);
|
|
2276
|
-
const whole = rawBigInt / divisor;
|
|
2277
|
-
const remainder = rawBigInt % divisor;
|
|
2278
|
-
|
|
2279
|
-
if (remainder === 0n) return `${whole}`;
|
|
2280
|
-
|
|
2281
|
-
const fracFull = remainder.toString().padStart(decimals, "0");
|
|
2282
|
-
|
|
2283
|
-
if (whole > 0n) {
|
|
2284
|
-
const frac = fracFull.slice(0, 4).replace(/0+$/, "");
|
|
2285
|
-
return frac ? `${whole}.${frac}` : `${whole}`;
|
|
2286
|
-
}
|
|
2287
|
-
|
|
2288
|
-
let firstSig = -1;
|
|
2289
|
-
for (let i = 0; i < fracFull.length; i++) {
|
|
2290
|
-
if (fracFull[i] !== "0") {
|
|
2291
|
-
firstSig = i;
|
|
2292
|
-
break;
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2295
|
-
if (firstSig === -1) return `${whole}`;
|
|
2296
|
-
|
|
2297
|
-
const sigSlice = fracFull.slice(firstSig, firstSig + 4).replace(/0+$/, "");
|
|
2298
|
-
return `0.${fracFull.slice(0, firstSig) + sigSlice}`;
|
|
2299
|
-
}
|
|
2300
|
-
|
|
2301
|
-
function resolveAcceptedTokens(input) {
|
|
2302
|
-
if (!input) return null;
|
|
2303
|
-
if (input === "stablecoins") return STABLECOIN_SYMBOLS;
|
|
2304
|
-
if (Array.isArray(input) && input.length)
|
|
2305
|
-
return input.map((t) => t.toUpperCase());
|
|
2306
|
-
return null;
|
|
2307
|
-
}
|
|
2308
|
-
|
|
2309
|
-
function isMobileDevice() {
|
|
2310
|
-
return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
|
2311
|
-
}
|
|
2312
|
-
|
|
2313
|
-
// ── Widget ────────────────────────────────────────────────────────────────────
|
|
2314
|
-
|
|
2315
|
-
class KwesPayWidget {
|
|
2316
|
-
constructor(config) {
|
|
2317
|
-
if (!config.apiKey) throw new Error("[KwesPayWidget] apiKey is required");
|
|
2318
|
-
if (!config.vendorId)
|
|
2319
|
-
throw new Error("[KwesPayWidget] vendorId is required");
|
|
2320
|
-
if (!config.amount || parseFloat(config.amount) <= 0)
|
|
2321
|
-
throw new Error("[KwesPayWidget] Valid amount is required");
|
|
2322
|
-
|
|
2323
|
-
this.config = {
|
|
2324
|
-
apiKey: config.apiKey,
|
|
2325
|
-
vendorId: config.vendorId,
|
|
2326
|
-
amount: parseFloat(config.amount),
|
|
2327
|
-
currency: config.currency || DEFAULT_CONFIG.currency,
|
|
2328
|
-
graphqlEndpoint: DEFAULT_CONFIG.graphqlEndpoint,
|
|
2329
|
-
acceptedTokens: resolveAcceptedTokens(config.acceptedTokens),
|
|
2330
|
-
};
|
|
2331
|
-
|
|
2332
|
-
if (!Object.values(SUPPORTED_CURRENCIES).includes(this.config.currency)) {
|
|
2333
|
-
throw new Error(
|
|
2334
|
-
`[KwesPayWidget] Unsupported currency: ${this.config.currency}`
|
|
2335
|
-
);
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
this.walletService = new WalletService();
|
|
2339
|
-
this.paymentService = new PaymentService(
|
|
2340
|
-
this.config.apiKey,
|
|
2341
|
-
this.config.graphqlEndpoint
|
|
2342
|
-
);
|
|
2343
|
-
|
|
2344
|
-
this.state = {
|
|
2345
|
-
isOpen: false,
|
|
2346
|
-
currentStep: 0,
|
|
2347
|
-
selectedNetwork: null,
|
|
2348
|
-
selectedNetworkName: "",
|
|
2349
|
-
selectedChainId: null,
|
|
2350
|
-
selectedRpcUrl: null,
|
|
2351
|
-
selectedContractAddress: null,
|
|
2352
|
-
selectedToken: null,
|
|
2353
|
-
selectedTokenConfig: null,
|
|
2354
|
-
vendorInfo: null,
|
|
2355
|
-
keyAllowedNetworks: null,
|
|
2356
|
-
keyAllowedTokens: null,
|
|
2357
|
-
currentPayload: null,
|
|
2358
|
-
quoteTimerInterval: null,
|
|
2359
|
-
wcUri: null,
|
|
2360
|
-
};
|
|
2361
|
-
|
|
2362
|
-
this._init();
|
|
2363
|
-
}
|
|
2364
|
-
|
|
2365
|
-
// ── Bootstrap ───────────────────────────────────────────────────────────────
|
|
2366
|
-
|
|
2277
|
+
const DomMethods = {
|
|
2367
2278
|
_init() {
|
|
2368
2279
|
this._injectFonts();
|
|
2369
2280
|
this._injectStyles();
|
|
2370
2281
|
this._createWidgetDOM();
|
|
2371
|
-
}
|
|
2282
|
+
},
|
|
2372
2283
|
|
|
2373
2284
|
_injectFonts() {
|
|
2374
2285
|
if (
|
|
@@ -2382,7 +2293,7 @@ class KwesPayWidget {
|
|
|
2382
2293
|
link.rel = "stylesheet";
|
|
2383
2294
|
document.head.appendChild(link);
|
|
2384
2295
|
}
|
|
2385
|
-
}
|
|
2296
|
+
},
|
|
2386
2297
|
|
|
2387
2298
|
_injectStyles() {
|
|
2388
2299
|
if (!document.getElementById("kwespay-widget-styles")) {
|
|
@@ -2391,7 +2302,7 @@ class KwesPayWidget {
|
|
|
2391
2302
|
style.textContent = WIDGET_STYLES;
|
|
2392
2303
|
document.head.appendChild(style);
|
|
2393
2304
|
}
|
|
2394
|
-
}
|
|
2305
|
+
},
|
|
2395
2306
|
|
|
2396
2307
|
_createWidgetDOM() {
|
|
2397
2308
|
document.getElementById("kwespay-widget-overlay")?.remove();
|
|
@@ -2418,15 +2329,16 @@ class KwesPayWidget {
|
|
|
2418
2329
|
document.body.appendChild(overlay);
|
|
2419
2330
|
|
|
2420
2331
|
this._setupEventListeners();
|
|
2332
|
+
this._setupSwipeToClose(container);
|
|
2421
2333
|
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
});
|
|
2334
|
+
// Clicking outside the container does NOT close the widget (intentional)
|
|
2335
|
+
},
|
|
2425
2336
|
|
|
2426
|
-
|
|
2337
|
+
_setupSwipeToClose(container) {
|
|
2427
2338
|
let _touchStartY = 0,
|
|
2428
2339
|
_touchCurrentY = 0,
|
|
2429
2340
|
_isDragging = false;
|
|
2341
|
+
|
|
2430
2342
|
container.addEventListener(
|
|
2431
2343
|
"touchstart",
|
|
2432
2344
|
(e) => {
|
|
@@ -2441,6 +2353,7 @@ class KwesPayWidget {
|
|
|
2441
2353
|
},
|
|
2442
2354
|
{ passive: true }
|
|
2443
2355
|
);
|
|
2356
|
+
|
|
2444
2357
|
container.addEventListener(
|
|
2445
2358
|
"touchmove",
|
|
2446
2359
|
(e) => {
|
|
@@ -2451,6 +2364,7 @@ class KwesPayWidget {
|
|
|
2451
2364
|
},
|
|
2452
2365
|
{ passive: true }
|
|
2453
2366
|
);
|
|
2367
|
+
|
|
2454
2368
|
container.addEventListener("touchend", () => {
|
|
2455
2369
|
if (!_isDragging) return;
|
|
2456
2370
|
_isDragging = false;
|
|
@@ -2459,7 +2373,7 @@ class KwesPayWidget {
|
|
|
2459
2373
|
if (delta > 120) this.close();
|
|
2460
2374
|
else container.style.transform = "translateY(0)";
|
|
2461
2375
|
});
|
|
2462
|
-
}
|
|
2376
|
+
},
|
|
2463
2377
|
|
|
2464
2378
|
_setupEventListeners() {
|
|
2465
2379
|
document
|
|
@@ -2568,10 +2482,83 @@ class KwesPayWidget {
|
|
|
2568
2482
|
});
|
|
2569
2483
|
});
|
|
2570
2484
|
}
|
|
2571
|
-
}
|
|
2485
|
+
},
|
|
2486
|
+
};
|
|
2572
2487
|
|
|
2573
|
-
|
|
2488
|
+
const NavMethods = {
|
|
2489
|
+
_goToStep(stepNumber) {
|
|
2490
|
+
document
|
|
2491
|
+
.querySelectorAll(".kwespay-container .step")
|
|
2492
|
+
.forEach((s) => s.classList.remove("active", "exiting"));
|
|
2493
|
+
this._activateStep(stepNumber);
|
|
2494
|
+
this.state.currentStep = stepNumber;
|
|
2495
|
+
},
|
|
2496
|
+
|
|
2497
|
+
_activateStep(stepNumber) {
|
|
2498
|
+
let targetStep;
|
|
2499
|
+
if (stepNumber === 0.5)
|
|
2500
|
+
targetStep = document.getElementById("kwespay-step0-invalid");
|
|
2501
|
+
else if (typeof stepNumber === "string")
|
|
2502
|
+
targetStep = document.getElementById(`kwespay-step-${stepNumber}`);
|
|
2503
|
+
else targetStep = document.getElementById(`kwespay-step${stepNumber}`);
|
|
2504
|
+
|
|
2505
|
+
if (targetStep) targetStep.classList.add("active");
|
|
2506
|
+
else console.warn(`[KwesPayWidget] Step not found: ${stepNumber}`);
|
|
2507
|
+
},
|
|
2508
|
+
|
|
2509
|
+
_removeCustomStep(id) {
|
|
2510
|
+
document.getElementById(id)?.remove();
|
|
2511
|
+
},
|
|
2574
2512
|
|
|
2513
|
+
_showError(title, message) {
|
|
2514
|
+
const titleEl = document.getElementById("kwespay-errorTitle");
|
|
2515
|
+
const msgEl = document.getElementById("kwespay-errorMessage");
|
|
2516
|
+
if (titleEl) titleEl.textContent = title;
|
|
2517
|
+
if (msgEl) msgEl.textContent = message;
|
|
2518
|
+
this._goToStep(6);
|
|
2519
|
+
},
|
|
2520
|
+
|
|
2521
|
+
_reset() {
|
|
2522
|
+
this._clearQuoteTimer();
|
|
2523
|
+
this._removeCustomStep("kwespay-step-wallet-picker");
|
|
2524
|
+
this._removeCustomStep("kwespay-step-wc");
|
|
2525
|
+
this.state.selectedNetwork = null;
|
|
2526
|
+
this.state.selectedNetworkName = "";
|
|
2527
|
+
this.state.selectedChainId = null;
|
|
2528
|
+
this.state.selectedRpcUrl = null;
|
|
2529
|
+
this.state.selectedContractAddress = null;
|
|
2530
|
+
this.state.selectedToken = null;
|
|
2531
|
+
this.state.selectedTokenConfig = null;
|
|
2532
|
+
this.state.currentPayload = null;
|
|
2533
|
+
this.state.wcUri = null;
|
|
2534
|
+
},
|
|
2535
|
+
};
|
|
2536
|
+
|
|
2537
|
+
const APIKeyMethods = {
|
|
2538
|
+
async _validateAPIKey() {
|
|
2539
|
+
try {
|
|
2540
|
+
const validation = await this.paymentService.validateAPIKey();
|
|
2541
|
+
if (validation.valid) {
|
|
2542
|
+
this.state.vendorInfo = validation.vendorInfo;
|
|
2543
|
+
this.state.keyAllowedNetworks = validation.allowedNetworks ?? null;
|
|
2544
|
+
this.state.keyAllowedTokens = validation.allowedTokens ?? null;
|
|
2545
|
+
this._goToStep(1);
|
|
2546
|
+
dispatchWidgetEvent("apiKeyValidated", {
|
|
2547
|
+
vendorInfo: this.state.vendorInfo,
|
|
2548
|
+
});
|
|
2549
|
+
} else {
|
|
2550
|
+
this._goToStep(0.5);
|
|
2551
|
+
dispatchWidgetEvent("apiKeyInvalid", {});
|
|
2552
|
+
}
|
|
2553
|
+
} catch (err) {
|
|
2554
|
+
console.error("[KwesPayWidget] API key validation error:", err.message);
|
|
2555
|
+
this._goToStep(0.5);
|
|
2556
|
+
dispatchWidgetEvent("apiKeyError", { error: err.message });
|
|
2557
|
+
}
|
|
2558
|
+
},
|
|
2559
|
+
};
|
|
2560
|
+
|
|
2561
|
+
const NetworkMethods = {
|
|
2575
2562
|
_renderNetworkList() {
|
|
2576
2563
|
const mainnetList = document.getElementById("kwespay-mainnetList");
|
|
2577
2564
|
const testnetList = document.getElementById("kwespay-testnetList");
|
|
@@ -2633,7 +2620,7 @@ class KwesPayWidget {
|
|
|
2633
2620
|
|
|
2634
2621
|
if (testnetSection)
|
|
2635
2622
|
testnetSection.style.display = hasTestnet ? "block" : "none";
|
|
2636
|
-
}
|
|
2623
|
+
},
|
|
2637
2624
|
|
|
2638
2625
|
_renderTokenList() {
|
|
2639
2626
|
if (!this.state.selectedNetwork) return;
|
|
@@ -2672,7 +2659,7 @@ class KwesPayWidget {
|
|
|
2672
2659
|
);
|
|
2673
2660
|
tokenList.appendChild(tokenItem);
|
|
2674
2661
|
});
|
|
2675
|
-
}
|
|
2662
|
+
},
|
|
2676
2663
|
|
|
2677
2664
|
_effectiveAllowedTokens() {
|
|
2678
2665
|
const keyTokens =
|
|
@@ -2682,17 +2669,64 @@ class KwesPayWidget {
|
|
|
2682
2669
|
if (!keyTokens) return configTokens;
|
|
2683
2670
|
if (!configTokens) return keyTokens;
|
|
2684
2671
|
return keyTokens.filter((t) => configTokens.includes(t));
|
|
2685
|
-
}
|
|
2672
|
+
},
|
|
2686
2673
|
|
|
2687
|
-
|
|
2674
|
+
async _handleNetworkSelection(key, network) {
|
|
2675
|
+
this.state.selectedNetwork = key;
|
|
2676
|
+
this.state.selectedNetworkName = network.name;
|
|
2677
|
+
this.state.selectedChainId = network.chainId;
|
|
2678
|
+
this.state.selectedRpcUrl = network.rpcUrl;
|
|
2679
|
+
this.state.selectedContractAddress = network.contractAddress;
|
|
2680
|
+
this.state.selectedToken = null;
|
|
2681
|
+
this.state.selectedTokenConfig = null;
|
|
2688
2682
|
|
|
2683
|
+
const nameEl = document.getElementById("kwespay-selectedNetworkName");
|
|
2684
|
+
if (nameEl)
|
|
2685
|
+
nameEl.textContent = `${network.name} ${
|
|
2686
|
+
network.type === "mainnet" ? "Mainnet" : "Testnet"
|
|
2687
|
+
}`;
|
|
2688
|
+
|
|
2689
|
+
const iconEl = document.getElementById("kwespay-selectedNetworkIcon");
|
|
2690
|
+
if (iconEl)
|
|
2691
|
+
iconEl.innerHTML = `<img src="${network.logo}" alt="${network.name}" />`;
|
|
2692
|
+
|
|
2693
|
+
const continueBtn = document.getElementById(
|
|
2694
|
+
"kwespay-continueToWalletConnect"
|
|
2695
|
+
);
|
|
2696
|
+
if (continueBtn) continueBtn.disabled = true;
|
|
2697
|
+
|
|
2698
|
+
this._renderTokenList();
|
|
2699
|
+
this._goToStep(2);
|
|
2700
|
+
},
|
|
2701
|
+
|
|
2702
|
+
_handleTokenSelection(token) {
|
|
2703
|
+
document
|
|
2704
|
+
.querySelectorAll("#kwespay-tokenList .token-item")
|
|
2705
|
+
.forEach((item) => item.classList.remove("selected"));
|
|
2706
|
+
document
|
|
2707
|
+
.querySelector(
|
|
2708
|
+
`#kwespay-tokenList .token-item[data-token-symbol="${token.symbol}"]`
|
|
2709
|
+
)
|
|
2710
|
+
?.classList.add("selected");
|
|
2711
|
+
|
|
2712
|
+
this.state.selectedToken = token.symbol;
|
|
2713
|
+
this.state.selectedTokenConfig = token;
|
|
2714
|
+
|
|
2715
|
+
const continueBtn = document.getElementById(
|
|
2716
|
+
"kwespay-continueToWalletConnect"
|
|
2717
|
+
);
|
|
2718
|
+
if (continueBtn) continueBtn.disabled = false;
|
|
2719
|
+
},
|
|
2720
|
+
};
|
|
2721
|
+
|
|
2722
|
+
const WalletMethods = {
|
|
2689
2723
|
async _handleWalletConnection() {
|
|
2690
2724
|
if (!this.state.selectedToken || !this.state.selectedTokenConfig) {
|
|
2691
2725
|
alert("Please select a token first");
|
|
2692
2726
|
return;
|
|
2693
2727
|
}
|
|
2694
2728
|
this._renderWalletPickerStep();
|
|
2695
|
-
}
|
|
2729
|
+
},
|
|
2696
2730
|
|
|
2697
2731
|
_renderWalletPickerStep() {
|
|
2698
2732
|
this._removeCustomStep("kwespay-step-wallet-picker");
|
|
@@ -2790,7 +2824,7 @@ class KwesPayWidget {
|
|
|
2790
2824
|
list.appendChild(item);
|
|
2791
2825
|
});
|
|
2792
2826
|
});
|
|
2793
|
-
}
|
|
2827
|
+
},
|
|
2794
2828
|
|
|
2795
2829
|
async _connectInjectedProvider(index) {
|
|
2796
2830
|
try {
|
|
@@ -2805,7 +2839,7 @@ class KwesPayWidget {
|
|
|
2805
2839
|
);
|
|
2806
2840
|
dispatchWidgetEvent("walletConnectionError", { error: err.message });
|
|
2807
2841
|
}
|
|
2808
|
-
}
|
|
2842
|
+
},
|
|
2809
2843
|
|
|
2810
2844
|
async _startWalletConnect() {
|
|
2811
2845
|
this._renderWCStep();
|
|
@@ -2821,7 +2855,7 @@ class KwesPayWidget {
|
|
|
2821
2855
|
this._renderWCMobileStep();
|
|
2822
2856
|
}
|
|
2823
2857
|
|
|
2824
|
-
// Pass the target chain so
|
|
2858
|
+
// Pass the target chain so the wallet starts the WC session on the right
|
|
2825
2859
|
// network — without this it defaults to its last-used WC chain (Mainnet).
|
|
2826
2860
|
await this.walletService.connectWalletConnect(this.state.selectedChainId);
|
|
2827
2861
|
} catch (err) {
|
|
@@ -2836,9 +2870,7 @@ class KwesPayWidget {
|
|
|
2836
2870
|
err.message || "WalletConnect failed."
|
|
2837
2871
|
);
|
|
2838
2872
|
}
|
|
2839
|
-
}
|
|
2840
|
-
|
|
2841
|
-
// ── WalletConnect UI ─────────────────────────────────────────────────────────
|
|
2873
|
+
},
|
|
2842
2874
|
|
|
2843
2875
|
_renderWCStep() {
|
|
2844
2876
|
this._removeCustomStep("kwespay-step-wc");
|
|
@@ -2871,7 +2903,7 @@ class KwesPayWidget {
|
|
|
2871
2903
|
});
|
|
2872
2904
|
|
|
2873
2905
|
this._goToStep("wc");
|
|
2874
|
-
}
|
|
2906
|
+
},
|
|
2875
2907
|
|
|
2876
2908
|
_wcDesktopHTML() {
|
|
2877
2909
|
return `
|
|
@@ -2893,7 +2925,7 @@ class KwesPayWidget {
|
|
|
2893
2925
|
<p style="font-size:11px;color:var(--kp-muted);font-family:var(--kp-mono);text-align:center">Waiting for wallet connection…</p>
|
|
2894
2926
|
</div>
|
|
2895
2927
|
`;
|
|
2896
|
-
}
|
|
2928
|
+
},
|
|
2897
2929
|
|
|
2898
2930
|
_wcMobileHTML() {
|
|
2899
2931
|
const wallets = this.walletService.getMobileWallets();
|
|
@@ -2941,7 +2973,7 @@ class KwesPayWidget {
|
|
|
2941
2973
|
</p>
|
|
2942
2974
|
</div>
|
|
2943
2975
|
`;
|
|
2944
|
-
}
|
|
2976
|
+
},
|
|
2945
2977
|
|
|
2946
2978
|
_renderWCMobileStep() {
|
|
2947
2979
|
const list = document.getElementById("kwespay-mobile-wallet-list");
|
|
@@ -2965,7 +2997,7 @@ class KwesPayWidget {
|
|
|
2965
2997
|
}
|
|
2966
2998
|
});
|
|
2967
2999
|
});
|
|
2968
|
-
}
|
|
3000
|
+
},
|
|
2969
3001
|
|
|
2970
3002
|
_onWCUri(uri) {
|
|
2971
3003
|
this.state.wcUri = uri;
|
|
@@ -2982,7 +3014,7 @@ class KwesPayWidget {
|
|
|
2982
3014
|
} else {
|
|
2983
3015
|
this._renderQRCode(uri);
|
|
2984
3016
|
}
|
|
2985
|
-
}
|
|
3017
|
+
},
|
|
2986
3018
|
|
|
2987
3019
|
_renderQRCode(uri) {
|
|
2988
3020
|
const canvas = document.getElementById("kwespay-qr-canvas");
|
|
@@ -3017,18 +3049,18 @@ class KwesPayWidget {
|
|
|
3017
3049
|
<p style="font-size:10px;color:#888;font-family:monospace">Use Copy URI button</p>
|
|
3018
3050
|
</div>
|
|
3019
3051
|
`;
|
|
3020
|
-
}
|
|
3052
|
+
},
|
|
3021
3053
|
|
|
3022
3054
|
_onWCConnected() {
|
|
3023
3055
|
const connection = this.walletService._connectionResult();
|
|
3024
3056
|
this._removeCustomStep("kwespay-step-wc");
|
|
3025
3057
|
this._onWalletConnected(connection);
|
|
3026
|
-
}
|
|
3058
|
+
},
|
|
3027
3059
|
|
|
3028
3060
|
_onWCDisconnected() {
|
|
3029
3061
|
this._removeCustomStep("kwespay-step-wc");
|
|
3030
3062
|
this._goToStep(2);
|
|
3031
|
-
}
|
|
3063
|
+
},
|
|
3032
3064
|
|
|
3033
3065
|
async _onWalletConnected(connection) {
|
|
3034
3066
|
const addressEl = document.getElementById("kwespay-connectedWalletAddress");
|
|
@@ -3045,7 +3077,7 @@ class KwesPayWidget {
|
|
|
3045
3077
|
const proceedBtn = document.getElementById("kwespay-proceedToPayment");
|
|
3046
3078
|
|
|
3047
3079
|
if (cryptoLine) {
|
|
3048
|
-
cryptoLine.textContent = "
|
|
3080
|
+
cryptoLine.textContent = "loading…";
|
|
3049
3081
|
cryptoLine.classList.add("loading");
|
|
3050
3082
|
}
|
|
3051
3083
|
if (timerEl) timerEl.style.display = "none";
|
|
@@ -3067,83 +3099,39 @@ class KwesPayWidget {
|
|
|
3067
3099
|
}
|
|
3068
3100
|
|
|
3069
3101
|
dispatchWidgetEvent("walletConnected", { address: connection.address });
|
|
3070
|
-
}
|
|
3071
|
-
|
|
3072
|
-
// ── API key validation ───────────────────────────────────────────────────────
|
|
3073
|
-
|
|
3074
|
-
async _validateAPIKey() {
|
|
3075
|
-
try {
|
|
3076
|
-
const validation = await this.paymentService.validateAPIKey();
|
|
3077
|
-
if (validation.valid) {
|
|
3078
|
-
this.state.vendorInfo = validation.vendorInfo;
|
|
3079
|
-
this.state.keyAllowedNetworks = validation.allowedNetworks ?? null;
|
|
3080
|
-
this.state.keyAllowedTokens = validation.allowedTokens ?? null;
|
|
3081
|
-
this._goToStep(1);
|
|
3082
|
-
dispatchWidgetEvent("apiKeyValidated", {
|
|
3083
|
-
vendorInfo: this.state.vendorInfo,
|
|
3084
|
-
});
|
|
3085
|
-
} else {
|
|
3086
|
-
this._goToStep(0.5);
|
|
3087
|
-
dispatchWidgetEvent("apiKeyInvalid", {});
|
|
3088
|
-
}
|
|
3089
|
-
} catch (err) {
|
|
3090
|
-
console.error("[KwesPayWidget] API key validation error:", err.message);
|
|
3091
|
-
this._goToStep(0.5);
|
|
3092
|
-
dispatchWidgetEvent("apiKeyError", { error: err.message });
|
|
3093
|
-
}
|
|
3094
|
-
}
|
|
3095
|
-
|
|
3096
|
-
// ── Network / token selection ────────────────────────────────────────────────
|
|
3102
|
+
},
|
|
3103
|
+
};
|
|
3097
3104
|
|
|
3098
|
-
|
|
3099
|
-
this.state.selectedNetwork = key;
|
|
3100
|
-
this.state.selectedNetworkName = network.name;
|
|
3101
|
-
this.state.selectedChainId = network.chainId;
|
|
3102
|
-
this.state.selectedRpcUrl = network.rpcUrl;
|
|
3103
|
-
this.state.selectedContractAddress = network.contractAddress;
|
|
3104
|
-
this.state.selectedToken = null;
|
|
3105
|
-
this.state.selectedTokenConfig = null;
|
|
3105
|
+
const PLATFORM_FEE_BPS = 25;
|
|
3106
3106
|
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
}`;
|
|
3107
|
+
function formatUnits(rawBigInt, decimals) {
|
|
3108
|
+
const divisor = BigInt(10 ** decimals);
|
|
3109
|
+
const whole = rawBigInt / divisor;
|
|
3110
|
+
const remainder = rawBigInt % divisor;
|
|
3112
3111
|
|
|
3113
|
-
|
|
3114
|
-
if (iconEl)
|
|
3115
|
-
iconEl.innerHTML = `<img src="${network.logo}" alt="${network.name}" />`;
|
|
3112
|
+
if (remainder === 0n) return `${whole}`;
|
|
3116
3113
|
|
|
3117
|
-
|
|
3118
|
-
"kwespay-continueToWalletConnect"
|
|
3119
|
-
);
|
|
3120
|
-
if (continueBtn) continueBtn.disabled = true;
|
|
3114
|
+
const fracFull = remainder.toString().padStart(decimals, "0");
|
|
3121
3115
|
|
|
3122
|
-
|
|
3123
|
-
|
|
3116
|
+
if (whole > 0n) {
|
|
3117
|
+
const frac = fracFull.slice(0, 4).replace(/0+$/, "");
|
|
3118
|
+
return frac ? `${whole}.${frac}` : `${whole}`;
|
|
3124
3119
|
}
|
|
3125
3120
|
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
`#kwespay-tokenList .token-item[data-token-symbol="${token.symbol}"]`
|
|
3133
|
-
)
|
|
3134
|
-
?.classList.add("selected");
|
|
3135
|
-
|
|
3136
|
-
this.state.selectedToken = token.symbol;
|
|
3137
|
-
this.state.selectedTokenConfig = token;
|
|
3138
|
-
|
|
3139
|
-
const continueBtn = document.getElementById(
|
|
3140
|
-
"kwespay-continueToWalletConnect"
|
|
3141
|
-
);
|
|
3142
|
-
if (continueBtn) continueBtn.disabled = false;
|
|
3121
|
+
let firstSig = -1;
|
|
3122
|
+
for (let i = 0; i < fracFull.length; i++) {
|
|
3123
|
+
if (fracFull[i] !== "0") {
|
|
3124
|
+
firstSig = i;
|
|
3125
|
+
break;
|
|
3126
|
+
}
|
|
3143
3127
|
}
|
|
3128
|
+
if (firstSig === -1) return `${whole}`;
|
|
3144
3129
|
|
|
3145
|
-
|
|
3130
|
+
const sigSlice = fracFull.slice(firstSig, firstSig + 4).replace(/0+$/, "");
|
|
3131
|
+
return `0.${fracFull.slice(0, firstSig) + sigSlice}`;
|
|
3132
|
+
}
|
|
3146
3133
|
|
|
3134
|
+
const QuoteMethods = {
|
|
3147
3135
|
async _loadReviewStep() {
|
|
3148
3136
|
this._clearQuoteTimer();
|
|
3149
3137
|
|
|
@@ -3155,7 +3143,7 @@ class KwesPayWidget {
|
|
|
3155
3143
|
const proceedBtn = document.getElementById("kwespay-proceedToPayment");
|
|
3156
3144
|
|
|
3157
3145
|
if (cryptoLine) {
|
|
3158
|
-
cryptoLine.textContent = "
|
|
3146
|
+
cryptoLine.textContent = "loading…";
|
|
3159
3147
|
cryptoLine.classList.add("loading");
|
|
3160
3148
|
}
|
|
3161
3149
|
if (timerEl) timerEl.style.display = "none";
|
|
@@ -3200,7 +3188,7 @@ class KwesPayWidget {
|
|
|
3200
3188
|
}
|
|
3201
3189
|
if (proceedBtn) proceedBtn.disabled = true;
|
|
3202
3190
|
}
|
|
3203
|
-
}
|
|
3191
|
+
},
|
|
3204
3192
|
|
|
3205
3193
|
_startQuoteTimer(expiresAt) {
|
|
3206
3194
|
const timerEl = document.getElementById("kwespay-quoteTimer");
|
|
@@ -3221,6 +3209,7 @@ class KwesPayWidget {
|
|
|
3221
3209
|
"kp-quote-timer" +
|
|
3222
3210
|
(secs <= 30 ? " urgent" : "") +
|
|
3223
3211
|
(secs <= 0 ? " expired" : "");
|
|
3212
|
+
|
|
3224
3213
|
if (secs <= 0) {
|
|
3225
3214
|
timerText.textContent = "Refreshing your rate…";
|
|
3226
3215
|
const proceedBtn = document.getElementById("kwespay-proceedToPayment");
|
|
@@ -3229,19 +3218,20 @@ class KwesPayWidget {
|
|
|
3229
3218
|
this._loadReviewStep();
|
|
3230
3219
|
}
|
|
3231
3220
|
};
|
|
3221
|
+
|
|
3232
3222
|
update();
|
|
3233
3223
|
this.state.quoteTimerInterval = setInterval(update, 1000);
|
|
3234
|
-
}
|
|
3224
|
+
},
|
|
3235
3225
|
|
|
3236
3226
|
_clearQuoteTimer() {
|
|
3237
3227
|
if (this.state.quoteTimerInterval) {
|
|
3238
3228
|
clearInterval(this.state.quoteTimerInterval);
|
|
3239
3229
|
this.state.quoteTimerInterval = null;
|
|
3240
3230
|
}
|
|
3241
|
-
}
|
|
3242
|
-
|
|
3243
|
-
// ── Payment processing ───────────────────────────────────────────────────────
|
|
3231
|
+
},
|
|
3232
|
+
};
|
|
3244
3233
|
|
|
3234
|
+
const PaymentMethods = {
|
|
3245
3235
|
async _handlePaymentProcessing() {
|
|
3246
3236
|
if (!this.state.currentPayload) {
|
|
3247
3237
|
this._showError(
|
|
@@ -3270,9 +3260,7 @@ class KwesPayWidget {
|
|
|
3270
3260
|
|
|
3271
3261
|
if (!provider) throw new Error("No wallet provider");
|
|
3272
3262
|
|
|
3273
|
-
|
|
3274
|
-
// Must come first — a stale WC session throws on any RPC call, and we want
|
|
3275
|
-
// a clean "reconnect" error rather than a cryptic provider error downstream.
|
|
3263
|
+
|
|
3276
3264
|
const alive = await this.walletService.isSessionAlive();
|
|
3277
3265
|
if (!alive) {
|
|
3278
3266
|
console.error(
|
|
@@ -3286,22 +3274,17 @@ class KwesPayWidget {
|
|
|
3286
3274
|
throw err;
|
|
3287
3275
|
}
|
|
3288
3276
|
|
|
3289
|
-
|
|
3277
|
+
|
|
3290
3278
|
if (isMobile) {
|
|
3291
3279
|
document
|
|
3292
3280
|
.getElementById("kwespay-mobileTransactionInstruction")
|
|
3293
3281
|
?.style.setProperty("display", "flex");
|
|
3294
3282
|
}
|
|
3295
3283
|
|
|
3296
|
-
// ── Chain validation ─────────────────────────────────────────────────────
|
|
3297
3284
|
if (strictMobile) {
|
|
3298
|
-
|
|
3299
|
-
// reflects the wallet's actual active chain — always call it live.
|
|
3300
|
-
// Never rely on session namespace account ordering (MetaMask puts
|
|
3301
|
-
// Mainnet first regardless of which chain the user is on).
|
|
3285
|
+
|
|
3302
3286
|
await this._assertMobileChain(provider, targetChainId);
|
|
3303
3287
|
} else {
|
|
3304
|
-
// Desktop / injected — eth_chainId is reliable, switch if needed
|
|
3305
3288
|
const rawChain = await provider.request({ method: "eth_chainId" });
|
|
3306
3289
|
const currentChainId = parseInt(rawChain, 16);
|
|
3307
3290
|
|
|
@@ -3322,21 +3305,17 @@ class KwesPayWidget {
|
|
|
3322
3305
|
this.state.selectedToken,
|
|
3323
3306
|
this.state.selectedTokenConfig.decimals
|
|
3324
3307
|
);
|
|
3325
|
-
console.log("[KwesPay] Network switched
|
|
3308
|
+
console.log("[KwesPay] Network switched");
|
|
3326
3309
|
}
|
|
3327
3310
|
}
|
|
3328
3311
|
|
|
3329
|
-
|
|
3330
|
-
// We deep-link to the wallet before the JSON-RPC request lands so the
|
|
3331
|
-
// approval prompt appears immediately without the user having to manually
|
|
3332
|
-
// switch apps.
|
|
3312
|
+
|
|
3333
3313
|
if (strictMobile) {
|
|
3334
3314
|
setStatus(
|
|
3335
3315
|
"Opening your wallet…",
|
|
3336
3316
|
"Approve the payment in your wallet."
|
|
3337
3317
|
);
|
|
3338
3318
|
this.walletService._openWalletForApproval();
|
|
3339
|
-
// Give the OS time to foreground the wallet app before the RPC lands
|
|
3340
3319
|
await new Promise((r) => setTimeout(r, 700));
|
|
3341
3320
|
} else {
|
|
3342
3321
|
setStatus(
|
|
@@ -3345,19 +3324,18 @@ class KwesPayWidget {
|
|
|
3345
3324
|
);
|
|
3346
3325
|
}
|
|
3347
3326
|
|
|
3348
|
-
// ── Send transaction ─────────────────────────────────────────────────────
|
|
3349
3327
|
const receipt = await this.paymentService.createPayment({
|
|
3350
3328
|
payload: this.state.currentPayload,
|
|
3351
3329
|
walletProvider: provider,
|
|
3352
3330
|
onStatusUpdate: setStatus,
|
|
3353
3331
|
});
|
|
3354
3332
|
|
|
3355
|
-
|
|
3333
|
+
|
|
3356
3334
|
document
|
|
3357
3335
|
.getElementById("kwespay-mobileTransactionInstruction")
|
|
3358
3336
|
?.style.setProperty("display", "none");
|
|
3359
3337
|
|
|
3360
|
-
|
|
3338
|
+
|
|
3361
3339
|
const decimals = this.state.selectedTokenConfig?.decimals ?? 6;
|
|
3362
3340
|
const amountBig = BigInt(this.state.currentPayload.amountBaseUnits);
|
|
3363
3341
|
const cryptoDisplay = `${formatUnits(amountBig, decimals)} ${
|
|
@@ -3420,7 +3398,7 @@ class KwesPayWidget {
|
|
|
3420
3398
|
this._showError(title, message);
|
|
3421
3399
|
dispatchWidgetEvent("paymentError", { error: message, errorType });
|
|
3422
3400
|
}
|
|
3423
|
-
}
|
|
3401
|
+
},
|
|
3424
3402
|
|
|
3425
3403
|
/**
|
|
3426
3404
|
* Confirm the wallet is on the target chain before sending a transaction.
|
|
@@ -3428,9 +3406,6 @@ class KwesPayWidget {
|
|
|
3428
3406
|
* Throws WRONG_NETWORK if the chain never matches.
|
|
3429
3407
|
*
|
|
3430
3408
|
* MOBILE WC ONLY — never call this on desktop/injected.
|
|
3431
|
-
*
|
|
3432
|
-
* @param {object} provider
|
|
3433
|
-
* @param {number} targetChainId
|
|
3434
3409
|
*/
|
|
3435
3410
|
async _assertMobileChain(provider, targetChainId) {
|
|
3436
3411
|
const MAX_ATTEMPTS = 3;
|
|
@@ -3464,25 +3439,14 @@ class KwesPayWidget {
|
|
|
3464
3439
|
}
|
|
3465
3440
|
}
|
|
3466
3441
|
|
|
3467
|
-
// All attempts failed — wallet is on wrong chain
|
|
3468
3442
|
const err = new Error(
|
|
3469
3443
|
`Please switch to ${this.state.selectedNetworkName} in your wallet and try again.`
|
|
3470
3444
|
);
|
|
3471
3445
|
err.code = "WRONG_NETWORK";
|
|
3472
3446
|
throw err;
|
|
3473
|
-
}
|
|
3447
|
+
},
|
|
3448
|
+
|
|
3474
3449
|
|
|
3475
|
-
/**
|
|
3476
|
-
* Switch network for desktop/injected providers and poll until confirmed.
|
|
3477
|
-
* MUST NOT be called in strictMobile mode (mobile WC doesn't support
|
|
3478
|
-
* wallet_switchEthereumChain reliably).
|
|
3479
|
-
*
|
|
3480
|
-
* @param {number} chainId
|
|
3481
|
-
* @param {string} networkName
|
|
3482
|
-
* @param {string} rpcUrl
|
|
3483
|
-
* @param {string} tokenSymbol
|
|
3484
|
-
* @param {number} tokenDecimals
|
|
3485
|
-
*/
|
|
3486
3450
|
async _switchNetworkSafe(
|
|
3487
3451
|
chainId,
|
|
3488
3452
|
networkName,
|
|
@@ -3490,7 +3454,6 @@ class KwesPayWidget {
|
|
|
3490
3454
|
tokenSymbol,
|
|
3491
3455
|
tokenDecimals
|
|
3492
3456
|
) {
|
|
3493
|
-
// Capture before any async boundary — prevents `this` loss in callbacks
|
|
3494
3457
|
const switchNetwork = this.walletService.switchNetwork;
|
|
3495
3458
|
const provider = this.walletService.getProvider();
|
|
3496
3459
|
|
|
@@ -3513,12 +3476,11 @@ class KwesPayWidget {
|
|
|
3513
3476
|
|
|
3514
3477
|
const targetHex = toHex(chainId);
|
|
3515
3478
|
|
|
3516
|
-
// Check current chain — skip if already correct
|
|
3517
3479
|
try {
|
|
3518
3480
|
const currentHex = toHex(
|
|
3519
3481
|
await provider.request({ method: "eth_chainId" })
|
|
3520
3482
|
);
|
|
3521
|
-
if (currentHex && currentHex === targetHex) return;
|
|
3483
|
+
if (currentHex && currentHex === targetHex) return;
|
|
3522
3484
|
} catch (err) {
|
|
3523
3485
|
console.warn(
|
|
3524
3486
|
"[KwesPay] Could not read chainId before switch:",
|
|
@@ -3526,7 +3488,6 @@ class KwesPayWidget {
|
|
|
3526
3488
|
);
|
|
3527
3489
|
}
|
|
3528
3490
|
|
|
3529
|
-
// Issue the switch
|
|
3530
3491
|
try {
|
|
3531
3492
|
await switchNetwork(
|
|
3532
3493
|
chainId,
|
|
@@ -3572,44 +3533,67 @@ class KwesPayWidget {
|
|
|
3572
3533
|
`Could not confirm network switch to ${networkName} after 15s. ` +
|
|
3573
3534
|
`Please switch manually in your wallet and try again.`
|
|
3574
3535
|
);
|
|
3575
|
-
}
|
|
3536
|
+
},
|
|
3537
|
+
};
|
|
3538
|
+
|
|
3539
|
+
function resolveAcceptedTokens(input) {
|
|
3540
|
+
if (!input) return null;
|
|
3541
|
+
if (input === "stablecoins") return STABLECOIN_SYMBOLS;
|
|
3542
|
+
if (Array.isArray(input) && input.length)
|
|
3543
|
+
return input.map((t) => t.toUpperCase());
|
|
3544
|
+
return null;
|
|
3545
|
+
}
|
|
3576
3546
|
|
|
3577
|
-
|
|
3547
|
+
class KwesPayWidget {
|
|
3548
|
+
constructor(config) {
|
|
3549
|
+
if (!config.apiKey) throw new Error("[KwesPayWidget] apiKey is required");
|
|
3550
|
+
if (!config.vendorId)
|
|
3551
|
+
throw new Error("[KwesPayWidget] vendorId is required");
|
|
3552
|
+
if (!config.amount || parseFloat(config.amount) <= 0)
|
|
3553
|
+
throw new Error("[KwesPayWidget] Valid amount is required");
|
|
3578
3554
|
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
.
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3555
|
+
this.config = {
|
|
3556
|
+
apiKey: config.apiKey,
|
|
3557
|
+
vendorId: config.vendorId,
|
|
3558
|
+
amount: parseFloat(config.amount),
|
|
3559
|
+
currency: config.currency || DEFAULT_CONFIG.currency,
|
|
3560
|
+
graphqlEndpoint: DEFAULT_CONFIG.graphqlEndpoint,
|
|
3561
|
+
acceptedTokens: resolveAcceptedTokens(config.acceptedTokens),
|
|
3562
|
+
};
|
|
3586
3563
|
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
targetStep = document.getElementById(`kwespay-step-${stepNumber}`);
|
|
3593
|
-
else targetStep = document.getElementById(`kwespay-step${stepNumber}`);
|
|
3564
|
+
if (!Object.values(SUPPORTED_CURRENCIES).includes(this.config.currency)) {
|
|
3565
|
+
throw new Error(
|
|
3566
|
+
`[KwesPayWidget] Unsupported currency: ${this.config.currency}`
|
|
3567
|
+
);
|
|
3568
|
+
}
|
|
3594
3569
|
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3570
|
+
this.walletService = new WalletService();
|
|
3571
|
+
this.paymentService = new PaymentService(
|
|
3572
|
+
this.config.apiKey,
|
|
3573
|
+
this.config.graphqlEndpoint
|
|
3574
|
+
);
|
|
3598
3575
|
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3576
|
+
this.state = {
|
|
3577
|
+
isOpen: false,
|
|
3578
|
+
currentStep: 0,
|
|
3579
|
+
selectedNetwork: null,
|
|
3580
|
+
selectedNetworkName: "",
|
|
3581
|
+
selectedChainId: null,
|
|
3582
|
+
selectedRpcUrl: null,
|
|
3583
|
+
selectedContractAddress: null,
|
|
3584
|
+
selectedToken: null,
|
|
3585
|
+
selectedTokenConfig: null,
|
|
3586
|
+
vendorInfo: null,
|
|
3587
|
+
keyAllowedNetworks: null,
|
|
3588
|
+
keyAllowedTokens: null,
|
|
3589
|
+
currentPayload: null,
|
|
3590
|
+
quoteTimerInterval: null,
|
|
3591
|
+
wcUri: null,
|
|
3592
|
+
};
|
|
3602
3593
|
|
|
3603
|
-
|
|
3604
|
-
const titleEl = document.getElementById("kwespay-errorTitle");
|
|
3605
|
-
const msgEl = document.getElementById("kwespay-errorMessage");
|
|
3606
|
-
if (titleEl) titleEl.textContent = title;
|
|
3607
|
-
if (msgEl) msgEl.textContent = message;
|
|
3608
|
-
this._goToStep(6);
|
|
3594
|
+
this._init();
|
|
3609
3595
|
}
|
|
3610
3596
|
|
|
3611
|
-
// ── Public API ───────────────────────────────────────────────────────────────
|
|
3612
|
-
|
|
3613
3597
|
async open() {
|
|
3614
3598
|
const overlay = document.getElementById("kwespay-widget-overlay");
|
|
3615
3599
|
const container = document.getElementById("kwespay-widget-container");
|
|
@@ -3685,21 +3669,6 @@ class KwesPayWidget {
|
|
|
3685
3669
|
return { ...this.state, config: { ...this.config } };
|
|
3686
3670
|
}
|
|
3687
3671
|
|
|
3688
|
-
_reset() {
|
|
3689
|
-
this._clearQuoteTimer();
|
|
3690
|
-
this._removeCustomStep("kwespay-step-wallet-picker");
|
|
3691
|
-
this._removeCustomStep("kwespay-step-wc");
|
|
3692
|
-
this.state.selectedNetwork = null;
|
|
3693
|
-
this.state.selectedNetworkName = "";
|
|
3694
|
-
this.state.selectedChainId = null;
|
|
3695
|
-
this.state.selectedRpcUrl = null;
|
|
3696
|
-
this.state.selectedContractAddress = null;
|
|
3697
|
-
this.state.selectedToken = null;
|
|
3698
|
-
this.state.selectedTokenConfig = null;
|
|
3699
|
-
this.state.currentPayload = null;
|
|
3700
|
-
this.state.wcUri = null;
|
|
3701
|
-
}
|
|
3702
|
-
|
|
3703
3672
|
destroy() {
|
|
3704
3673
|
this._clearQuoteTimer();
|
|
3705
3674
|
document.body.classList.remove("kwespay-open");
|
|
@@ -3714,6 +3683,17 @@ class KwesPayWidget {
|
|
|
3714
3683
|
}
|
|
3715
3684
|
}
|
|
3716
3685
|
|
|
3686
|
+
Object.assign(
|
|
3687
|
+
KwesPayWidget.prototype,
|
|
3688
|
+
DomMethods,
|
|
3689
|
+
NavMethods,
|
|
3690
|
+
APIKeyMethods,
|
|
3691
|
+
NetworkMethods,
|
|
3692
|
+
WalletMethods,
|
|
3693
|
+
QuoteMethods,
|
|
3694
|
+
PaymentMethods
|
|
3695
|
+
);
|
|
3696
|
+
|
|
3717
3697
|
/**
|
|
3718
3698
|
* KwesPay Widget - Main entry point
|
|
3719
3699
|
* @module @kwespay/widget
|