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