@kwespay/widget 1.0.8 → 1.0.9
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 +105 -12
- package/dist/esm/index--_dPnlv5.js +2553 -0
- package/dist/esm/index-BDgXWGKX.js +2598 -0
- package/dist/esm/index-BL1KrVhv.js +2540 -0
- package/dist/esm/index-BRvdL-Wp.js +3085 -0
- package/dist/esm/index-Bd9oDICQ.js +3085 -0
- package/dist/esm/index-Bu22k5YO.js +2562 -0
- package/dist/esm/{index-338l55OY.js → index-C5OB57Yl.js} +87 -44
- package/dist/esm/index-C7bWyK3p.js +2449 -0
- package/dist/esm/index-CEIKLFls.js +2562 -0
- package/dist/esm/index-CQIWeG1B.js +3083 -0
- package/dist/esm/index-ChlX7kVk.js +2530 -0
- package/dist/esm/index-DKT22_47.js +2601 -0
- package/dist/esm/index-DLb23sAI.js +2562 -0
- package/dist/esm/index-DSz-IL-x.js +2515 -0
- package/dist/esm/index-Dm1jCXcL.js +2540 -0
- package/dist/esm/index-DrZ7hcts.js +2602 -0
- package/dist/esm/index-Ds5OUtZv.js +2552 -0
- package/dist/esm/index-ZsnaJECC.js +3085 -0
- package/dist/esm/index-azjjYn2F.js +2553 -0
- package/dist/esm/index-hxHZv-eJ.js +3085 -0
- package/dist/esm/index.js +1189 -534
- package/dist/index.d.ts +52 -5
- package/dist/kwespay-widget.js +46645 -53961
- package/dist/kwespay-widget.min.js +551 -576
- package/package.json +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -5,10 +5,17 @@ const SUPPORTED_CURRENCIES = {
|
|
|
5
5
|
GHS: "GHS",
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
const STABLECOIN_SYMBOLS = [
|
|
8
|
+
const STABLECOIN_SYMBOLS = [
|
|
9
|
+
"USDT",
|
|
10
|
+
"USDC",
|
|
11
|
+
"DAI",
|
|
12
|
+
"BUSD",
|
|
13
|
+
"USDc",
|
|
14
|
+
"MUSD",
|
|
15
|
+
];
|
|
9
16
|
|
|
10
17
|
const DEFAULT_CONFIG = {
|
|
11
|
-
graphqlEndpoint: "https://api.kwespay.xyz/",
|
|
18
|
+
graphqlEndpoint: "https://api.testnet.kwespay.xyz/graphql",
|
|
12
19
|
currency: SUPPORTED_CURRENCIES.USD,
|
|
13
20
|
};
|
|
14
21
|
|
|
@@ -49,11 +56,29 @@ const NETWORK_CONFIGS = {
|
|
|
49
56
|
explorer: "https://blockscout.lisk.com/tx/",
|
|
50
57
|
type: "mainnet",
|
|
51
58
|
},
|
|
59
|
+
mezo: {
|
|
60
|
+
name: "Mezo",
|
|
61
|
+
chainId: 31612,
|
|
62
|
+
rpcUrl: "https://rpc.mezo.org",
|
|
63
|
+
contractAddress: "",
|
|
64
|
+
logo: "https://arthuremma2.github.io/img-hosting/mezo.png",
|
|
65
|
+
explorer: "https://explorer.mezo.org/tx/",
|
|
66
|
+
type: "mainnet",
|
|
67
|
+
},
|
|
68
|
+
arbitrum: {
|
|
69
|
+
name: "Arbitrum",
|
|
70
|
+
chainId: 42161,
|
|
71
|
+
rpcUrl: "https://arb1.arbitrum.io/rpc",
|
|
72
|
+
contractAddress: "",
|
|
73
|
+
logo: "https://arthuremma2.github.io/img-hosting/arb44.svg",
|
|
74
|
+
explorer: "https://arbiscan.io/tx/",
|
|
75
|
+
type: "mainnet",
|
|
76
|
+
},
|
|
52
77
|
sepolia: {
|
|
53
78
|
name: "Sepolia",
|
|
54
79
|
chainId: 11155111,
|
|
55
80
|
rpcUrl: "https://rpc.sepolia.org",
|
|
56
|
-
contractAddress: "
|
|
81
|
+
contractAddress: "0xD9312df771aEf74a6748c0C46A706873C67F44C7",
|
|
57
82
|
logo: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
58
83
|
explorer: "https://sepolia.etherscan.io/tx/",
|
|
59
84
|
type: "testnet",
|
|
@@ -62,7 +87,7 @@ const NETWORK_CONFIGS = {
|
|
|
62
87
|
name: "Polygon Amoy",
|
|
63
88
|
chainId: 80002,
|
|
64
89
|
rpcUrl: "https://rpc-amoy.polygon.technology",
|
|
65
|
-
contractAddress: "
|
|
90
|
+
contractAddress: "0xEb40935599d5D8ef39C1aAE38E7A1f6d9c89B3fF",
|
|
66
91
|
logo: "https://arthuremma2.github.io/img-hosting/Polygon_Icon.png",
|
|
67
92
|
explorer: "https://amoy.polygonscan.com/tx/",
|
|
68
93
|
type: "testnet",
|
|
@@ -71,7 +96,7 @@ const NETWORK_CONFIGS = {
|
|
|
71
96
|
name: "Base Sepolia",
|
|
72
97
|
chainId: 84532,
|
|
73
98
|
rpcUrl: "https://sepolia.base.org",
|
|
74
|
-
contractAddress: "
|
|
99
|
+
contractAddress: "0x3d7A6a7aD72374D2d3dca4e97053bAbFA6E49ec0",
|
|
75
100
|
logo: "https://arthuremma2.github.io/img-hosting/base23.png",
|
|
76
101
|
explorer: "https://sepolia.basescan.org/tx/",
|
|
77
102
|
type: "testnet",
|
|
@@ -80,11 +105,29 @@ const NETWORK_CONFIGS = {
|
|
|
80
105
|
name: "Lisk Sepolia",
|
|
81
106
|
chainId: 4202,
|
|
82
107
|
rpcUrl: "https://rpc.sepolia-api.lisk.com",
|
|
83
|
-
contractAddress: "
|
|
108
|
+
contractAddress: "0x3378B6074A9DA47Aef8b7C849aFcaF58b8D8134b",
|
|
84
109
|
logo: "https://arthuremma2.github.io/img-hosting/liskt.png",
|
|
85
110
|
explorer: "https://sepolia-blockscout.lisk.com/tx/",
|
|
86
111
|
type: "testnet",
|
|
87
112
|
},
|
|
113
|
+
mezoTestnet: {
|
|
114
|
+
name: "Mezo Testnet",
|
|
115
|
+
chainId: 31611,
|
|
116
|
+
rpcUrl: "https://rpc.test.mezo.org",
|
|
117
|
+
contractAddress: "0x67f3Df6B5BE714303F397104d8F2A3861b9E8b6d",
|
|
118
|
+
logo: "https://arthuremma2.github.io/img-hosting/mezo.png",
|
|
119
|
+
explorer: "https://explorer.test.mezo.org/tx/",
|
|
120
|
+
type: "testnet",
|
|
121
|
+
},
|
|
122
|
+
arbitrumSepolia: {
|
|
123
|
+
name: "Arbitrum Sepolia",
|
|
124
|
+
chainId: 421614,
|
|
125
|
+
rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
126
|
+
contractAddress: "0xa430B2e0D1273464809f8541286058e90781DA9C",
|
|
127
|
+
logo: "https://arthuremma2.github.io/img-hosting/arb44.svg",
|
|
128
|
+
explorer: "https://sepolia.arbiscan.io/tx/",
|
|
129
|
+
type: "testnet",
|
|
130
|
+
},
|
|
88
131
|
};
|
|
89
132
|
|
|
90
133
|
const TOKEN_CONFIGS = {
|
|
@@ -116,6 +159,15 @@ const TOKEN_CONFIGS = {
|
|
|
116
159
|
coingeckoId: "usd-coin",
|
|
117
160
|
binanceSymbol: "USDCUSDT",
|
|
118
161
|
},
|
|
162
|
+
{
|
|
163
|
+
symbol: "DAI",
|
|
164
|
+
name: "Dai Stablecoin",
|
|
165
|
+
icon: "https://move-flow.github.io/assets/dai-logo.svg",
|
|
166
|
+
address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
167
|
+
decimals: 18,
|
|
168
|
+
coingeckoId: "dai",
|
|
169
|
+
binanceSymbol: "DAIUSDT",
|
|
170
|
+
},
|
|
119
171
|
],
|
|
120
172
|
polygon: [
|
|
121
173
|
{
|
|
@@ -136,6 +188,15 @@ const TOKEN_CONFIGS = {
|
|
|
136
188
|
coingeckoId: "tether",
|
|
137
189
|
binanceSymbol: "USDTUSDT",
|
|
138
190
|
},
|
|
191
|
+
{
|
|
192
|
+
symbol: "USDC",
|
|
193
|
+
name: "USD Coin",
|
|
194
|
+
icon: "https://move-flow.github.io/assets/usd-coin-usdc-logo.svg",
|
|
195
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
196
|
+
decimals: 6,
|
|
197
|
+
coingeckoId: "usd-coin",
|
|
198
|
+
binanceSymbol: "USDCUSDT",
|
|
199
|
+
},
|
|
139
200
|
],
|
|
140
201
|
base: [
|
|
141
202
|
{
|
|
@@ -156,38 +217,172 @@ const TOKEN_CONFIGS = {
|
|
|
156
217
|
coingeckoId: "usd-coin",
|
|
157
218
|
binanceSymbol: "USDCUSDT",
|
|
158
219
|
},
|
|
220
|
+
{
|
|
221
|
+
symbol: "USDbC",
|
|
222
|
+
name: "USD Base Coin",
|
|
223
|
+
icon: "https://move-flow.github.io/assets/usd-coin-usdc-logo.svg",
|
|
224
|
+
address: "0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA",
|
|
225
|
+
decimals: 6,
|
|
226
|
+
coingeckoId: "usd-coin",
|
|
227
|
+
binanceSymbol: "USDCUSDT",
|
|
228
|
+
},
|
|
159
229
|
],
|
|
160
230
|
lisk: [
|
|
161
231
|
{
|
|
162
232
|
symbol: "ETH",
|
|
163
|
-
name: "
|
|
164
|
-
icon: "https://arthuremma2.github.io/img-hosting/
|
|
233
|
+
name: "Ethereum",
|
|
234
|
+
icon: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
165
235
|
address: "0x0000000000000000000000000000000000000000",
|
|
166
236
|
decimals: 18,
|
|
167
237
|
coingeckoId: "ethereum",
|
|
168
238
|
binanceSymbol: "ETHUSDT",
|
|
169
239
|
},
|
|
240
|
+
{
|
|
241
|
+
symbol: "LSK",
|
|
242
|
+
name: "Lisk",
|
|
243
|
+
icon: "https://arthuremma2.github.io/img-hosting/liskt.png",
|
|
244
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
245
|
+
decimals: 18,
|
|
246
|
+
coingeckoId: "lisk",
|
|
247
|
+
binanceSymbol: "LSKUSDT",
|
|
248
|
+
},
|
|
170
249
|
],
|
|
171
|
-
|
|
250
|
+
mezo: [
|
|
251
|
+
// {
|
|
252
|
+
// symbol: "BTC",
|
|
253
|
+
// name: "Bitcoin",
|
|
254
|
+
// icon: "https://arthuremma2.github.io/img-hosting/btc.png",
|
|
255
|
+
// address: "0x0000000000000000000000000000000000000000",
|
|
256
|
+
// decimals: 18,
|
|
257
|
+
// coingeckoId: "bitcoin",
|
|
258
|
+
// binanceSymbol: "BTCUSDT",
|
|
259
|
+
// },
|
|
260
|
+
{
|
|
261
|
+
symbol: "MEZO",
|
|
262
|
+
name: "Mezo",
|
|
263
|
+
icon: "https://arthuremma2.github.io/img-hosting/mezo.png",
|
|
264
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
265
|
+
decimals: 18,
|
|
266
|
+
coingeckoId: "mezo",
|
|
267
|
+
binanceSymbol: null,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
symbol: "USDT",
|
|
271
|
+
name: "Mock USDT",
|
|
272
|
+
icon: "https://move-flow.github.io/assets/tether-usdt-logo.svg",
|
|
273
|
+
address: "0xd16c9c341Bc15B5db7E25881893CA5a3117bB9A5",
|
|
274
|
+
decimals: 6,
|
|
275
|
+
coingeckoId: "tether",
|
|
276
|
+
binanceSymbol: "USDTUSDT",
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
arbitrum: [
|
|
172
280
|
{
|
|
173
281
|
symbol: "ETH",
|
|
174
|
-
name: "
|
|
282
|
+
name: "Ethereum",
|
|
175
283
|
icon: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
176
284
|
address: "0x0000000000000000000000000000000000000000",
|
|
177
285
|
decimals: 18,
|
|
178
286
|
coingeckoId: "ethereum",
|
|
179
287
|
binanceSymbol: "ETHUSDT",
|
|
180
288
|
},
|
|
289
|
+
{
|
|
290
|
+
symbol: "USDC",
|
|
291
|
+
name: "USD Coin",
|
|
292
|
+
icon: "https://move-flow.github.io/assets/usd-coin-usdc-logo.svg",
|
|
293
|
+
address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
294
|
+
decimals: 6,
|
|
295
|
+
coingeckoId: "usd-coin",
|
|
296
|
+
binanceSymbol: "USDCUSDT",
|
|
297
|
+
},
|
|
181
298
|
{
|
|
182
299
|
symbol: "USDT",
|
|
183
|
-
name: "
|
|
300
|
+
name: "Tether USD",
|
|
184
301
|
icon: "https://move-flow.github.io/assets/tether-usdt-logo.svg",
|
|
185
|
-
address: "
|
|
302
|
+
address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
|
303
|
+
decimals: 6,
|
|
304
|
+
coingeckoId: "tether",
|
|
305
|
+
binanceSymbol: "USDTUSDT",
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
mezoTestnet: [
|
|
309
|
+
// {
|
|
310
|
+
// symbol: "BTC",
|
|
311
|
+
// name: "Bitcoin (Testnet)",
|
|
312
|
+
// icon: "https://arthuremma2.github.io/img-hosting/btc.png",
|
|
313
|
+
// address: "0x0000000000000000000000000000000000000000",
|
|
314
|
+
// decimals: 18,
|
|
315
|
+
// coingeckoId: "bitcoin",
|
|
316
|
+
// binanceSymbol: "BTCUSDT",
|
|
317
|
+
// },
|
|
318
|
+
{
|
|
319
|
+
symbol: "Mezo",
|
|
320
|
+
name: "Mezo",
|
|
321
|
+
icon: "https://arthuremma2.github.io/img-hosting/mezo.png",
|
|
322
|
+
address: "0x7B7c000000000000000000000000000000000001",
|
|
186
323
|
decimals: 18,
|
|
324
|
+
coingeckoId: "mezo",
|
|
325
|
+
binanceSymbol: "mezo",
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
symbol: "USDT",
|
|
329
|
+
name: "Mock USDT",
|
|
330
|
+
icon: "https://move-flow.github.io/assets/tether-usdt-logo.svg",
|
|
331
|
+
address: "0xd16c9c341Bc15B5db7E25881893CA5a3117bB9A5",
|
|
332
|
+
decimals: 6,
|
|
187
333
|
coingeckoId: "tether",
|
|
188
334
|
binanceSymbol: "USDTUSDT",
|
|
189
335
|
},
|
|
190
336
|
],
|
|
337
|
+
liskTestnet: [
|
|
338
|
+
{
|
|
339
|
+
symbol: "ETH",
|
|
340
|
+
name: "Lisk Sepolia ETH",
|
|
341
|
+
icon: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
342
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
343
|
+
decimals: 18,
|
|
344
|
+
coingeckoId: "ethereum",
|
|
345
|
+
binanceSymbol: "ETHUSDT",
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
symbol: "LSK",
|
|
349
|
+
name: "Lisk",
|
|
350
|
+
icon: "https://arthuremma2.github.io/img-hosting/liskt.png",
|
|
351
|
+
address: "0x8a21CF9Ba08Ae709D64Cb25AfAA951183EC9FF6D",
|
|
352
|
+
decimals: 18,
|
|
353
|
+
coingeckoId: "lisk",
|
|
354
|
+
binanceSymbol: "LSKUSDT",
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
sepolia: [
|
|
358
|
+
{
|
|
359
|
+
symbol: "ETH",
|
|
360
|
+
name: "Sepolia ETH",
|
|
361
|
+
icon: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
362
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
363
|
+
decimals: 18,
|
|
364
|
+
coingeckoId: "ethereum",
|
|
365
|
+
binanceSymbol: "ETHUSDT",
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
symbol: "MUSD",
|
|
369
|
+
name: "MUSD",
|
|
370
|
+
icon: "https://arthuremma2.github.io/img-hosting/MUSD.png",
|
|
371
|
+
address: "0xeB5a5d39dE4Ea42C2Aa6A57EcA2894376683bB8E",
|
|
372
|
+
decimals: 18,
|
|
373
|
+
coingeckoId: "",
|
|
374
|
+
binanceSymbol: "",
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
symbol: "USDC",
|
|
378
|
+
name: "Mock USDC",
|
|
379
|
+
icon: "https://move-flow.github.io/assets/usd-coin-usdc-logo.svg",
|
|
380
|
+
address: "0xeB5a5d39dE4Ea42C2Aa6A57EcA2894376683bB8E",
|
|
381
|
+
decimals: 6,
|
|
382
|
+
coingeckoId: "usd-coin",
|
|
383
|
+
binanceSymbol: "USDCUSDT",
|
|
384
|
+
},
|
|
385
|
+
],
|
|
191
386
|
polygonAmoy: [
|
|
192
387
|
{
|
|
193
388
|
symbol: "MATIC",
|
|
@@ -199,13 +394,13 @@ const TOKEN_CONFIGS = {
|
|
|
199
394
|
binanceSymbol: "MATICUSDT",
|
|
200
395
|
},
|
|
201
396
|
{
|
|
202
|
-
symbol: "
|
|
203
|
-
name: "Mock
|
|
204
|
-
icon: "https://move-flow.github.io/assets/
|
|
397
|
+
symbol: "USDC",
|
|
398
|
+
name: "Mock USDC",
|
|
399
|
+
icon: "https://move-flow.github.io/assets/usd-coin-usdc-logo.svg",
|
|
205
400
|
address: "0x8B0180f2101c8260d49339abfEe87927412494B4",
|
|
206
401
|
decimals: 6,
|
|
207
|
-
coingeckoId: "
|
|
208
|
-
binanceSymbol: "
|
|
402
|
+
coingeckoId: "usd-coin",
|
|
403
|
+
binanceSymbol: "USDCUSDT",
|
|
209
404
|
},
|
|
210
405
|
],
|
|
211
406
|
baseSepolia: [
|
|
@@ -220,7 +415,7 @@ const TOKEN_CONFIGS = {
|
|
|
220
415
|
},
|
|
221
416
|
{
|
|
222
417
|
symbol: "USDC",
|
|
223
|
-
name: "
|
|
418
|
+
name: "Mock USDC",
|
|
224
419
|
icon: "https://move-flow.github.io/assets/usd-coin-usdc-logo.svg",
|
|
225
420
|
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
226
421
|
decimals: 6,
|
|
@@ -228,24 +423,33 @@ const TOKEN_CONFIGS = {
|
|
|
228
423
|
binanceSymbol: "USDCUSDT",
|
|
229
424
|
},
|
|
230
425
|
],
|
|
231
|
-
|
|
426
|
+
arbitrumSepolia: [
|
|
232
427
|
{
|
|
233
428
|
symbol: "ETH",
|
|
234
|
-
name: "
|
|
235
|
-
icon: "https://arthuremma2.github.io/img-hosting/
|
|
429
|
+
name: "Arbitrum Sepolia ETH",
|
|
430
|
+
icon: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
236
431
|
address: "0x0000000000000000000000000000000000000000",
|
|
237
432
|
decimals: 18,
|
|
238
433
|
coingeckoId: "ethereum",
|
|
239
434
|
binanceSymbol: "ETHUSDT",
|
|
240
435
|
},
|
|
241
436
|
{
|
|
242
|
-
symbol: "
|
|
243
|
-
name: "
|
|
244
|
-
icon: "https://
|
|
245
|
-
address: "
|
|
437
|
+
symbol: "USDC",
|
|
438
|
+
name: "Mock USDC",
|
|
439
|
+
icon: "https://move-flow.github.io/assets/usd-coin-usdc-logo.svg",
|
|
440
|
+
address: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
441
|
+
decimals: 6,
|
|
442
|
+
coingeckoId: "usd-coin",
|
|
443
|
+
binanceSymbol: "USDCUSDT",
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
symbol: "ARB",
|
|
447
|
+
name: "Arbitrum (Testnet)",
|
|
448
|
+
icon: "https://arthuremma2.github.io/img-hosting/arb44.svg",
|
|
449
|
+
address: "0x15dd68a7a37d7edBf284b93Bc25d6b286d33fCc3",
|
|
246
450
|
decimals: 18,
|
|
247
|
-
coingeckoId: "
|
|
248
|
-
binanceSymbol: "
|
|
451
|
+
coingeckoId: "arbitrum",
|
|
452
|
+
binanceSymbol: "ARBUSDT",
|
|
249
453
|
},
|
|
250
454
|
],
|
|
251
455
|
};
|
|
@@ -809,36 +1013,36 @@ class PaymentService {
|
|
|
809
1013
|
constructor(apiKey, graphqlEndpoint) {
|
|
810
1014
|
this.apiKey = apiKey;
|
|
811
1015
|
this.graphqlEndpoint = graphqlEndpoint;
|
|
1016
|
+
this._graphqlEndpoint = graphqlEndpoint;
|
|
1017
|
+
this._apiKey = apiKey;
|
|
812
1018
|
this.client = null;
|
|
813
|
-
|
|
814
|
-
console.log("[KwesPay] PaymentService initialized", {
|
|
815
|
-
apiKey: this.apiKey?.slice(0, 6) + "...",
|
|
816
|
-
});
|
|
817
1019
|
}
|
|
818
1020
|
|
|
819
1021
|
async _initClient() {
|
|
820
1022
|
if (this.client) return;
|
|
821
|
-
|
|
822
|
-
console.log("[KwesPay] Initializing SDK client...");
|
|
823
|
-
|
|
824
|
-
const { KwesPayClient } = await import('./index-338l55OY.js');
|
|
825
|
-
|
|
1023
|
+
const { KwesPayClient } = await import('./index-BDgXWGKX.js');
|
|
826
1024
|
this.client = new KwesPayClient({ apiKey: this.apiKey });
|
|
1025
|
+
}
|
|
827
1026
|
|
|
828
|
-
|
|
1027
|
+
async _rawGql(query, variables) {
|
|
1028
|
+
const res = await fetch(this._graphqlEndpoint, {
|
|
1029
|
+
method: "POST",
|
|
1030
|
+
headers: {
|
|
1031
|
+
"Content-Type": "application/json",
|
|
1032
|
+
"X-API-Key": this._apiKey,
|
|
1033
|
+
},
|
|
1034
|
+
body: JSON.stringify({ query, variables }),
|
|
1035
|
+
});
|
|
1036
|
+
return res.json();
|
|
829
1037
|
}
|
|
830
1038
|
|
|
831
1039
|
async validateAPIKey() {
|
|
832
1040
|
try {
|
|
833
1041
|
await this._initClient();
|
|
834
|
-
|
|
835
1042
|
const result = await this.client.validateKey();
|
|
836
|
-
|
|
837
1043
|
if (!result.isValid) {
|
|
838
|
-
console.error("[KwesPay] Invalid API key:", result.error);
|
|
839
1044
|
return { valid: false, error: result.error ?? "Invalid access key" };
|
|
840
1045
|
}
|
|
841
|
-
|
|
842
1046
|
return {
|
|
843
1047
|
valid: true,
|
|
844
1048
|
keyId: result.keyId,
|
|
@@ -855,94 +1059,94 @@ class PaymentService {
|
|
|
855
1059
|
|
|
856
1060
|
async getQuote(params) {
|
|
857
1061
|
await this._initClient();
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const quote = await this.client.quote({
|
|
862
|
-
vendorIdentifier: params.vendorId,
|
|
1062
|
+
return this.client.getQuote({
|
|
1063
|
+
vendorIdentifier: params.vendorIdentifier,
|
|
863
1064
|
fiatAmount: params.fiatAmount,
|
|
864
1065
|
fiatCurrency: params.fiatCurrency || "USD",
|
|
865
1066
|
cryptoCurrency: params.cryptoCurrency,
|
|
866
1067
|
network: params.network,
|
|
867
|
-
payerWalletAddress: params.payerWalletAddress,
|
|
868
1068
|
});
|
|
869
|
-
|
|
870
|
-
console.log("[KwesPay] Quote received:", quote);
|
|
871
|
-
|
|
872
|
-
return quote;
|
|
873
1069
|
}
|
|
874
1070
|
|
|
875
1071
|
async createPayment({ payload, walletProvider, onStatusUpdate }) {
|
|
876
1072
|
await this._initClient();
|
|
877
1073
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1074
|
+
const accounts = await walletProvider.request({ method: "eth_accounts" });
|
|
1075
|
+
const payerWalletAddress = accounts[0];
|
|
1076
|
+
|
|
1077
|
+
// Call createTransaction directly so we can request the deadline field,
|
|
1078
|
+
// which the SDK's GQL_CREATE_TRANSACTION query does not yet include.
|
|
1079
|
+
const rawTx = await this._rawGql(
|
|
1080
|
+
`mutation CreateTransaction($input: CreateTransactionInput!) {
|
|
1081
|
+
createTransaction(input: $input) {
|
|
1082
|
+
success
|
|
1083
|
+
message
|
|
1084
|
+
paymentIdBytes32
|
|
1085
|
+
backendSignature
|
|
1086
|
+
tokenAddress
|
|
1087
|
+
amountBaseUnits
|
|
1088
|
+
chainId
|
|
1089
|
+
deadline
|
|
1090
|
+
expiresAt
|
|
1091
|
+
transaction {
|
|
1092
|
+
transactionReference
|
|
1093
|
+
transactionStatus
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}`,
|
|
1097
|
+
{ input: { quoteId: payload.quoteId, payerWalletAddress } }
|
|
1098
|
+
);
|
|
897
1099
|
|
|
898
|
-
|
|
1100
|
+
const ct = rawTx?.data?.createTransaction;
|
|
899
1101
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
blockNumber: result.blockNumber,
|
|
903
|
-
transactionReference: result.transactionReference,
|
|
904
|
-
paymentIdBytes32: result.paymentIdBytes32,
|
|
905
|
-
};
|
|
906
|
-
} catch (err) {
|
|
907
|
-
console.error("[KwesPay] ❌ createPayment error:", {
|
|
908
|
-
message: err?.message,
|
|
909
|
-
code: err?.code,
|
|
910
|
-
reason: err?.reason,
|
|
911
|
-
data: err?.data,
|
|
912
|
-
transaction: err?.transaction
|
|
913
|
-
? {
|
|
914
|
-
to: err.transaction?.to,
|
|
915
|
-
from: err.transaction?.from,
|
|
916
|
-
value: err.transaction?.value?.toString(),
|
|
917
|
-
gasLimit: err.transaction?.gasLimit?.toString(),
|
|
918
|
-
data: err.transaction?.data,
|
|
919
|
-
}
|
|
920
|
-
: undefined,
|
|
921
|
-
receipt: err?.receipt
|
|
922
|
-
? {
|
|
923
|
-
status: err.receipt?.status,
|
|
924
|
-
gasUsed: err.receipt?.gasUsed?.toString(),
|
|
925
|
-
blockNumber: err.receipt?.blockNumber,
|
|
926
|
-
transactionHash: err.receipt?.transactionHash,
|
|
927
|
-
}
|
|
928
|
-
: undefined,
|
|
929
|
-
stack: err?.stack,
|
|
930
|
-
raw: err,
|
|
931
|
-
});
|
|
932
|
-
throw err;
|
|
1102
|
+
if (!ct?.success) {
|
|
1103
|
+
throw new Error(ct?.message ?? "Transaction creation failed");
|
|
933
1104
|
}
|
|
934
|
-
}
|
|
935
1105
|
|
|
936
|
-
|
|
937
|
-
|
|
1106
|
+
if (!ct.deadline) {
|
|
1107
|
+
throw new Error("Backend did not return a deadline");
|
|
1108
|
+
}
|
|
938
1109
|
|
|
939
|
-
|
|
1110
|
+
// Compute totalBaseUnits locally — mirrors the contract formula:
|
|
1111
|
+
// fee = (amount * 50) / 10000, total = amount + fee
|
|
1112
|
+
const PLATFORM_FEE_BPS = 50n;
|
|
1113
|
+
const amountBig = BigInt(ct.amountBaseUnits);
|
|
1114
|
+
const feeBig = (amountBig * PLATFORM_FEE_BPS) / 10000n;
|
|
1115
|
+
const totalBig = amountBig + feeBig;
|
|
1116
|
+
|
|
1117
|
+
// Build the complete TransactionPayload the SDK's pay() expects.
|
|
1118
|
+
const txPayload = {
|
|
1119
|
+
paymentIdBytes32: ct.paymentIdBytes32,
|
|
1120
|
+
backendSignature: ct.backendSignature,
|
|
1121
|
+
tokenAddress: ct.tokenAddress,
|
|
1122
|
+
amountBaseUnits: ct.amountBaseUnits,
|
|
1123
|
+
totalBaseUnits: totalBig.toString(),
|
|
1124
|
+
chainId: ct.chainId,
|
|
1125
|
+
deadline: ct.deadline,
|
|
1126
|
+
expiresAt: ct.expiresAt,
|
|
1127
|
+
transactionReference: ct.transaction.transactionReference,
|
|
1128
|
+
transactionStatus: ct.transaction.transactionStatus,
|
|
1129
|
+
network: payload.network,
|
|
1130
|
+
vendorIdentifier: payload.vendorIdentifier,
|
|
1131
|
+
};
|
|
940
1132
|
|
|
941
|
-
const
|
|
1133
|
+
const result = await this.client.pay({
|
|
1134
|
+
provider: walletProvider,
|
|
1135
|
+
payload: txPayload,
|
|
1136
|
+
onStatus: (title, detail) => onStatusUpdate?.(title, detail),
|
|
1137
|
+
});
|
|
942
1138
|
|
|
943
|
-
|
|
1139
|
+
return {
|
|
1140
|
+
hash: result.txHash,
|
|
1141
|
+
blockNumber: result.blockNumber,
|
|
1142
|
+
transactionReference: result.transactionReference,
|
|
1143
|
+
paymentIdBytes32: result.paymentIdBytes32,
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
944
1146
|
|
|
945
|
-
|
|
1147
|
+
async getTransactionStatus(transactionReference) {
|
|
1148
|
+
await this._initClient();
|
|
1149
|
+
return this.client.getTransactionStatus(transactionReference);
|
|
946
1150
|
}
|
|
947
1151
|
|
|
948
1152
|
async pollTransactionStatus(
|
|
@@ -950,28 +1154,13 @@ class PaymentService {
|
|
|
950
1154
|
{ onStatus, intervalMs = 4000, maxAttempts = 60 } = {}
|
|
951
1155
|
) {
|
|
952
1156
|
await this._initClient();
|
|
953
|
-
|
|
954
|
-
console.log("[KwesPay] Starting polling...", {
|
|
955
|
-
transactionReference,
|
|
956
|
-
intervalMs,
|
|
957
|
-
});
|
|
958
|
-
|
|
959
1157
|
let attempts = 0;
|
|
960
|
-
|
|
961
1158
|
return new Promise((resolve, reject) => {
|
|
962
1159
|
const id = setInterval(async () => {
|
|
963
1160
|
attempts++;
|
|
964
|
-
|
|
965
1161
|
try {
|
|
966
1162
|
const status = await this.getTransactionStatus(transactionReference);
|
|
967
|
-
|
|
968
|
-
console.log(
|
|
969
|
-
`[KwesPay] Poll attempt ${attempts}:`,
|
|
970
|
-
status.transactionStatus
|
|
971
|
-
);
|
|
972
|
-
|
|
973
1163
|
onStatus?.(status.transactionStatus);
|
|
974
|
-
|
|
975
1164
|
const terminal = [
|
|
976
1165
|
"completed",
|
|
977
1166
|
"failed",
|
|
@@ -980,22 +1169,14 @@ class PaymentService {
|
|
|
980
1169
|
"overpaid",
|
|
981
1170
|
"refunded",
|
|
982
1171
|
];
|
|
983
|
-
|
|
984
1172
|
if (terminal.includes(status.transactionStatus)) {
|
|
985
|
-
console.log(
|
|
986
|
-
"[KwesPay] Final status reached:",
|
|
987
|
-
status.transactionStatus
|
|
988
|
-
);
|
|
989
1173
|
clearInterval(id);
|
|
990
1174
|
resolve(status);
|
|
991
1175
|
} else if (attempts >= maxAttempts) {
|
|
992
|
-
console.error("[KwesPay] Polling timeout");
|
|
993
1176
|
clearInterval(id);
|
|
994
1177
|
reject(new Error("Transaction status polling timed out."));
|
|
995
1178
|
}
|
|
996
1179
|
} catch (err) {
|
|
997
|
-
console.error("[KwesPay] Polling error:", err);
|
|
998
|
-
|
|
999
1180
|
if (attempts >= maxAttempts) {
|
|
1000
1181
|
clearInterval(id);
|
|
1001
1182
|
reject(err);
|
|
@@ -1026,6 +1207,18 @@ function truncateHash(hash, startChars = 10, endChars = 8) {
|
|
|
1026
1207
|
return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
|
|
1027
1208
|
}
|
|
1028
1209
|
|
|
1210
|
+
function formatCryptoAmount(amount, symbol = "") {
|
|
1211
|
+
const num = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
1212
|
+
if (isNaN(num) || num === 0) return symbol ? `0 ${symbol}` : "0";
|
|
1213
|
+
|
|
1214
|
+
const magnitude = Math.floor(Math.log10(Math.abs(num)));
|
|
1215
|
+
const decimalPlaces = Math.max(0, 5 - magnitude);
|
|
1216
|
+
const capped = Math.min(decimalPlaces, 8);
|
|
1217
|
+
const formatted = num.toFixed(capped).replace(/\.?0+$/, "");
|
|
1218
|
+
|
|
1219
|
+
return symbol ? `${formatted} ${symbol}` : formatted;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1029
1222
|
function getErrorType(error) {
|
|
1030
1223
|
const msg = error?.message ?? "";
|
|
1031
1224
|
if (
|
|
@@ -1090,20 +1283,20 @@ const WIDGET_STYLES = `
|
|
|
1090
1283
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1091
1284
|
|
|
1092
1285
|
:root {
|
|
1093
|
-
--kp-bg:
|
|
1094
|
-
--kp-surface:
|
|
1095
|
-
--kp-surface-2:
|
|
1096
|
-
--kp-border:
|
|
1286
|
+
--kp-bg: #0a0a0f;
|
|
1287
|
+
--kp-surface: #0f0f18;
|
|
1288
|
+
--kp-surface-2: #16161f;
|
|
1289
|
+
--kp-border: rgba(255,255,255,0.06);
|
|
1097
1290
|
--kp-border-active: rgba(99,102,241,0.5);
|
|
1098
|
-
--kp-accent:
|
|
1099
|
-
--kp-accent-dim:
|
|
1100
|
-
--kp-accent-glow:
|
|
1101
|
-
--kp-green:
|
|
1102
|
-
--kp-red:
|
|
1103
|
-
--kp-text:
|
|
1104
|
-
--kp-muted:
|
|
1105
|
-
--kp-mono:
|
|
1106
|
-
--kp-sans:
|
|
1291
|
+
--kp-accent: #6366f1;
|
|
1292
|
+
--kp-accent-dim: rgba(99,102,241,0.1);
|
|
1293
|
+
--kp-accent-glow: rgba(99,102,241,0.25);
|
|
1294
|
+
--kp-green: #10b981;
|
|
1295
|
+
--kp-red: #f43f5e;
|
|
1296
|
+
--kp-text: #f1f0ff;
|
|
1297
|
+
--kp-muted: #6b6a80;
|
|
1298
|
+
--kp-mono: 'Inter', monospace;
|
|
1299
|
+
--kp-sans: 'Inter', sans-serif;
|
|
1107
1300
|
}
|
|
1108
1301
|
|
|
1109
1302
|
body.kwespay-open {
|
|
@@ -1128,19 +1321,17 @@ const WIDGET_STYLES = `
|
|
|
1128
1321
|
|
|
1129
1322
|
.kwespay-close-btn {
|
|
1130
1323
|
position: absolute;
|
|
1131
|
-
top:
|
|
1324
|
+
top: 12px; right: 12px;
|
|
1132
1325
|
background: rgba(255,255,255,0.06);
|
|
1133
1326
|
border: 1px solid var(--kp-border);
|
|
1134
1327
|
color: var(--kp-muted);
|
|
1135
|
-
width:
|
|
1136
|
-
border-radius:
|
|
1328
|
+
width: 28px; height: 28px;
|
|
1329
|
+
border-radius: 7px;
|
|
1137
1330
|
cursor: pointer;
|
|
1138
|
-
display: flex;
|
|
1139
|
-
|
|
1140
|
-
justify-content: center;
|
|
1141
|
-
font-size: 18px;
|
|
1331
|
+
display: flex; align-items: center; justify-content: center;
|
|
1332
|
+
font-size: 16px;
|
|
1142
1333
|
transition: all 0.15s;
|
|
1143
|
-
z-index:
|
|
1334
|
+
z-index: 10;
|
|
1144
1335
|
}
|
|
1145
1336
|
|
|
1146
1337
|
.kwespay-close-btn:hover {
|
|
@@ -1149,6 +1340,12 @@ const WIDGET_STYLES = `
|
|
|
1149
1340
|
border-color: var(--kp-border-active);
|
|
1150
1341
|
}
|
|
1151
1342
|
|
|
1343
|
+
.kwespay-steps-wrapper {
|
|
1344
|
+
position: relative;
|
|
1345
|
+
flex: 1;
|
|
1346
|
+
overflow: hidden;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1152
1349
|
.material-symbols-outlined {
|
|
1153
1350
|
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
|
|
1154
1351
|
}
|
|
@@ -1207,6 +1404,8 @@ const WIDGET_STYLES = `
|
|
|
1207
1404
|
to { opacity: 0; }
|
|
1208
1405
|
}
|
|
1209
1406
|
|
|
1407
|
+
|
|
1408
|
+
|
|
1210
1409
|
.kp-topbar {
|
|
1211
1410
|
display: flex;
|
|
1212
1411
|
align-items: center;
|
|
@@ -1223,11 +1422,8 @@ const WIDGET_STYLES = `
|
|
|
1223
1422
|
}
|
|
1224
1423
|
|
|
1225
1424
|
.kp-back-btn {
|
|
1226
|
-
display: flex;
|
|
1227
|
-
|
|
1228
|
-
justify-content: center;
|
|
1229
|
-
width: 28px;
|
|
1230
|
-
height: 28px;
|
|
1425
|
+
display: flex; align-items: center; justify-content: center;
|
|
1426
|
+
width: 28px; height: 28px;
|
|
1231
1427
|
border-radius: 7px;
|
|
1232
1428
|
background: rgba(255,255,255,0.04);
|
|
1233
1429
|
border: 1px solid var(--kp-border);
|
|
@@ -1252,11 +1448,12 @@ const WIDGET_STYLES = `
|
|
|
1252
1448
|
background: var(--kp-accent);
|
|
1253
1449
|
box-shadow: 0 0 8px var(--kp-accent-glow);
|
|
1254
1450
|
animation: kpPulse 2s ease infinite;
|
|
1451
|
+
flex-shrink: 0;
|
|
1255
1452
|
}
|
|
1256
1453
|
|
|
1257
1454
|
@keyframes kpPulse {
|
|
1258
1455
|
0%, 100% { opacity: 1; }
|
|
1259
|
-
50%
|
|
1456
|
+
50% { opacity: 0.4; }
|
|
1260
1457
|
}
|
|
1261
1458
|
|
|
1262
1459
|
.kp-topbar-name {
|
|
@@ -1267,9 +1464,7 @@ const WIDGET_STYLES = `
|
|
|
1267
1464
|
}
|
|
1268
1465
|
|
|
1269
1466
|
.kp-topbar-secure {
|
|
1270
|
-
display: flex;
|
|
1271
|
-
align-items: center;
|
|
1272
|
-
gap: 5px;
|
|
1467
|
+
display: flex; align-items: center; gap: 5px;
|
|
1273
1468
|
font-family: var(--kp-mono);
|
|
1274
1469
|
font-size: 10px;
|
|
1275
1470
|
color: var(--kp-muted);
|
|
@@ -1277,10 +1472,8 @@ const WIDGET_STYLES = `
|
|
|
1277
1472
|
font-weight: 500;
|
|
1278
1473
|
}
|
|
1279
1474
|
|
|
1280
|
-
.kp-topbar-secure .material-symbols-outlined {
|
|
1281
|
-
|
|
1282
|
-
color: var(--kp-green);
|
|
1283
|
-
}
|
|
1475
|
+
.kp-topbar-secure .material-symbols-outlined { font-size: 13px; color: var(--kp-green); }
|
|
1476
|
+
|
|
1284
1477
|
|
|
1285
1478
|
.kp-amount-block {
|
|
1286
1479
|
padding: 20px 20px 16px;
|
|
@@ -1327,6 +1520,8 @@ const WIDGET_STYLES = `
|
|
|
1327
1520
|
|
|
1328
1521
|
.kp-amount-crypto.loading { opacity: 0.4; }
|
|
1329
1522
|
|
|
1523
|
+
/* ── Progress ───────────────────────────────────────────────────────────── */
|
|
1524
|
+
|
|
1330
1525
|
.progress-section {
|
|
1331
1526
|
padding: 14px 20px 12px;
|
|
1332
1527
|
border-bottom: 1px solid var(--kp-border);
|
|
@@ -1334,9 +1529,7 @@ const WIDGET_STYLES = `
|
|
|
1334
1529
|
}
|
|
1335
1530
|
|
|
1336
1531
|
.progress-info {
|
|
1337
|
-
display: flex;
|
|
1338
|
-
align-items: center;
|
|
1339
|
-
justify-content: space-between;
|
|
1532
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
1340
1533
|
margin-bottom: 8px;
|
|
1341
1534
|
}
|
|
1342
1535
|
|
|
@@ -1359,8 +1552,7 @@ const WIDGET_STYLES = `
|
|
|
1359
1552
|
.progress-bars { display: flex; width: 100%; gap: 5px; }
|
|
1360
1553
|
|
|
1361
1554
|
.progress-bar {
|
|
1362
|
-
height: 2px;
|
|
1363
|
-
flex: 1;
|
|
1555
|
+
height: 2px; flex: 1;
|
|
1364
1556
|
border-radius: 999px;
|
|
1365
1557
|
background: var(--kp-surface-2);
|
|
1366
1558
|
transition: background 0.3s ease;
|
|
@@ -1371,6 +1563,8 @@ const WIDGET_STYLES = `
|
|
|
1371
1563
|
box-shadow: 0 0 8px var(--kp-accent-glow);
|
|
1372
1564
|
}
|
|
1373
1565
|
|
|
1566
|
+
/* ── Scrollable content ─────────────────────────────────────────────────── */
|
|
1567
|
+
|
|
1374
1568
|
.section-hint {
|
|
1375
1569
|
font-size: 12px;
|
|
1376
1570
|
color: var(--kp-muted);
|
|
@@ -1394,10 +1588,10 @@ const WIDGET_STYLES = `
|
|
|
1394
1588
|
border-radius: 10px;
|
|
1395
1589
|
}
|
|
1396
1590
|
|
|
1591
|
+
/* ── Network list ───────────────────────────────────────────────────────── */
|
|
1592
|
+
|
|
1397
1593
|
.item-list {
|
|
1398
|
-
display: flex;
|
|
1399
|
-
flex-direction: column;
|
|
1400
|
-
gap: 6px;
|
|
1594
|
+
display: flex; flex-direction: column; gap: 6px;
|
|
1401
1595
|
margin-bottom: 6px;
|
|
1402
1596
|
}
|
|
1403
1597
|
|
|
@@ -1416,9 +1610,7 @@ const WIDGET_STYLES = `
|
|
|
1416
1610
|
}
|
|
1417
1611
|
|
|
1418
1612
|
.list-item {
|
|
1419
|
-
display: flex;
|
|
1420
|
-
align-items: center;
|
|
1421
|
-
gap: 12px;
|
|
1613
|
+
display: flex; align-items: center; gap: 12px;
|
|
1422
1614
|
background: var(--kp-surface);
|
|
1423
1615
|
padding: 12px 14px;
|
|
1424
1616
|
border-radius: 12px;
|
|
@@ -1443,15 +1635,11 @@ const WIDGET_STYLES = `
|
|
|
1443
1635
|
flex-shrink: 0;
|
|
1444
1636
|
}
|
|
1445
1637
|
|
|
1446
|
-
.item-icon img { width:
|
|
1638
|
+
.item-icon img { width: 28px; height: 28px; object-fit: contain; }
|
|
1447
1639
|
.item-info { display: flex; flex-direction: column; flex: 1; }
|
|
1448
1640
|
.item-name-row { display: flex; align-items: center; gap: 7px; }
|
|
1449
1641
|
|
|
1450
|
-
.item-name {
|
|
1451
|
-
font-size: 13px;
|
|
1452
|
-
font-weight: 600;
|
|
1453
|
-
color: var(--kp-text);
|
|
1454
|
-
}
|
|
1642
|
+
.item-name { font-size: 13px; font-weight: 600; color: var(--kp-text); }
|
|
1455
1643
|
|
|
1456
1644
|
.item-badge {
|
|
1457
1645
|
padding: 1px 6px;
|
|
@@ -1479,10 +1667,10 @@ const WIDGET_STYLES = `
|
|
|
1479
1667
|
|
|
1480
1668
|
.item-chevron { color: var(--kp-muted); font-size: 16px; }
|
|
1481
1669
|
|
|
1670
|
+
/* ── Token list ─────────────────────────────────────────────────────────── */
|
|
1671
|
+
|
|
1482
1672
|
.token-item {
|
|
1483
|
-
display: flex;
|
|
1484
|
-
align-items: center;
|
|
1485
|
-
gap: 12px;
|
|
1673
|
+
display: flex; align-items: center; gap: 12px;
|
|
1486
1674
|
padding: 13px 14px;
|
|
1487
1675
|
cursor: pointer;
|
|
1488
1676
|
transition: all 0.15s;
|
|
@@ -1495,14 +1683,24 @@ const WIDGET_STYLES = `
|
|
|
1495
1683
|
}
|
|
1496
1684
|
|
|
1497
1685
|
.token-item:last-child { margin-bottom: 0; }
|
|
1498
|
-
|
|
1499
|
-
.token-item
|
|
1686
|
+
|
|
1687
|
+
.token-item:hover {
|
|
1688
|
+
border-color: var(--kp-border-active);
|
|
1689
|
+
background: var(--kp-accent-dim);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
.token-item.selected {
|
|
1693
|
+
background: var(--kp-accent-dim);
|
|
1694
|
+
border-color: var(--kp-accent);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1500
1697
|
.token-item.selected::after {
|
|
1501
1698
|
content: '';
|
|
1502
1699
|
position: absolute; inset: 0;
|
|
1503
1700
|
background: linear-gradient(90deg, rgba(99,102,241,0.08) 0%, transparent 100%);
|
|
1504
1701
|
pointer-events: none;
|
|
1505
1702
|
}
|
|
1703
|
+
|
|
1506
1704
|
.token-item.selected .token-symbol { color: var(--kp-accent); }
|
|
1507
1705
|
|
|
1508
1706
|
.token-left { display: flex; align-items: center; gap: 12px; flex: 1; }
|
|
@@ -1512,11 +1710,8 @@ const WIDGET_STYLES = `
|
|
|
1512
1710
|
border-radius: 10px;
|
|
1513
1711
|
background: var(--kp-surface-2);
|
|
1514
1712
|
border: 1px solid var(--kp-border);
|
|
1515
|
-
overflow: hidden;
|
|
1516
|
-
flex-
|
|
1517
|
-
display: flex;
|
|
1518
|
-
align-items: center;
|
|
1519
|
-
justify-content: center;
|
|
1713
|
+
overflow: hidden; flex-shrink: 0;
|
|
1714
|
+
display: flex; align-items: center; justify-content: center;
|
|
1520
1715
|
}
|
|
1521
1716
|
|
|
1522
1717
|
.token-icon img { width: 22px; height: 22px; object-fit: contain; }
|
|
@@ -1542,6 +1737,8 @@ const WIDGET_STYLES = `
|
|
|
1542
1737
|
.token-chevron { color: var(--kp-muted); font-size: 16px; flex-shrink: 0; }
|
|
1543
1738
|
#kwespay-tokenList { display: flex; flex-direction: column; }
|
|
1544
1739
|
|
|
1740
|
+
/* ── Buttons ────────────────────────────────────────────────────────────── */
|
|
1741
|
+
|
|
1545
1742
|
.bottom-action {
|
|
1546
1743
|
padding: 12px 20px 16px;
|
|
1547
1744
|
background: var(--kp-bg);
|
|
@@ -1583,6 +1780,7 @@ const WIDGET_STYLES = `
|
|
|
1583
1780
|
}
|
|
1584
1781
|
|
|
1585
1782
|
.action-btn.secondary::before { display: none; }
|
|
1783
|
+
|
|
1586
1784
|
.action-btn.secondary:hover {
|
|
1587
1785
|
background: var(--kp-surface-2);
|
|
1588
1786
|
border-color: var(--kp-border-active);
|
|
@@ -1592,14 +1790,13 @@ const WIDGET_STYLES = `
|
|
|
1592
1790
|
|
|
1593
1791
|
.action-btn:disabled { opacity: 0.3; cursor: not-allowed; box-shadow: none; }
|
|
1594
1792
|
|
|
1793
|
+
/* ── Footer ─────────────────────────────────────────────────────────────── */
|
|
1794
|
+
|
|
1595
1795
|
.kp-footer {
|
|
1596
1796
|
padding: 10px 20px 12px;
|
|
1597
1797
|
background: var(--kp-bg);
|
|
1598
1798
|
border-top: 1px solid var(--kp-border);
|
|
1599
|
-
display: flex;
|
|
1600
|
-
align-items: center;
|
|
1601
|
-
justify-content: center;
|
|
1602
|
-
gap: 6px;
|
|
1799
|
+
display: flex; align-items: center; justify-content: center; gap: 6px;
|
|
1603
1800
|
flex-shrink: 0;
|
|
1604
1801
|
}
|
|
1605
1802
|
|
|
@@ -1615,11 +1812,11 @@ const WIDGET_STYLES = `
|
|
|
1615
1812
|
|
|
1616
1813
|
.kp-footer-text span { color: var(--kp-text); font-weight: 500; }
|
|
1617
1814
|
|
|
1815
|
+
/* ── Spinner / loading ──────────────────────────────────────────────────── */
|
|
1816
|
+
|
|
1618
1817
|
.loading-container {
|
|
1619
|
-
display: flex;
|
|
1620
|
-
|
|
1621
|
-
align-items: center;
|
|
1622
|
-
justify-content: center;
|
|
1818
|
+
display: flex; flex-direction: column;
|
|
1819
|
+
align-items: center; justify-content: center;
|
|
1623
1820
|
padding: 24px 20px;
|
|
1624
1821
|
flex: 1;
|
|
1625
1822
|
}
|
|
@@ -1649,6 +1846,21 @@ const WIDGET_STYLES = `
|
|
|
1649
1846
|
animation: kpSpin 1.2s linear infinite reverse;
|
|
1650
1847
|
}
|
|
1651
1848
|
|
|
1849
|
+
.kp-spin-soft.spinner-ring {
|
|
1850
|
+
border-top-color: rgba(16,185,129,0.55);
|
|
1851
|
+
animation-duration: 1.6s;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
.kp-spin-soft.spinner-ring-2 {
|
|
1855
|
+
border-bottom-color: rgba(16,185,129,0.18);
|
|
1856
|
+
animation-duration: 2.2s;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
.kp-spinner-icon-green {
|
|
1860
|
+
background: rgba(16,185,129,0.08) !important;
|
|
1861
|
+
border-color: rgba(16,185,129,0.15) !important;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1652
1864
|
@keyframes kpSpin { to { transform: rotate(360deg); } }
|
|
1653
1865
|
|
|
1654
1866
|
.spinner-icon {
|
|
@@ -1682,6 +1894,39 @@ const WIDGET_STYLES = `
|
|
|
1682
1894
|
font-weight: 400;
|
|
1683
1895
|
}
|
|
1684
1896
|
|
|
1897
|
+
/* ── Processing badges ──────────────────────────────────────────────────── */
|
|
1898
|
+
|
|
1899
|
+
.kp-onchain-badge {
|
|
1900
|
+
display: inline-flex; align-items: center; gap: 5px;
|
|
1901
|
+
margin-top: 14px;
|
|
1902
|
+
padding: 6px 14px;
|
|
1903
|
+
border-radius: 20px;
|
|
1904
|
+
background: rgba(16,185,129,0.08);
|
|
1905
|
+
border: 1px solid rgba(16,185,129,0.18);
|
|
1906
|
+
font-family: var(--kp-mono);
|
|
1907
|
+
font-size: 10px;
|
|
1908
|
+
color: var(--kp-green);
|
|
1909
|
+
letter-spacing: 0.04em;
|
|
1910
|
+
font-weight: 500;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
.kp-onchain-badge .material-symbols-outlined { font-size: 12px; color: var(--kp-green); }
|
|
1914
|
+
|
|
1915
|
+
.kp-confirmed-bar {
|
|
1916
|
+
display: flex; align-items: center; justify-content: center; gap: 6px;
|
|
1917
|
+
padding: 8px 20px;
|
|
1918
|
+
background: rgba(16,185,129,0.06);
|
|
1919
|
+
border-bottom: 1px solid rgba(16,185,129,0.12);
|
|
1920
|
+
font-family: var(--kp-mono);
|
|
1921
|
+
font-size: 10px;
|
|
1922
|
+
color: var(--kp-green);
|
|
1923
|
+
letter-spacing: 0.04em;
|
|
1924
|
+
font-weight: 500;
|
|
1925
|
+
flex-shrink: 0;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
/* ── Success icons ──────────────────────────────────────────────────────── */
|
|
1929
|
+
|
|
1685
1930
|
.success-icon {
|
|
1686
1931
|
width: 64px; height: 64px;
|
|
1687
1932
|
border-radius: 16px;
|
|
@@ -1693,6 +1938,8 @@ const WIDGET_STYLES = `
|
|
|
1693
1938
|
|
|
1694
1939
|
.success-icon .material-symbols-outlined { font-size: 36px; color: var(--kp-green); }
|
|
1695
1940
|
|
|
1941
|
+
/* ── Error icon + hint ──────────────────────────────────────────────────── */
|
|
1942
|
+
|
|
1696
1943
|
.error-icon {
|
|
1697
1944
|
width: 64px; height: 64px;
|
|
1698
1945
|
border-radius: 16px;
|
|
@@ -1704,12 +1951,28 @@ const WIDGET_STYLES = `
|
|
|
1704
1951
|
|
|
1705
1952
|
.error-icon .material-symbols-outlined { font-size: 36px; color: var(--kp-red); }
|
|
1706
1953
|
|
|
1954
|
+
/* Reassuring strip below the error message */
|
|
1955
|
+
.kp-error-hint {
|
|
1956
|
+
display: flex; align-items: flex-start; gap: 7px;
|
|
1957
|
+
margin-top: 16px;
|
|
1958
|
+
padding: 10px 14px;
|
|
1959
|
+
border-radius: 10px;
|
|
1960
|
+
background: rgba(255,255,255,0.03);
|
|
1961
|
+
border: 1px solid var(--kp-border);
|
|
1962
|
+
font-family: var(--kp-mono);
|
|
1963
|
+
font-size: 11px;
|
|
1964
|
+
color: var(--kp-muted);
|
|
1965
|
+
line-height: 1.55;
|
|
1966
|
+
max-width: 280px;
|
|
1967
|
+
text-align: left;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
/* ── Network status pill ────────────────────────────────────────────────── */
|
|
1971
|
+
|
|
1707
1972
|
.network-status { padding: 14px 20px 0; }
|
|
1708
1973
|
|
|
1709
1974
|
.status-card {
|
|
1710
|
-
display: flex;
|
|
1711
|
-
align-items: center;
|
|
1712
|
-
gap: 10px;
|
|
1975
|
+
display: flex; align-items: center; gap: 10px;
|
|
1713
1976
|
background: var(--kp-accent-dim);
|
|
1714
1977
|
border: 1px solid rgba(99,102,241,0.2);
|
|
1715
1978
|
border-radius: 10px;
|
|
@@ -1727,6 +1990,8 @@ const WIDGET_STYLES = `
|
|
|
1727
1990
|
.status-icon img { width: 16px; height: 16px; object-fit: contain; }
|
|
1728
1991
|
.status-text { color: var(--kp-accent); font-size: 12px; font-weight: 600; flex: 1; }
|
|
1729
1992
|
|
|
1993
|
+
/* ── Review body ────────────────────────────────────────────────────────── */
|
|
1994
|
+
|
|
1730
1995
|
.kp-review-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
|
|
1731
1996
|
.kp-review-body::-webkit-scrollbar { width: 3px; }
|
|
1732
1997
|
.kp-review-body::-webkit-scrollbar-thumb { background: rgba(99,102,241,0.2); border-radius: 10px; }
|
|
@@ -1758,6 +2023,8 @@ const WIDGET_STYLES = `
|
|
|
1758
2023
|
|
|
1759
2024
|
.kp-review-crypto-line.loading { opacity: 0.4; }
|
|
1760
2025
|
|
|
2026
|
+
/* ── Detail / fee blocks ────────────────────────────────────────────────── */
|
|
2027
|
+
|
|
1761
2028
|
.kp-detail-block {
|
|
1762
2029
|
background: var(--kp-surface);
|
|
1763
2030
|
border: 1px solid var(--kp-border);
|
|
@@ -1767,9 +2034,7 @@ const WIDGET_STYLES = `
|
|
|
1767
2034
|
}
|
|
1768
2035
|
|
|
1769
2036
|
.kp-detail-row {
|
|
1770
|
-
display: flex;
|
|
1771
|
-
justify-content: space-between;
|
|
1772
|
-
align-items: center;
|
|
2037
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
1773
2038
|
padding: 11px 16px;
|
|
1774
2039
|
border-bottom: 1px solid var(--kp-border);
|
|
1775
2040
|
}
|
|
@@ -1793,7 +2058,7 @@ const WIDGET_STYLES = `
|
|
|
1793
2058
|
}
|
|
1794
2059
|
|
|
1795
2060
|
.kp-detail-val.accent { color: var(--kp-accent); }
|
|
1796
|
-
.kp-detail-val.green
|
|
2061
|
+
.kp-detail-val.green { color: var(--kp-green); }
|
|
1797
2062
|
|
|
1798
2063
|
.kp-fee-block {
|
|
1799
2064
|
background: rgba(16,185,129,0.04);
|
|
@@ -1823,6 +2088,8 @@ const WIDGET_STYLES = `
|
|
|
1823
2088
|
|
|
1824
2089
|
.kp-fee-header .material-symbols-outlined { font-size: 13px; color: var(--kp-green); }
|
|
1825
2090
|
|
|
2091
|
+
/* ── Transaction receipt ────────────────────────────────────────────────── */
|
|
2092
|
+
|
|
1826
2093
|
.tx-details {
|
|
1827
2094
|
width: 100%;
|
|
1828
2095
|
background: var(--kp-surface);
|
|
@@ -1833,17 +2100,15 @@ const WIDGET_STYLES = `
|
|
|
1833
2100
|
}
|
|
1834
2101
|
|
|
1835
2102
|
.tx-row {
|
|
1836
|
-
display: flex;
|
|
1837
|
-
justify-content: space-between;
|
|
1838
|
-
align-items: center;
|
|
2103
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
1839
2104
|
padding: 10px 16px;
|
|
1840
2105
|
border-bottom: 1px solid var(--kp-border);
|
|
1841
2106
|
}
|
|
1842
2107
|
|
|
1843
2108
|
.tx-row:last-child { border-bottom: none; }
|
|
1844
2109
|
|
|
1845
|
-
.tx-label
|
|
1846
|
-
.tx-value
|
|
2110
|
+
.tx-label { font-family: var(--kp-mono); color: var(--kp-muted); font-size: 10px; letter-spacing: 0.02em; font-weight: 400; }
|
|
2111
|
+
.tx-value { color: var(--kp-text); font-size: 11px; font-weight: 500; font-family: var(--kp-mono); }
|
|
1847
2112
|
.tx-hash-row { display: flex; align-items: center; gap: 8px; }
|
|
1848
2113
|
|
|
1849
2114
|
.explorer-link {
|
|
@@ -1860,26 +2125,57 @@ const WIDGET_STYLES = `
|
|
|
1860
2125
|
.explorer-link:hover { background: rgba(99,102,241,0.2); }
|
|
1861
2126
|
.explorer-link .material-symbols-outlined { font-size: 12px; color: var(--kp-accent); }
|
|
1862
2127
|
|
|
2128
|
+
|
|
2129
|
+
|
|
2130
|
+
/* Full-width track that drains left-to-right */
|
|
2131
|
+
.kp-countdown-track {
|
|
2132
|
+
width: 100%;
|
|
2133
|
+
height: 3px;
|
|
2134
|
+
border-radius: 999px;
|
|
2135
|
+
background: var(--kp-surface-2);
|
|
2136
|
+
overflow: hidden;
|
|
2137
|
+
margin-bottom: 6px;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
.kp-countdown-bar {
|
|
2141
|
+
height: 100%;
|
|
2142
|
+
width: 100%;
|
|
2143
|
+
border-radius: 999px;
|
|
2144
|
+
background: var(--kp-accent);
|
|
2145
|
+
/* smooth drain: transition only width changes */
|
|
2146
|
+
transition: width 0.9s linear, background 0.4s ease;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
.kp-countdown-label {
|
|
2150
|
+
font-family: var(--kp-mono);
|
|
2151
|
+
font-size: 10px;
|
|
2152
|
+
color: var(--kp-muted);
|
|
2153
|
+
text-align: center;
|
|
2154
|
+
letter-spacing: 0.04em;
|
|
2155
|
+
font-weight: 400;
|
|
2156
|
+
margin-bottom: 2px;
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
|
|
1863
2161
|
.mobile-instruction {
|
|
1864
2162
|
background: var(--kp-accent-dim);
|
|
1865
2163
|
border: 1px solid rgba(99,102,241,0.2);
|
|
1866
2164
|
border-radius: 10px;
|
|
1867
2165
|
padding: 12px;
|
|
1868
2166
|
margin: 12px 0 0;
|
|
1869
|
-
display: flex;
|
|
1870
|
-
align-items: flex-start;
|
|
1871
|
-
gap: 10px;
|
|
2167
|
+
display: flex; align-items: flex-start; gap: 10px;
|
|
1872
2168
|
}
|
|
1873
2169
|
|
|
1874
2170
|
.mobile-instruction-icon { color: var(--kp-accent); font-size: 18px; flex-shrink: 0; margin-top: 1px; }
|
|
1875
2171
|
.mobile-instruction-text { flex: 1; }
|
|
1876
2172
|
.mobile-instruction-title { color: var(--kp-text); font-size: 12px; font-weight: 600; margin-bottom: 3px; }
|
|
1877
|
-
.mobile-instruction-desc
|
|
2173
|
+
.mobile-instruction-desc { color: var(--kp-muted); font-size: 11px; line-height: 1.5; font-weight: 400; }
|
|
2174
|
+
|
|
2175
|
+
/* ── Quote timer ────────────────────────────────────────────────────────── */
|
|
1878
2176
|
|
|
1879
2177
|
.kp-quote-timer {
|
|
1880
|
-
display: flex;
|
|
1881
|
-
align-items: center;
|
|
1882
|
-
gap: 5px;
|
|
2178
|
+
display: flex; align-items: center; gap: 5px;
|
|
1883
2179
|
font-family: var(--kp-mono);
|
|
1884
2180
|
font-size: 10px;
|
|
1885
2181
|
color: var(--kp-muted);
|
|
@@ -1887,9 +2183,33 @@ const WIDGET_STYLES = `
|
|
|
1887
2183
|
font-weight: 400;
|
|
1888
2184
|
}
|
|
1889
2185
|
|
|
1890
|
-
.kp-quote-timer .material-symbols-outlined { font-size: 12px; }
|
|
1891
|
-
.kp-quote-timer.urgent
|
|
1892
|
-
.kp-quote-timer.expired { color: var(--kp-red); }
|
|
2186
|
+
.kp-quote-timer .material-symbols-outlined { font-size: 12px; }
|
|
2187
|
+
.kp-quote-timer.urgent { color: #fb923c; }
|
|
2188
|
+
.kp-quote-timer.expired { color: var(--kp-red); }
|
|
2189
|
+
|
|
2190
|
+
/* ── WalletConnect mobile ───────────────────────────────────────────────── */
|
|
2191
|
+
|
|
2192
|
+
.kp-mobile-connect-status {
|
|
2193
|
+
display: flex; align-items: center; gap: 12px;
|
|
2194
|
+
background: var(--kp-accent-dim);
|
|
2195
|
+
border: 1px solid rgba(99,102,241,0.2);
|
|
2196
|
+
border-radius: 12px;
|
|
2197
|
+
padding: 14px 16px;
|
|
2198
|
+
margin-bottom: 4px;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
.kp-mobile-status-icon { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
|
2202
|
+
.kp-mobile-status-text { flex: 1; }
|
|
2203
|
+
.kp-mobile-status-title { font-size: 13px; font-weight: 600; color: var(--kp-text); margin-bottom: 2px; }
|
|
2204
|
+
.kp-mobile-status-desc { font-family: var(--kp-mono); font-size: 11px; color: var(--kp-muted); }
|
|
2205
|
+
|
|
2206
|
+
.kp-wallet-option:active {
|
|
2207
|
+
transform: scale(0.98);
|
|
2208
|
+
background: var(--kp-accent-dim);
|
|
2209
|
+
border-color: var(--kp-border-active);
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
|
|
1893
2213
|
|
|
1894
2214
|
@media (max-width: 480px) {
|
|
1895
2215
|
.kwespay-overlay {
|
|
@@ -1928,66 +2248,25 @@ const WIDGET_STYLES = `
|
|
|
1928
2248
|
}
|
|
1929
2249
|
}
|
|
1930
2250
|
|
|
1931
|
-
|
|
1932
|
-
.kp-mobile-connect-status {
|
|
1933
|
-
display: flex;
|
|
1934
|
-
align-items: center;
|
|
1935
|
-
gap: 12px;
|
|
1936
|
-
background: var(--kp-accent-dim);
|
|
1937
|
-
border: 1px solid rgba(99,102,241,0.2);
|
|
1938
|
-
border-radius: 12px;
|
|
1939
|
-
padding: 14px 16px;
|
|
1940
|
-
margin-bottom: 4px;
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
.kp-mobile-status-icon {
|
|
1944
|
-
width: 28px; height: 28px;
|
|
1945
|
-
display: flex; align-items: center; justify-content: center;
|
|
1946
|
-
flex-shrink: 0;
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
.kp-mobile-status-text { flex: 1; }
|
|
1950
|
-
|
|
1951
|
-
.kp-mobile-status-title {
|
|
1952
|
-
font-size: 13px;
|
|
1953
|
-
font-weight: 600;
|
|
1954
|
-
color: var(--kp-text);
|
|
1955
|
-
margin-bottom: 2px;
|
|
1956
|
-
}
|
|
1957
|
-
|
|
1958
|
-
.kp-mobile-status-desc {
|
|
1959
|
-
font-family: var(--kp-mono);
|
|
1960
|
-
font-size: 11px;
|
|
1961
|
-
color: var(--kp-muted);
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
.kp-wallet-option:active {
|
|
1965
|
-
transform: scale(0.98);
|
|
1966
|
-
background: var(--kp-accent-dim);
|
|
1967
|
-
border-color: var(--kp-border-active);
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
2251
|
@keyframes kpSheetUp {
|
|
1972
2252
|
from { transform: translateY(100%); opacity: 0.8; }
|
|
1973
|
-
to { transform: translateY(0);
|
|
2253
|
+
to { transform: translateY(0); opacity: 1; }
|
|
1974
2254
|
}
|
|
1975
2255
|
|
|
1976
2256
|
@keyframes kpSheetDown {
|
|
1977
|
-
from { transform: translateY(0);
|
|
2257
|
+
from { transform: translateY(0); opacity: 1; }
|
|
1978
2258
|
to { transform: translateY(100%); opacity: 0; }
|
|
1979
2259
|
}
|
|
1980
2260
|
`;
|
|
1981
2261
|
|
|
1982
2262
|
function getStepTemplates(fiatAmount, currency) {
|
|
1983
2263
|
return `
|
|
2264
|
+
<!-- Step 0: Initialising -->
|
|
1984
2265
|
<div class="step active" id="kwespay-step0">
|
|
1985
2266
|
<div class="kp-topbar">
|
|
1986
2267
|
<div class="kp-topbar-brand">
|
|
1987
|
-
<div class="kp-topbar-dot"></div>
|
|
1988
2268
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
1989
2269
|
</div>
|
|
1990
|
-
|
|
1991
2270
|
</div>
|
|
1992
2271
|
<div class="loading-container" style="flex:1">
|
|
1993
2272
|
<div class="spinner-wrapper">
|
|
@@ -2006,10 +2285,10 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2006
2285
|
</div>
|
|
2007
2286
|
</div>
|
|
2008
2287
|
|
|
2288
|
+
<!-- Step 0.5: API key invalid / network error on init -->
|
|
2009
2289
|
<div class="step" id="kwespay-step0-invalid">
|
|
2010
2290
|
<div class="kp-topbar">
|
|
2011
2291
|
<div class="kp-topbar-brand">
|
|
2012
|
-
<div class="kp-topbar-dot" style="background:var(--kp-red);box-shadow:none;animation:none"></div>
|
|
2013
2292
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2014
2293
|
</div>
|
|
2015
2294
|
</div>
|
|
@@ -2025,13 +2304,12 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2025
2304
|
</div>
|
|
2026
2305
|
</div>
|
|
2027
2306
|
|
|
2307
|
+
<!-- Step 1: Select Network -->
|
|
2028
2308
|
<div class="step" id="kwespay-step1">
|
|
2029
2309
|
<div class="kp-topbar">
|
|
2030
2310
|
<div class="kp-topbar-brand">
|
|
2031
|
-
<div class="kp-topbar-dot"></div>
|
|
2032
2311
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2033
2312
|
</div>
|
|
2034
|
-
|
|
2035
2313
|
</div>
|
|
2036
2314
|
<div class="kp-amount-block">
|
|
2037
2315
|
<div class="kp-amount-label">Total due</div>
|
|
@@ -2059,21 +2337,19 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2059
2337
|
</div>
|
|
2060
2338
|
</div>
|
|
2061
2339
|
|
|
2340
|
+
<!-- Step 2: Select Token -->
|
|
2062
2341
|
<div class="step" id="kwespay-step2">
|
|
2063
2342
|
<div class="kp-topbar">
|
|
2064
2343
|
<div class="kp-topbar-brand">
|
|
2065
2344
|
<button class="kp-back-btn" id="kwespay-back2">
|
|
2066
2345
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2067
2346
|
</button>
|
|
2068
|
-
<div class="kp-topbar-dot"></div>
|
|
2069
2347
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2070
2348
|
</div>
|
|
2071
|
-
|
|
2072
2349
|
</div>
|
|
2073
2350
|
<div class="kp-amount-block">
|
|
2074
2351
|
<div class="kp-amount-label">Total due</div>
|
|
2075
2352
|
<div class="kp-amount-value">${fiatAmount} ${currency}</div>
|
|
2076
|
-
<div class="kp-amount-hint">Select a token — exact amount shown at review</div>
|
|
2077
2353
|
</div>
|
|
2078
2354
|
<div class="progress-section">
|
|
2079
2355
|
<div class="progress-info">
|
|
@@ -2102,16 +2378,15 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2102
2378
|
</div>
|
|
2103
2379
|
</div>
|
|
2104
2380
|
|
|
2381
|
+
<!-- Step 3: Review & Pay -->
|
|
2105
2382
|
<div class="step" id="kwespay-step3">
|
|
2106
2383
|
<div class="kp-topbar">
|
|
2107
2384
|
<div class="kp-topbar-brand">
|
|
2108
2385
|
<button class="kp-back-btn" id="kwespay-back3">
|
|
2109
2386
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2110
2387
|
</button>
|
|
2111
|
-
<div class="kp-topbar-dot"></div>
|
|
2112
2388
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2113
2389
|
</div>
|
|
2114
|
-
|
|
2115
2390
|
</div>
|
|
2116
2391
|
<div class="progress-section">
|
|
2117
2392
|
<div class="progress-info">
|
|
@@ -2133,37 +2408,35 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2133
2408
|
<span id="kwespay-quoteTimerText">—</span>
|
|
2134
2409
|
</div>
|
|
2135
2410
|
</div>
|
|
2136
|
-
|
|
2137
2411
|
<div class="kp-detail-block">
|
|
2138
2412
|
<div class="kp-detail-row">
|
|
2139
|
-
<span class="kp-detail-key">
|
|
2413
|
+
<span class="kp-detail-key">Wallet</span>
|
|
2140
2414
|
<span class="kp-detail-val" id="kwespay-connectedWalletAddress">—</span>
|
|
2141
2415
|
</div>
|
|
2142
2416
|
<div class="kp-detail-row">
|
|
2143
|
-
<span class="kp-detail-key">
|
|
2417
|
+
<span class="kp-detail-key">Network</span>
|
|
2144
2418
|
<span class="kp-detail-val" id="kwespay-summaryNetwork">—</span>
|
|
2145
2419
|
</div>
|
|
2146
2420
|
<div class="kp-detail-row">
|
|
2147
|
-
<span class="kp-detail-key">
|
|
2421
|
+
<span class="kp-detail-key">Token</span>
|
|
2148
2422
|
<span class="kp-detail-val accent" id="kwespay-summaryToken">—</span>
|
|
2149
2423
|
</div>
|
|
2150
2424
|
</div>
|
|
2151
|
-
|
|
2152
2425
|
<div class="kp-fee-block">
|
|
2153
2426
|
<div class="kp-fee-header">
|
|
2154
2427
|
<span class="material-symbols-outlined">receipt_long</span>
|
|
2155
2428
|
<span class="kp-fee-header-text">Fee Breakdown</span>
|
|
2156
2429
|
</div>
|
|
2157
2430
|
<div class="kp-detail-row">
|
|
2158
|
-
<span class="kp-detail-key">
|
|
2431
|
+
<span class="kp-detail-key">Payment amount</span>
|
|
2159
2432
|
<span class="kp-detail-val" id="kwespay-feePaymentAmount">—</span>
|
|
2160
2433
|
</div>
|
|
2161
2434
|
<div class="kp-detail-row">
|
|
2162
|
-
<span class="kp-detail-key">
|
|
2435
|
+
<span class="kp-detail-key">Platform fee (0.25%)</span>
|
|
2163
2436
|
<span class="kp-detail-val" id="kwespay-feePlatformFee">—</span>
|
|
2164
2437
|
</div>
|
|
2165
2438
|
<div class="kp-detail-row">
|
|
2166
|
-
<span class="kp-detail-key">
|
|
2439
|
+
<span class="kp-detail-key">Vendor receives</span>
|
|
2167
2440
|
<span class="kp-detail-val green" id="kwespay-feeVendorAmount">—</span>
|
|
2168
2441
|
</div>
|
|
2169
2442
|
</div>
|
|
@@ -2174,14 +2447,18 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2174
2447
|
</div>
|
|
2175
2448
|
</div>
|
|
2176
2449
|
|
|
2450
|
+
<!-- Step 4: Processing / Confirming / Success (mutating sub-views) -->
|
|
2177
2451
|
<div class="step" id="kwespay-step4">
|
|
2178
2452
|
<div class="kp-topbar">
|
|
2179
2453
|
<div class="kp-topbar-brand">
|
|
2180
|
-
<div class="kp-topbar-dot"></div>
|
|
2181
|
-
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2454
|
+
<div class="kp-topbar-dot" id="kwespay-step4-dot"></div>
|
|
2455
|
+
<span class="kp-topbar-name" id="kwespay-step4-title">KwesPay Checkout</span>
|
|
2182
2456
|
</div>
|
|
2457
|
+
|
|
2183
2458
|
</div>
|
|
2184
|
-
|
|
2459
|
+
|
|
2460
|
+
<!-- Sub-view: processing (wallet approval + on-chain) -->
|
|
2461
|
+
<div id="kwespay-view-processing" class="loading-container" style="flex:1">
|
|
2185
2462
|
<div class="spinner-wrapper">
|
|
2186
2463
|
<div class="spinner-ring"></div>
|
|
2187
2464
|
<div class="spinner-ring-2"></div>
|
|
@@ -2199,62 +2476,92 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2199
2476
|
</div>
|
|
2200
2477
|
</div>
|
|
2201
2478
|
</div>
|
|
2202
|
-
</div>
|
|
2203
2479
|
|
|
2204
|
-
|
|
2205
|
-
<div class="
|
|
2206
|
-
<div class="
|
|
2207
|
-
<div class="
|
|
2208
|
-
<
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
CONFIRMED
|
|
2480
|
+
<!-- Sub-view: confirming (on-chain done, polling backend) -->
|
|
2481
|
+
<div id="kwespay-view-confirming" class="loading-container" style="flex:1;display:none">
|
|
2482
|
+
<div class="spinner-wrapper">
|
|
2483
|
+
<div class="spinner-ring kp-spin-soft"></div>
|
|
2484
|
+
<div class="spinner-ring-2 kp-spin-soft"></div>
|
|
2485
|
+
<div class="spinner-icon kp-spinner-icon-green">
|
|
2486
|
+
<span class="material-symbols-outlined" style="color:var(--kp-green)">cloud_sync</span>
|
|
2487
|
+
</div>
|
|
2213
2488
|
</div>
|
|
2489
|
+
<h2 class="headline">Confirming Payment</h2>
|
|
2490
|
+
<p class="body-text" id="kwespay-confirmingText">Waiting for network confirmation.</p>
|
|
2214
2491
|
</div>
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2492
|
+
|
|
2493
|
+
<!-- Sub-view: success receipt -->
|
|
2494
|
+
<div id="kwespay-view-success" style="display:none;flex-direction:column;flex:1;overflow:hidden">
|
|
2495
|
+
|
|
2496
|
+
<!-- Thin green confirmed bar -->
|
|
2497
|
+
<div class="kp-confirmed-bar">
|
|
2498
|
+
<span class="material-symbols-outlined" style="font-size:13px;color:var(--kp-green)">check_circle</span>
|
|
2499
|
+
<span>Payment confirmed on-chain</span>
|
|
2218
2500
|
</div>
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
<div class="
|
|
2222
|
-
<div class="
|
|
2223
|
-
<span class="
|
|
2224
|
-
<div class="tx-hash-row">
|
|
2225
|
-
<span class="tx-value" id="kwespay-txHash">—</span>
|
|
2226
|
-
<a class="explorer-link" id="kwespay-explorerLink" target="_blank" rel="noopener noreferrer">
|
|
2227
|
-
<span class="material-symbols-outlined">open_in_new</span>
|
|
2228
|
-
</a>
|
|
2229
|
-
</div>
|
|
2230
|
-
</div>
|
|
2231
|
-
<div class="tx-row">
|
|
2232
|
-
<span class="tx-label">amount paid</span>
|
|
2233
|
-
<span class="tx-value" id="kwespay-txFiatAmount">${fiatAmount} ${currency}</span>
|
|
2501
|
+
|
|
2502
|
+
<!-- Scrollable receipt body -->
|
|
2503
|
+
<div class="loading-container" style="flex:1;padding-bottom:0;justify-content:flex-start;padding-top:20px;overflow-y:auto">
|
|
2504
|
+
<div class="success-icon">
|
|
2505
|
+
<span class="material-symbols-outlined">check_circle</span>
|
|
2234
2506
|
</div>
|
|
2235
|
-
<
|
|
2236
|
-
|
|
2237
|
-
|
|
2507
|
+
<h2 class="headline">Payment Successful</h2>
|
|
2508
|
+
<p class="body-text">Your transaction has been confirmed on-chain.</p>
|
|
2509
|
+
|
|
2510
|
+
<div class="tx-details">
|
|
2511
|
+
<div class="tx-row">
|
|
2512
|
+
<span class="tx-label">Tx hash</span>
|
|
2513
|
+
<div class="tx-hash-row">
|
|
2514
|
+
<span class="tx-value" id="kwespay-txHash">—</span>
|
|
2515
|
+
<a class="explorer-link" id="kwespay-explorerLink" target="_blank" rel="noopener noreferrer">
|
|
2516
|
+
<span class="material-symbols-outlined">open_in_new</span>
|
|
2517
|
+
</a>
|
|
2518
|
+
</div>
|
|
2519
|
+
</div>
|
|
2520
|
+
<div class="tx-row">
|
|
2521
|
+
<span class="tx-label">Reference</span>
|
|
2522
|
+
<span class="tx-value" id="kwespay-txRef">—</span>
|
|
2523
|
+
</div>
|
|
2524
|
+
<div class="tx-row">
|
|
2525
|
+
<span class="tx-label">Amount paid</span>
|
|
2526
|
+
<span class="tx-value" id="kwespay-txFiatAmount">${fiatAmount} ${currency}</span>
|
|
2527
|
+
</div>
|
|
2528
|
+
<div class="tx-row">
|
|
2529
|
+
<span class="tx-label">Crypto amount</span>
|
|
2530
|
+
<span class="tx-value" id="kwespay-txCryptoAmount">—</span>
|
|
2531
|
+
</div>
|
|
2532
|
+
<div class="tx-row">
|
|
2533
|
+
<span class="tx-label">Network</span>
|
|
2534
|
+
<span class="tx-value" id="kwespay-txNetwork">—</span>
|
|
2535
|
+
</div>
|
|
2536
|
+
<div class="tx-row">
|
|
2537
|
+
<span class="tx-label">Time</span>
|
|
2538
|
+
<span class="tx-value" id="kwespay-txTime">—</span>
|
|
2539
|
+
</div>
|
|
2238
2540
|
</div>
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2541
|
+
</div>
|
|
2542
|
+
|
|
2543
|
+
<!-- Countdown + Done button -->
|
|
2544
|
+
<div class="bottom-action" style="padding-top:10px">
|
|
2545
|
+
<!-- Countdown bar -->
|
|
2546
|
+
<div class="kp-countdown-track">
|
|
2547
|
+
<div class="kp-countdown-bar" id="kwespay-receiptCountdownBar"></div>
|
|
2242
2548
|
</div>
|
|
2549
|
+
<p class="kp-countdown-label" id="kwespay-receiptCountdown">Closing in 10s</p>
|
|
2550
|
+
<button class="action-btn" id="kwespay-closeSuccessBtn" style="margin-top:8px">Done</button>
|
|
2551
|
+
</div>
|
|
2552
|
+
|
|
2553
|
+
<div class="kp-footer">
|
|
2554
|
+
<span class="material-symbols-outlined kp-footer-lock" style="font-size:12px">lock</span>
|
|
2555
|
+
<span class="kp-footer-text">Secured by <span>KwesPay</span> · On-chain verified</span>
|
|
2243
2556
|
</div>
|
|
2244
|
-
</div>
|
|
2245
|
-
<div class="bottom-action">
|
|
2246
|
-
<button class="action-btn" id="kwespay-closeSuccessBtn">Done</button>
|
|
2247
|
-
</div>
|
|
2248
|
-
<div class="kp-footer">
|
|
2249
|
-
<span class="material-symbols-outlined kp-footer-lock" style="font-size:12px">lock</span>
|
|
2250
|
-
<span class="kp-footer-text">Secured by <span>KwesPay</span> · On-chain verified</span>
|
|
2251
2557
|
</div>
|
|
2252
2558
|
</div>
|
|
2253
2559
|
|
|
2254
|
-
|
|
2560
|
+
<!-- Step 5: Error / Failed -->
|
|
2561
|
+
<div class="step" id="kwespay-step5">
|
|
2255
2562
|
<div class="kp-topbar">
|
|
2256
2563
|
<div class="kp-topbar-brand">
|
|
2257
|
-
<div class="kp-topbar-dot" style="background:var(--kp-red);box-shadow:
|
|
2564
|
+
<div class="kp-topbar-dot" style="background:var(--kp-red);box-shadow:0 0 8px rgba(244,63,94,0.35);animation:none"></div>
|
|
2258
2565
|
<span class="kp-topbar-name">Payment Failed</span>
|
|
2259
2566
|
</div>
|
|
2260
2567
|
</div>
|
|
@@ -2264,6 +2571,12 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2264
2571
|
</div>
|
|
2265
2572
|
<h2 class="headline" id="kwespay-errorTitle">Something went wrong</h2>
|
|
2266
2573
|
<p class="body-text" id="kwespay-errorMessage">An error occurred while processing your payment.</p>
|
|
2574
|
+
|
|
2575
|
+
<!-- Hint strip — gives context without overwhelming the user -->
|
|
2576
|
+
<div class="kp-error-hint">
|
|
2577
|
+
<span class="material-symbols-outlined" style="font-size:14px;color:var(--kp-muted)">info</span>
|
|
2578
|
+
<span>Your funds have not been charged. You can retry safely.</span>
|
|
2579
|
+
</div>
|
|
2267
2580
|
</div>
|
|
2268
2581
|
<div class="bottom-action">
|
|
2269
2582
|
<button class="action-btn" id="kwespay-retryPayment">Try Again</button>
|
|
@@ -2310,27 +2623,29 @@ const DomMethods = {
|
|
|
2310
2623
|
overlay.className = "kwespay-overlay";
|
|
2311
2624
|
overlay.id = "kwespay-widget-overlay";
|
|
2312
2625
|
|
|
2626
|
+
const container = document.createElement("div");
|
|
2627
|
+
container.className = "kwespay-container";
|
|
2628
|
+
container.id = "kwespay-widget-container";
|
|
2629
|
+
|
|
2313
2630
|
const closeBtn = document.createElement("button");
|
|
2314
2631
|
closeBtn.className = "kwespay-close-btn";
|
|
2315
2632
|
closeBtn.innerHTML = "×";
|
|
2316
2633
|
closeBtn.onclick = () => this.close();
|
|
2317
|
-
|
|
2634
|
+
container.appendChild(closeBtn);
|
|
2318
2635
|
|
|
2319
|
-
const
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
this.config.amount,
|
|
2636
|
+
const stepsWrapper = document.createElement("div");
|
|
2637
|
+
stepsWrapper.className = "kwespay-steps-wrapper";
|
|
2638
|
+
stepsWrapper.innerHTML = getStepTemplates(
|
|
2639
|
+
this._displayAmount,
|
|
2324
2640
|
this.config.currency
|
|
2325
2641
|
);
|
|
2642
|
+
container.appendChild(stepsWrapper);
|
|
2326
2643
|
|
|
2327
2644
|
overlay.appendChild(container);
|
|
2328
2645
|
document.body.appendChild(overlay);
|
|
2329
2646
|
|
|
2330
2647
|
this._setupEventListeners();
|
|
2331
2648
|
this._setupSwipeToClose(container);
|
|
2332
|
-
|
|
2333
|
-
// Clicking outside the container does NOT close the widget (intentional)
|
|
2334
2649
|
},
|
|
2335
2650
|
|
|
2336
2651
|
_setupSwipeToClose(container) {
|
|
@@ -2501,8 +2816,11 @@ const NavMethods = {
|
|
|
2501
2816
|
targetStep = document.getElementById(`kwespay-step-${stepNumber}`);
|
|
2502
2817
|
else targetStep = document.getElementById(`kwespay-step${stepNumber}`);
|
|
2503
2818
|
|
|
2504
|
-
if (targetStep)
|
|
2505
|
-
|
|
2819
|
+
if (targetStep) {
|
|
2820
|
+
targetStep.classList.add("active");
|
|
2821
|
+
} else {
|
|
2822
|
+
console.warn(`[KwesPayWidget] Step not found: ${stepNumber}`);
|
|
2823
|
+
}
|
|
2506
2824
|
},
|
|
2507
2825
|
|
|
2508
2826
|
_removeCustomStep(id) {
|
|
@@ -2512,13 +2830,16 @@ const NavMethods = {
|
|
|
2512
2830
|
_showError(title, message) {
|
|
2513
2831
|
const titleEl = document.getElementById("kwespay-errorTitle");
|
|
2514
2832
|
const msgEl = document.getElementById("kwespay-errorMessage");
|
|
2515
|
-
if (titleEl) titleEl.textContent = title;
|
|
2516
|
-
if (msgEl)
|
|
2517
|
-
|
|
2833
|
+
if (titleEl) titleEl.textContent = title ?? "Something went wrong";
|
|
2834
|
+
if (msgEl)
|
|
2835
|
+
msgEl.textContent =
|
|
2836
|
+
message ?? "An unexpected error occurred. Please try again.";
|
|
2837
|
+
this._goToStep(5);
|
|
2518
2838
|
},
|
|
2519
2839
|
|
|
2520
2840
|
_reset() {
|
|
2521
2841
|
this._clearQuoteTimer();
|
|
2842
|
+
this._stopReceiptCountdown();
|
|
2522
2843
|
this._removeCustomStep("kwespay-step-wallet-picker");
|
|
2523
2844
|
this._removeCustomStep("kwespay-step-wc");
|
|
2524
2845
|
this.state.selectedNetwork = null;
|
|
@@ -2586,23 +2907,13 @@ const NetworkMethods = {
|
|
|
2586
2907
|
const listItem = document.createElement("div");
|
|
2587
2908
|
listItem.className = "list-item";
|
|
2588
2909
|
listItem.innerHTML = `
|
|
2589
|
-
<div class="item-icon"><img src="${network.logo}" alt="${
|
|
2590
|
-
network.name
|
|
2591
|
-
}" /></div>
|
|
2910
|
+
<div class="item-icon"><img src="${network.logo}" alt="${network.name}" /></div>
|
|
2592
2911
|
<div class="item-info">
|
|
2593
2912
|
<div class="item-name-row">
|
|
2594
2913
|
<p class="item-name">${network.name}</p>
|
|
2595
|
-
|
|
2596
|
-
network.type === "testnet"
|
|
2597
|
-
? '<span class="item-badge badge-testnet">Testnet</span>'
|
|
2598
|
-
: ""
|
|
2599
|
-
}
|
|
2914
|
+
|
|
2600
2915
|
</div>
|
|
2601
|
-
|
|
2602
|
-
network.type === "mainnet"
|
|
2603
|
-
? "Mainnet · Production"
|
|
2604
|
-
: "Testnet · Development"
|
|
2605
|
-
}</p>
|
|
2916
|
+
|
|
2606
2917
|
</div>
|
|
2607
2918
|
<span class="material-symbols-outlined item-chevron">chevron_right</span>
|
|
2608
2919
|
`;
|
|
@@ -2741,7 +3052,7 @@ const WalletMethods = {
|
|
|
2741
3052
|
<button class="kp-back-btn" id="kwespay-picker-back">
|
|
2742
3053
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2743
3054
|
</button>
|
|
2744
|
-
|
|
3055
|
+
|
|
2745
3056
|
<span class="kp-topbar-name">Connect Wallet</span>
|
|
2746
3057
|
</div>
|
|
2747
3058
|
</div>
|
|
@@ -2911,7 +3222,7 @@ const WalletMethods = {
|
|
|
2911
3222
|
<button class="kp-back-btn" id="kwespay-wc-back">
|
|
2912
3223
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2913
3224
|
</button>
|
|
2914
|
-
|
|
3225
|
+
|
|
2915
3226
|
<span class="kp-topbar-name">Scan QR Code</span>
|
|
2916
3227
|
</div>
|
|
2917
3228
|
</div>
|
|
@@ -2951,7 +3262,7 @@ const WalletMethods = {
|
|
|
2951
3262
|
<button class="kp-back-btn" id="kwespay-wc-back">
|
|
2952
3263
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2953
3264
|
</button>
|
|
2954
|
-
|
|
3265
|
+
|
|
2955
3266
|
<span class="kp-topbar-name">Connect Wallet</span>
|
|
2956
3267
|
</div>
|
|
2957
3268
|
</div>
|
|
@@ -3101,8 +3412,6 @@ const WalletMethods = {
|
|
|
3101
3412
|
},
|
|
3102
3413
|
};
|
|
3103
3414
|
|
|
3104
|
-
const PLATFORM_FEE_BPS = 25;
|
|
3105
|
-
|
|
3106
3415
|
function formatUnits(rawBigInt, decimals) {
|
|
3107
3416
|
const divisor = BigInt(10 ** decimals);
|
|
3108
3417
|
const whole = rawBigInt / divisor;
|
|
@@ -3130,6 +3439,31 @@ function formatUnits(rawBigInt, decimals) {
|
|
|
3130
3439
|
return `0.${fracFull.slice(0, firstSig) + sigSlice}`;
|
|
3131
3440
|
}
|
|
3132
3441
|
|
|
3442
|
+
function _validateQuoteDecimals(
|
|
3443
|
+
amountBaseUnits,
|
|
3444
|
+
fiatAmount,
|
|
3445
|
+
decimals,
|
|
3446
|
+
usdPrice
|
|
3447
|
+
) {
|
|
3448
|
+
try {
|
|
3449
|
+
if (!usdPrice || usdPrice <= 0) return { ok: true };
|
|
3450
|
+
const humanAmount = Number(BigInt(amountBaseUnits)) / 10 ** decimals;
|
|
3451
|
+
const impliedUsd = humanAmount * usdPrice;
|
|
3452
|
+
const ratio = impliedUsd / fiatAmount;
|
|
3453
|
+
if (ratio < 0.5 || ratio > 10) {
|
|
3454
|
+
return {
|
|
3455
|
+
ok: false,
|
|
3456
|
+
reason: `Amount sanity check failed: got ${humanAmount} (≈$${impliedUsd.toFixed(
|
|
3457
|
+
6
|
|
3458
|
+
)}) for $${fiatAmount} fiat.`,
|
|
3459
|
+
};
|
|
3460
|
+
}
|
|
3461
|
+
return { ok: true };
|
|
3462
|
+
} catch {
|
|
3463
|
+
return { ok: true };
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3133
3467
|
const QuoteMethods = {
|
|
3134
3468
|
async _loadReviewStep() {
|
|
3135
3469
|
this._clearQuoteTimer();
|
|
@@ -3142,45 +3476,63 @@ const QuoteMethods = {
|
|
|
3142
3476
|
const proceedBtn = document.getElementById("kwespay-proceedToPayment");
|
|
3143
3477
|
|
|
3144
3478
|
if (cryptoLine) {
|
|
3145
|
-
cryptoLine.textContent = "loading
|
|
3479
|
+
cryptoLine.textContent = "loading...";
|
|
3146
3480
|
cryptoLine.classList.add("loading");
|
|
3147
3481
|
}
|
|
3148
3482
|
if (timerEl) timerEl.style.display = "none";
|
|
3149
3483
|
if (proceedBtn) proceedBtn.disabled = true;
|
|
3150
3484
|
|
|
3151
3485
|
try {
|
|
3152
|
-
const
|
|
3153
|
-
|
|
3486
|
+
const quote = await this.paymentService.getQuote({
|
|
3487
|
+
vendorIdentifier: this.config.vendorId,
|
|
3154
3488
|
cryptoCurrency: this.state.selectedToken,
|
|
3155
3489
|
fiatAmount: this.config.amount,
|
|
3156
3490
|
fiatCurrency: this.config.currency,
|
|
3157
3491
|
network: this.state.selectedNetwork,
|
|
3158
|
-
payerWalletAddress: this.walletService.getAddress(),
|
|
3159
3492
|
});
|
|
3160
3493
|
|
|
3161
|
-
this.state.
|
|
3162
|
-
|
|
3163
|
-
const decimals = this.state.selectedTokenConfig?.decimals ?? 6;
|
|
3164
|
-
const amountBig = BigInt(payload.amountBaseUnits);
|
|
3165
|
-
const feeNum = (Number(amountBig) * PLATFORM_FEE_BPS) / 10000;
|
|
3166
|
-
const feeBig = BigInt(Math.max(1, Math.round(feeNum)));
|
|
3167
|
-
const vendorBig = amountBig - feeBig;
|
|
3494
|
+
const decimals = this.state.selectedTokenConfig?.decimals ?? 18;
|
|
3168
3495
|
const sym = this.state.selectedToken;
|
|
3169
|
-
|
|
3496
|
+
|
|
3497
|
+
const check = _validateQuoteDecimals(
|
|
3498
|
+
quote.amountBaseUnits,
|
|
3499
|
+
this.config.amount,
|
|
3500
|
+
decimals,
|
|
3501
|
+
quote.usdPriceSnapshot
|
|
3502
|
+
);
|
|
3503
|
+
if (!check.ok) {
|
|
3504
|
+
console.error("[KwesPay] Quote sanity check failed:", check.reason);
|
|
3505
|
+
throw new Error(
|
|
3506
|
+
`Quote returned an implausible amount for ${sym}. Please contact support.`
|
|
3507
|
+
);
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
this.state.currentPayload = {
|
|
3511
|
+
...quote,
|
|
3512
|
+
vendorIdentifier: this.config.vendorId,
|
|
3513
|
+
fiatAmount: this.config.amount,
|
|
3514
|
+
fiatCurrency: this.config.currency,
|
|
3515
|
+
};
|
|
3516
|
+
|
|
3517
|
+
const fmt = (n) =>
|
|
3518
|
+
formatCryptoAmount(parseFloat(formatUnits(n, decimals)), sym);
|
|
3519
|
+
|
|
3520
|
+
const amountBig = BigInt(quote.amountBaseUnits);
|
|
3521
|
+
const totalBig = BigInt(quote.totalBaseUnits);
|
|
3522
|
+
const feeBig = totalBig - amountBig;
|
|
3170
3523
|
|
|
3171
3524
|
if (cryptoLine) {
|
|
3172
|
-
cryptoLine.textContent = fmt(
|
|
3525
|
+
cryptoLine.textContent = fmt(totalBig);
|
|
3173
3526
|
cryptoLine.classList.remove("loading");
|
|
3174
3527
|
}
|
|
3175
|
-
if (feeAmount) feeAmount.textContent = fmt(
|
|
3528
|
+
if (feeAmount) feeAmount.textContent = fmt(totalBig);
|
|
3176
3529
|
if (feePlatform) feePlatform.textContent = fmt(feeBig);
|
|
3177
|
-
if (feeVendor)
|
|
3178
|
-
feeVendor.textContent = fmt(vendorBig < 0n ? 0n : vendorBig);
|
|
3530
|
+
if (feeVendor) feeVendor.textContent = fmt(amountBig);
|
|
3179
3531
|
if (proceedBtn) proceedBtn.disabled = false;
|
|
3180
3532
|
|
|
3181
|
-
this._startQuoteTimer(
|
|
3533
|
+
this._startQuoteTimer(quote.expiresAt);
|
|
3182
3534
|
} catch (err) {
|
|
3183
|
-
console.error("[
|
|
3535
|
+
console.error("[KwesPay] Quote fetch failed:", err.message);
|
|
3184
3536
|
if (cryptoLine) {
|
|
3185
3537
|
cryptoLine.textContent = "Could not load please try again.";
|
|
3186
3538
|
cryptoLine.classList.remove("loading");
|
|
@@ -3200,7 +3552,8 @@ const QuoteMethods = {
|
|
|
3200
3552
|
const secs = Math.ceil(remaining / 1000);
|
|
3201
3553
|
const mins = Math.floor(secs / 60);
|
|
3202
3554
|
const s = secs % 60;
|
|
3203
|
-
|
|
3555
|
+
|
|
3556
|
+
timerText.textContent = `Quote expires in ${mins}:${String(s).padStart(
|
|
3204
3557
|
2,
|
|
3205
3558
|
"0"
|
|
3206
3559
|
)}`;
|
|
@@ -3210,7 +3563,7 @@ const QuoteMethods = {
|
|
|
3210
3563
|
(secs <= 0 ? " expired" : "");
|
|
3211
3564
|
|
|
3212
3565
|
if (secs <= 0) {
|
|
3213
|
-
timerText.textContent = "Refreshing your rate
|
|
3566
|
+
timerText.textContent = "Refreshing your rate...";
|
|
3214
3567
|
const proceedBtn = document.getElementById("kwespay-proceedToPayment");
|
|
3215
3568
|
if (proceedBtn) proceedBtn.disabled = true;
|
|
3216
3569
|
this._clearQuoteTimer();
|
|
@@ -3230,6 +3583,8 @@ const QuoteMethods = {
|
|
|
3230
3583
|
},
|
|
3231
3584
|
};
|
|
3232
3585
|
|
|
3586
|
+
const RECEIPT_DWELL_MS = 10_000;
|
|
3587
|
+
|
|
3233
3588
|
const PaymentMethods = {
|
|
3234
3589
|
async _handlePaymentProcessing() {
|
|
3235
3590
|
if (!this.state.currentPayload) {
|
|
@@ -3243,6 +3598,7 @@ const PaymentMethods = {
|
|
|
3243
3598
|
try {
|
|
3244
3599
|
this._clearQuoteTimer();
|
|
3245
3600
|
this._goToStep(4);
|
|
3601
|
+
this._setProcessingView("processing");
|
|
3246
3602
|
|
|
3247
3603
|
const setStatus = (title, text) => {
|
|
3248
3604
|
const titleEl = document.getElementById("kwespay-processingTitle");
|
|
@@ -3253,27 +3609,34 @@ const PaymentMethods = {
|
|
|
3253
3609
|
|
|
3254
3610
|
const isWC = this.walletService.connectionType === "walletconnect";
|
|
3255
3611
|
const isMobile = this.walletService.isMobile();
|
|
3256
|
-
const strictMobile = isWC && isMobile;
|
|
3612
|
+
const strictMobile = isWC && isMobile;
|
|
3257
3613
|
const provider = this.walletService.getProvider();
|
|
3258
3614
|
const targetChainId = this.state.selectedChainId;
|
|
3259
3615
|
|
|
3260
|
-
if (!provider)
|
|
3261
|
-
|
|
3616
|
+
if (!provider) {
|
|
3617
|
+
throw Object.assign(
|
|
3618
|
+
new Error(
|
|
3619
|
+
"No wallet provider available. Please reconnect your wallet."
|
|
3620
|
+
),
|
|
3621
|
+
{
|
|
3622
|
+
code: "NO_PROVIDER",
|
|
3623
|
+
}
|
|
3624
|
+
);
|
|
3625
|
+
}
|
|
3262
3626
|
|
|
3263
|
-
const alive = await this.walletService
|
|
3627
|
+
const alive = await this.walletService
|
|
3628
|
+
.isSessionAlive()
|
|
3629
|
+
.catch(() => false);
|
|
3264
3630
|
if (!alive) {
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3631
|
+
await this.walletService.disconnect().catch(() => {});
|
|
3632
|
+
throw Object.assign(
|
|
3633
|
+
new Error(
|
|
3634
|
+
"Your wallet session expired. Please reconnect your wallet."
|
|
3635
|
+
),
|
|
3636
|
+
{ code: "SESSION_EXPIRED" }
|
|
3271
3637
|
);
|
|
3272
|
-
err.code = "SESSION_EXPIRED";
|
|
3273
|
-
throw err;
|
|
3274
3638
|
}
|
|
3275
3639
|
|
|
3276
|
-
|
|
3277
3640
|
if (isMobile) {
|
|
3278
3641
|
document
|
|
3279
3642
|
.getElementById("kwespay-mobileTransactionInstruction")
|
|
@@ -3281,17 +3644,20 @@ const PaymentMethods = {
|
|
|
3281
3644
|
}
|
|
3282
3645
|
|
|
3283
3646
|
if (strictMobile) {
|
|
3284
|
-
|
|
3285
3647
|
await this._assertMobileChain(provider, targetChainId);
|
|
3286
3648
|
} else {
|
|
3287
|
-
|
|
3649
|
+
let rawChain;
|
|
3650
|
+
try {
|
|
3651
|
+
rawChain = await provider.request({ method: "eth_chainId" });
|
|
3652
|
+
} catch {
|
|
3653
|
+
throw Object.assign(
|
|
3654
|
+
new Error(
|
|
3655
|
+
"Could not read the current network from your wallet. Please try again."
|
|
3656
|
+
),
|
|
3657
|
+
{ code: "NETWORK_ERROR" }
|
|
3658
|
+
);
|
|
3659
|
+
}
|
|
3288
3660
|
const currentChainId = parseInt(rawChain, 16);
|
|
3289
|
-
|
|
3290
|
-
console.log("[KwesPay] Desktop chain check —", {
|
|
3291
|
-
currentChainId,
|
|
3292
|
-
targetChainId,
|
|
3293
|
-
});
|
|
3294
|
-
|
|
3295
3661
|
if (currentChainId !== targetChainId) {
|
|
3296
3662
|
setStatus(
|
|
3297
3663
|
"Switching network…",
|
|
@@ -3304,11 +3670,9 @@ const PaymentMethods = {
|
|
|
3304
3670
|
this.state.selectedToken,
|
|
3305
3671
|
this.state.selectedTokenConfig.decimals
|
|
3306
3672
|
);
|
|
3307
|
-
console.log("[KwesPay] Network switched");
|
|
3308
3673
|
}
|
|
3309
3674
|
}
|
|
3310
3675
|
|
|
3311
|
-
|
|
3312
3676
|
if (strictMobile) {
|
|
3313
3677
|
setStatus(
|
|
3314
3678
|
"Opening your wallet…",
|
|
@@ -3323,56 +3687,111 @@ const PaymentMethods = {
|
|
|
3323
3687
|
);
|
|
3324
3688
|
}
|
|
3325
3689
|
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3690
|
+
let receipt;
|
|
3691
|
+
try {
|
|
3692
|
+
receipt = await this.paymentService.createPayment({
|
|
3693
|
+
payload: this.state.currentPayload,
|
|
3694
|
+
walletProvider: provider,
|
|
3695
|
+
onStatusUpdate: setStatus,
|
|
3696
|
+
});
|
|
3697
|
+
} catch (payErr) {
|
|
3698
|
+
const msg =
|
|
3699
|
+
payErr?.message ?? "Payment submission failed. Please try again.";
|
|
3700
|
+
throw Object.assign(new Error(msg), {
|
|
3701
|
+
code: payErr?.code ?? "CONTRACT_ERROR",
|
|
3702
|
+
original: payErr,
|
|
3703
|
+
});
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
if (!receipt?.hash) {
|
|
3707
|
+
throw Object.assign(
|
|
3708
|
+
new Error(
|
|
3709
|
+
"Transaction was submitted but no hash was returned. Check your wallet for status."
|
|
3710
|
+
),
|
|
3711
|
+
{ code: "MISSING_HASH" }
|
|
3712
|
+
);
|
|
3713
|
+
}
|
|
3331
3714
|
|
|
3332
|
-
|
|
3333
3715
|
document
|
|
3334
3716
|
.getElementById("kwespay-mobileTransactionInstruction")
|
|
3335
3717
|
?.style.setProperty("display", "none");
|
|
3336
3718
|
|
|
3337
|
-
|
|
3338
|
-
const decimals = this.state.selectedTokenConfig?.decimals ?? 6;
|
|
3339
|
-
const amountBig = BigInt(this.state.currentPayload.amountBaseUnits);
|
|
3340
|
-
const cryptoDisplay = `${formatUnits(amountBig, decimals)} ${
|
|
3341
|
-
this.state.selectedToken
|
|
3342
|
-
}`;
|
|
3719
|
+
this._setProcessingView("confirming");
|
|
3343
3720
|
|
|
3344
|
-
|
|
3345
|
-
receipt.hash
|
|
3346
|
-
);
|
|
3347
|
-
document.getElementById(
|
|
3348
|
-
"kwespay-txFiatAmount"
|
|
3349
|
-
).textContent = `${this.config.amount} ${this.config.currency}`;
|
|
3350
|
-
document.getElementById("kwespay-txCryptoAmount").textContent =
|
|
3351
|
-
cryptoDisplay;
|
|
3352
|
-
document.getElementById("kwespay-txNetwork").textContent =
|
|
3353
|
-
this.state.selectedNetworkName;
|
|
3354
|
-
document.getElementById("kwespay-explorerLink").href =
|
|
3355
|
-
NETWORK_CONFIGS[this.state.selectedNetwork].explorer + receipt.hash;
|
|
3356
|
-
|
|
3357
|
-
this._goToStep(5);
|
|
3358
|
-
|
|
3359
|
-
dispatchWidgetEvent("paymentSuccess", {
|
|
3721
|
+
const onChainPayload = {
|
|
3360
3722
|
transactionReference: receipt.transactionReference,
|
|
3361
3723
|
paymentIdBytes32: receipt.paymentIdBytes32,
|
|
3362
3724
|
transactionHash: receipt.hash,
|
|
3725
|
+
transactionStatus: "pending",
|
|
3363
3726
|
fiatAmount: this.config.amount,
|
|
3364
3727
|
currency: this.config.currency,
|
|
3365
3728
|
token: this.state.selectedToken,
|
|
3366
3729
|
network: this.state.selectedNetwork,
|
|
3730
|
+
};
|
|
3731
|
+
|
|
3732
|
+
dispatchWidgetEvent("paymentSuccess", onChainPayload);
|
|
3733
|
+
this.config.onPaymentSuccess?.(onChainPayload);
|
|
3734
|
+
|
|
3735
|
+
const confirmed = await this._awaitBackendConfirmation(receipt);
|
|
3736
|
+
|
|
3737
|
+
const decimals = this.state.selectedTokenConfig?.decimals ?? 18;
|
|
3738
|
+
const sym = this.state.selectedToken;
|
|
3739
|
+
const payload = this.state.currentPayload;
|
|
3740
|
+
const totalBig = BigInt(payload.totalBaseUnits);
|
|
3741
|
+
const cryptoDisplay = formatCryptoAmount(
|
|
3742
|
+
parseFloat(formatUnits(totalBig, decimals)),
|
|
3743
|
+
sym
|
|
3744
|
+
);
|
|
3745
|
+
const now = new Date();
|
|
3746
|
+
const timeString = now.toLocaleTimeString([], {
|
|
3747
|
+
hour: "2-digit",
|
|
3748
|
+
minute: "2-digit",
|
|
3367
3749
|
});
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3750
|
+
const dateString = now.toLocaleDateString([], {
|
|
3751
|
+
month: "short",
|
|
3752
|
+
day: "numeric",
|
|
3753
|
+
year: "numeric",
|
|
3754
|
+
});
|
|
3755
|
+
|
|
3756
|
+
const setEl = (id, val) => {
|
|
3757
|
+
const el = document.getElementById(id);
|
|
3758
|
+
if (el) el.textContent = val;
|
|
3759
|
+
};
|
|
3760
|
+
|
|
3761
|
+
setEl("kwespay-txHash", truncateHash(receipt.hash));
|
|
3762
|
+
setEl(
|
|
3763
|
+
"kwespay-txFiatAmount",
|
|
3764
|
+
`${this.config.amount} ${this.config.currency}`
|
|
3374
3765
|
);
|
|
3766
|
+
setEl("kwespay-txCryptoAmount", cryptoDisplay);
|
|
3767
|
+
setEl("kwespay-txNetwork", this.state.selectedNetworkName);
|
|
3768
|
+
setEl("kwespay-txRef", receipt.transactionReference ?? "—");
|
|
3769
|
+
setEl("kwespay-txTime", `${dateString} · ${timeString}`);
|
|
3770
|
+
|
|
3771
|
+
const explorerLink = document.getElementById("kwespay-explorerLink");
|
|
3772
|
+
if (explorerLink) {
|
|
3773
|
+
explorerLink.href =
|
|
3774
|
+
(NETWORK_CONFIGS[this.state.selectedNetwork]?.explorer ?? "") +
|
|
3775
|
+
receipt.txHash;
|
|
3776
|
+
}
|
|
3777
|
+
|
|
3778
|
+
this._setProcessingView("success");
|
|
3779
|
+
this._startReceiptCountdown(RECEIPT_DWELL_MS);
|
|
3780
|
+
|
|
3781
|
+
const finalStatus = confirmed?.transactionStatus ?? "completed";
|
|
3782
|
+
const finalPayload = {
|
|
3783
|
+
transactionReference: receipt.transactionReference,
|
|
3784
|
+
paymentIdBytes32: receipt.paymentIdBytes32,
|
|
3785
|
+
transactionHash: receipt.hash,
|
|
3786
|
+
transactionStatus: finalStatus,
|
|
3787
|
+
fiatAmount: this.config.amount,
|
|
3788
|
+
currency: this.config.currency,
|
|
3789
|
+
token: this.state.selectedToken,
|
|
3790
|
+
network: this.state.selectedNetwork,
|
|
3791
|
+
};
|
|
3375
3792
|
|
|
3793
|
+
this._finalisePayment(finalPayload, false);
|
|
3794
|
+
} catch (error) {
|
|
3376
3795
|
document
|
|
3377
3796
|
.getElementById("kwespay-mobileTransactionInstruction")
|
|
3378
3797
|
?.style.setProperty("display", "none");
|
|
@@ -3381,71 +3800,171 @@ const PaymentMethods = {
|
|
|
3381
3800
|
let title = "Payment Failed";
|
|
3382
3801
|
let message = getErrorMessage(error, { token: this.state.selectedToken });
|
|
3383
3802
|
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3803
|
+
switch (error.code) {
|
|
3804
|
+
case "SESSION_EXPIRED":
|
|
3805
|
+
title = "Session Expired";
|
|
3806
|
+
message = error.message;
|
|
3807
|
+
break;
|
|
3808
|
+
case "NO_PROVIDER":
|
|
3809
|
+
title = "Wallet Disconnected";
|
|
3810
|
+
message = error.message;
|
|
3811
|
+
break;
|
|
3812
|
+
case "NETWORK_ERROR":
|
|
3813
|
+
title = "Network Error";
|
|
3814
|
+
message = error.message;
|
|
3815
|
+
break;
|
|
3816
|
+
case "WRONG_NETWORK":
|
|
3817
|
+
title = "Wrong Network";
|
|
3818
|
+
message = error.message;
|
|
3819
|
+
break;
|
|
3820
|
+
case "MISSING_HASH":
|
|
3821
|
+
title = "Unknown Transaction Status";
|
|
3822
|
+
message = error.message;
|
|
3823
|
+
break;
|
|
3824
|
+
default:
|
|
3825
|
+
if (errorType === "USER_REJECTED") {
|
|
3826
|
+
title = "Transaction Cancelled";
|
|
3827
|
+
message = "You rejected the transaction in your wallet.";
|
|
3828
|
+
} else if (errorType === "INSUFFICIENT_BALANCE") {
|
|
3829
|
+
title = "Insufficient Balance";
|
|
3830
|
+
} else if (!message) {
|
|
3831
|
+
message =
|
|
3832
|
+
"An unexpected error occurred. Please try again or contact support.";
|
|
3833
|
+
}
|
|
3395
3834
|
}
|
|
3396
3835
|
|
|
3836
|
+
console.error("[KwesPayWidget] Payment error:", {
|
|
3837
|
+
title,
|
|
3838
|
+
message,
|
|
3839
|
+
code: error.code,
|
|
3840
|
+
error,
|
|
3841
|
+
});
|
|
3842
|
+
|
|
3397
3843
|
this._showError(title, message);
|
|
3398
|
-
|
|
3844
|
+
this._failPayment(message, errorType);
|
|
3399
3845
|
}
|
|
3400
3846
|
},
|
|
3401
3847
|
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3848
|
+
_setProcessingView(view) {
|
|
3849
|
+
["processing", "confirming", "success"].forEach((v) => {
|
|
3850
|
+
const el = document.getElementById(`kwespay-view-${v}`);
|
|
3851
|
+
if (!el) return;
|
|
3852
|
+
el.style.display = v === view ? (v === "success" ? "flex" : "") : "none";
|
|
3853
|
+
});
|
|
3854
|
+
|
|
3855
|
+
const dot = document.getElementById("kwespay-step4-dot");
|
|
3856
|
+
const title = document.getElementById("kwespay-step4-title");
|
|
3857
|
+
const secure = document.getElementById("kwespay-step4-secure");
|
|
3858
|
+
|
|
3859
|
+
if (view === "success") {
|
|
3860
|
+
if (dot) {
|
|
3861
|
+
dot.style.background = "var(--kp-green)";
|
|
3862
|
+
dot.style.boxShadow = "0 0 8px rgba(16,185,129,0.4)";
|
|
3863
|
+
dot.style.animation = "none";
|
|
3864
|
+
}
|
|
3865
|
+
if (title) title.textContent = "Payment Complete";
|
|
3866
|
+
if (secure) secure.style.display = "flex";
|
|
3867
|
+
} else {
|
|
3868
|
+
if (dot) {
|
|
3869
|
+
dot.style.background = "var(--kp-accent)";
|
|
3870
|
+
dot.style.boxShadow = "0 0 8px var(--kp-accent-glow)";
|
|
3871
|
+
dot.style.animation = "";
|
|
3872
|
+
}
|
|
3873
|
+
if (title) title.textContent = "KwesPay Checkout";
|
|
3874
|
+
if (secure) secure.style.display = "none";
|
|
3875
|
+
}
|
|
3876
|
+
},
|
|
3877
|
+
|
|
3878
|
+
_startReceiptCountdown(totalMs) {
|
|
3879
|
+
this._stopReceiptCountdown();
|
|
3880
|
+
|
|
3881
|
+
const totalSec = Math.round(totalMs / 1000);
|
|
3882
|
+
let remaining = totalSec;
|
|
3883
|
+
|
|
3884
|
+
const countdownEl = document.getElementById("kwespay-receiptCountdown");
|
|
3885
|
+
const barEl = document.getElementById("kwespay-receiptCountdownBar");
|
|
3886
|
+
|
|
3887
|
+
const update = () => {
|
|
3888
|
+
if (countdownEl) {
|
|
3889
|
+
countdownEl.textContent =
|
|
3890
|
+
remaining > 0 ? `Closing in ${remaining}s` : "Closing…";
|
|
3891
|
+
}
|
|
3892
|
+
if (barEl) {
|
|
3893
|
+
const pct = (remaining / totalSec) * 100;
|
|
3894
|
+
barEl.style.width = `${pct}%`;
|
|
3895
|
+
barEl.style.background =
|
|
3896
|
+
remaining <= 3 ? "var(--kp-green)" : "var(--kp-accent)";
|
|
3897
|
+
}
|
|
3898
|
+
};
|
|
3899
|
+
|
|
3900
|
+
update();
|
|
3901
|
+
|
|
3902
|
+
this._receiptCountdownInterval = setInterval(() => {
|
|
3903
|
+
remaining -= 1;
|
|
3904
|
+
update();
|
|
3905
|
+
if (remaining <= 0) {
|
|
3906
|
+
this._stopReceiptCountdown();
|
|
3907
|
+
this.close();
|
|
3908
|
+
}
|
|
3909
|
+
}, 1000);
|
|
3910
|
+
},
|
|
3911
|
+
|
|
3912
|
+
_stopReceiptCountdown() {
|
|
3913
|
+
if (this._receiptCountdownInterval) {
|
|
3914
|
+
clearInterval(this._receiptCountdownInterval);
|
|
3915
|
+
this._receiptCountdownInterval = null;
|
|
3916
|
+
}
|
|
3917
|
+
},
|
|
3918
|
+
|
|
3919
|
+
_awaitBackendConfirmation(receipt) {
|
|
3920
|
+
return this.paymentService
|
|
3921
|
+
.pollTransactionStatus(receipt.transactionReference, {
|
|
3922
|
+
intervalMs: 4000,
|
|
3923
|
+
maxAttempts: 15,
|
|
3924
|
+
onStatus: (status) => {
|
|
3925
|
+
const el = document.getElementById("kwespay-confirmingText");
|
|
3926
|
+
if (el) el.textContent = `Network status: ${status}…`;
|
|
3927
|
+
},
|
|
3928
|
+
})
|
|
3929
|
+
.catch((err) => {
|
|
3930
|
+
dispatchWidgetEvent("paymentUnconfirmed", {
|
|
3931
|
+
transactionReference: receipt.transactionReference,
|
|
3932
|
+
transactionHash: receipt.hash,
|
|
3933
|
+
reason: err.message,
|
|
3934
|
+
});
|
|
3935
|
+
this.config.onPaymentUnconfirmed?.({
|
|
3936
|
+
transactionReference: receipt.transactionReference,
|
|
3937
|
+
transactionHash: receipt.hash,
|
|
3938
|
+
reason: err.message,
|
|
3939
|
+
});
|
|
3940
|
+
return { transactionStatus: "unconfirmed" };
|
|
3941
|
+
});
|
|
3942
|
+
},
|
|
3943
|
+
|
|
3409
3944
|
async _assertMobileChain(provider, targetChainId) {
|
|
3410
3945
|
const MAX_ATTEMPTS = 3;
|
|
3411
3946
|
const DELAY_MS = 1000;
|
|
3412
3947
|
|
|
3413
3948
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
3414
3949
|
let currentChainId = null;
|
|
3415
|
-
|
|
3416
3950
|
try {
|
|
3417
3951
|
const raw = await provider.request({ method: "eth_chainId" });
|
|
3418
3952
|
currentChainId = parseInt(raw, 16);
|
|
3419
|
-
} catch
|
|
3420
|
-
console.error(
|
|
3421
|
-
`[KwesPay] eth_chainId RPC failed (attempt ${attempt}/${MAX_ATTEMPTS}):`,
|
|
3422
|
-
err.message
|
|
3423
|
-
);
|
|
3424
|
-
}
|
|
3425
|
-
|
|
3426
|
-
console.log(
|
|
3427
|
-
`[KwesPay] Chain check attempt ${attempt}/${MAX_ATTEMPTS} —`,
|
|
3428
|
-
{ currentChainId, targetChainId }
|
|
3429
|
-
);
|
|
3430
|
-
|
|
3431
|
-
if (currentChainId === targetChainId) {
|
|
3432
|
-
console.log("[KwesPay] Chain confirmed ✅", currentChainId);
|
|
3433
|
-
return;
|
|
3434
|
-
}
|
|
3953
|
+
} catch {}
|
|
3435
3954
|
|
|
3436
|
-
if (
|
|
3955
|
+
if (currentChainId === targetChainId) return;
|
|
3956
|
+
if (attempt < MAX_ATTEMPTS)
|
|
3437
3957
|
await new Promise((r) => setTimeout(r, DELAY_MS));
|
|
3438
|
-
}
|
|
3439
3958
|
}
|
|
3440
3959
|
|
|
3441
|
-
|
|
3442
|
-
|
|
3960
|
+
throw Object.assign(
|
|
3961
|
+
new Error(
|
|
3962
|
+
`Please switch to ${this.state.selectedNetworkName} in your wallet and try again.`
|
|
3963
|
+
),
|
|
3964
|
+
{ code: "WRONG_NETWORK" }
|
|
3443
3965
|
);
|
|
3444
|
-
err.code = "WRONG_NETWORK";
|
|
3445
|
-
throw err;
|
|
3446
3966
|
},
|
|
3447
3967
|
|
|
3448
|
-
|
|
3449
3968
|
async _switchNetworkSafe(
|
|
3450
3969
|
chainId,
|
|
3451
3970
|
networkName,
|
|
@@ -3453,9 +3972,7 @@ const PaymentMethods = {
|
|
|
3453
3972
|
tokenSymbol,
|
|
3454
3973
|
tokenDecimals
|
|
3455
3974
|
) {
|
|
3456
|
-
const switchNetwork = this.walletService.switchNetwork;
|
|
3457
3975
|
const provider = this.walletService.getProvider();
|
|
3458
|
-
|
|
3459
3976
|
if (!provider) throw new Error("[WalletService] No provider connected");
|
|
3460
3977
|
|
|
3461
3978
|
const toHex = (val) => {
|
|
@@ -3480,15 +3997,10 @@ const PaymentMethods = {
|
|
|
3480
3997
|
await provider.request({ method: "eth_chainId" })
|
|
3481
3998
|
);
|
|
3482
3999
|
if (currentHex && currentHex === targetHex) return;
|
|
3483
|
-
} catch
|
|
3484
|
-
console.warn(
|
|
3485
|
-
"[KwesPay] Could not read chainId before switch:",
|
|
3486
|
-
err.message
|
|
3487
|
-
);
|
|
3488
|
-
}
|
|
4000
|
+
} catch {}
|
|
3489
4001
|
|
|
3490
4002
|
try {
|
|
3491
|
-
await switchNetwork(
|
|
4003
|
+
await this.walletService.switchNetwork(
|
|
3492
4004
|
chainId,
|
|
3493
4005
|
networkName,
|
|
3494
4006
|
rpcUrl,
|
|
@@ -3496,15 +4008,9 @@ const PaymentMethods = {
|
|
|
3496
4008
|
tokenDecimals
|
|
3497
4009
|
);
|
|
3498
4010
|
} catch (err) {
|
|
3499
|
-
if (err.code === 4001) throw err;
|
|
3500
|
-
// Some wallets throw even on success — continue to verify below
|
|
3501
|
-
console.warn(
|
|
3502
|
-
"[KwesPay] switchNetwork threw (verifying anyway):",
|
|
3503
|
-
err.message
|
|
3504
|
-
);
|
|
4011
|
+
if (err.code === 4001) throw err;
|
|
3505
4012
|
}
|
|
3506
4013
|
|
|
3507
|
-
// Poll until confirmed or 15s timeout
|
|
3508
4014
|
const POLL_MS = 500;
|
|
3509
4015
|
const TIMEOUT_MS = 15_000;
|
|
3510
4016
|
const started = Date.now();
|
|
@@ -3515,17 +4021,8 @@ const PaymentMethods = {
|
|
|
3515
4021
|
const currentHex = toHex(
|
|
3516
4022
|
await provider.request({ method: "eth_chainId" })
|
|
3517
4023
|
);
|
|
3518
|
-
if (currentHex && currentHex === targetHex)
|
|
3519
|
-
|
|
3520
|
-
`[KwesPay] Network switch confirmed after ${
|
|
3521
|
-
Date.now() - started
|
|
3522
|
-
}ms ✅`
|
|
3523
|
-
);
|
|
3524
|
-
return;
|
|
3525
|
-
}
|
|
3526
|
-
} catch (err) {
|
|
3527
|
-
console.warn("[KwesPay] Poll eth_chainId error:", err.message);
|
|
3528
|
-
}
|
|
4024
|
+
if (currentHex && currentHex === targetHex) return;
|
|
4025
|
+
} catch {}
|
|
3529
4026
|
}
|
|
3530
4027
|
|
|
3531
4028
|
throw new Error(
|
|
@@ -3543,6 +4040,14 @@ function resolveAcceptedTokens(input) {
|
|
|
3543
4040
|
return null;
|
|
3544
4041
|
}
|
|
3545
4042
|
|
|
4043
|
+
function formatFiatDisplay(amount) {
|
|
4044
|
+
const num = parseFloat(amount);
|
|
4045
|
+
if (isNaN(num)) return "0.00";
|
|
4046
|
+
return num % 1 === 0
|
|
4047
|
+
? num.toString()
|
|
4048
|
+
: parseFloat(num.toPrecision(10)).toString();
|
|
4049
|
+
}
|
|
4050
|
+
|
|
3546
4051
|
class KwesPayWidget {
|
|
3547
4052
|
constructor(config) {
|
|
3548
4053
|
if (!config.apiKey) throw new Error("[KwesPayWidget] apiKey is required");
|
|
@@ -3558,6 +4063,10 @@ class KwesPayWidget {
|
|
|
3558
4063
|
currency: config.currency || DEFAULT_CONFIG.currency,
|
|
3559
4064
|
graphqlEndpoint: DEFAULT_CONFIG.graphqlEndpoint,
|
|
3560
4065
|
acceptedTokens: resolveAcceptedTokens(config.acceptedTokens),
|
|
4066
|
+
onPaymentSuccess: config.onPaymentSuccess ?? null,
|
|
4067
|
+
onPaymentConfirmed: config.onPaymentConfirmed ?? null,
|
|
4068
|
+
onPaymentUnconfirmed: config.onPaymentUnconfirmed ?? null,
|
|
4069
|
+
onPaymentError: config.onPaymentError ?? null,
|
|
3561
4070
|
};
|
|
3562
4071
|
|
|
3563
4072
|
if (!Object.values(SUPPORTED_CURRENCIES).includes(this.config.currency)) {
|
|
@@ -3590,21 +4099,61 @@ class KwesPayWidget {
|
|
|
3590
4099
|
wcUri: null,
|
|
3591
4100
|
};
|
|
3592
4101
|
|
|
4102
|
+
this._paymentResolve = null;
|
|
4103
|
+
this._paymentReject = null;
|
|
4104
|
+
this._finalised = false;
|
|
4105
|
+
this._receiptCountdownInterval = null;
|
|
4106
|
+
|
|
3593
4107
|
this._init();
|
|
3594
4108
|
}
|
|
3595
4109
|
|
|
3596
|
-
|
|
4110
|
+
get _displayAmount() {
|
|
4111
|
+
return formatFiatDisplay(this.config.amount);
|
|
4112
|
+
}
|
|
4113
|
+
|
|
4114
|
+
open() {
|
|
4115
|
+
this._finalised = false;
|
|
4116
|
+
this._paymentPromise = new Promise((resolve, reject) => {
|
|
4117
|
+
this._paymentResolve = resolve;
|
|
4118
|
+
this._paymentReject = reject;
|
|
4119
|
+
});
|
|
4120
|
+
|
|
3597
4121
|
const overlay = document.getElementById("kwespay-widget-overlay");
|
|
3598
4122
|
const container = document.getElementById("kwespay-widget-container");
|
|
3599
|
-
if (
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
4123
|
+
if (overlay && container) {
|
|
4124
|
+
container.classList.remove("closing");
|
|
4125
|
+
overlay.classList.add("open");
|
|
4126
|
+
document.body.classList.add("kwespay-open");
|
|
4127
|
+
this.state.isOpen = true;
|
|
4128
|
+
}
|
|
3605
4129
|
|
|
3606
|
-
|
|
4130
|
+
this._validateAPIKey();
|
|
3607
4131
|
dispatchWidgetEvent("widgetOpened", {});
|
|
4132
|
+
|
|
4133
|
+
return this._paymentPromise;
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
_finalisePayment(payload, autoClose = true) {
|
|
4137
|
+
if (this._finalised) return;
|
|
4138
|
+
this._finalised = true;
|
|
4139
|
+
|
|
4140
|
+
dispatchWidgetEvent("paymentConfirmed", payload);
|
|
4141
|
+
this.config.onPaymentConfirmed?.(payload);
|
|
4142
|
+
|
|
4143
|
+
this._paymentResolve?.(payload);
|
|
4144
|
+
|
|
4145
|
+
if (autoClose) {
|
|
4146
|
+
this.close();
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
|
|
4150
|
+
_failPayment(message, errorType) {
|
|
4151
|
+
const errorPayload = { error: message, errorType };
|
|
4152
|
+
dispatchWidgetEvent("paymentError", errorPayload);
|
|
4153
|
+
this.config.onPaymentError?.(errorPayload);
|
|
4154
|
+
|
|
4155
|
+
const err = Object.assign(new Error(message), { code: errorType });
|
|
4156
|
+
this._paymentReject?.(err);
|
|
3608
4157
|
}
|
|
3609
4158
|
|
|
3610
4159
|
close() {
|
|
@@ -3612,28 +4161,29 @@ class KwesPayWidget {
|
|
|
3612
4161
|
const container = document.getElementById("kwespay-widget-container");
|
|
3613
4162
|
if (!overlay || !container) return;
|
|
3614
4163
|
|
|
4164
|
+
this._stopReceiptCountdown();
|
|
3615
4165
|
this._clearQuoteTimer();
|
|
3616
|
-
|
|
4166
|
+
|
|
4167
|
+
const closedAfterSuccess = this.state.currentStep === 4;
|
|
3617
4168
|
const mobile = window.innerWidth <= 480;
|
|
3618
4169
|
|
|
3619
|
-
|
|
3620
|
-
container.classList.add("closing");
|
|
3621
|
-
setTimeout(() => {
|
|
3622
|
-
container.classList.remove("closing");
|
|
3623
|
-
overlay.classList.remove("open");
|
|
3624
|
-
document.body.classList.remove("kwespay-open");
|
|
3625
|
-
this.state.isOpen = false;
|
|
3626
|
-
dispatchWidgetEvent("widgetClosed", {
|
|
3627
|
-
completedPayment: closedAfterSuccess,
|
|
3628
|
-
});
|
|
3629
|
-
}, 300);
|
|
3630
|
-
} else {
|
|
4170
|
+
const finish = () => {
|
|
3631
4171
|
overlay.classList.remove("open");
|
|
3632
4172
|
document.body.classList.remove("kwespay-open");
|
|
3633
4173
|
this.state.isOpen = false;
|
|
3634
4174
|
dispatchWidgetEvent("widgetClosed", {
|
|
3635
4175
|
completedPayment: closedAfterSuccess,
|
|
3636
4176
|
});
|
|
4177
|
+
};
|
|
4178
|
+
|
|
4179
|
+
if (mobile) {
|
|
4180
|
+
container.classList.add("closing");
|
|
4181
|
+
setTimeout(() => {
|
|
4182
|
+
container.classList.remove("closing");
|
|
4183
|
+
finish();
|
|
4184
|
+
}, 300);
|
|
4185
|
+
} else {
|
|
4186
|
+
finish();
|
|
3637
4187
|
}
|
|
3638
4188
|
}
|
|
3639
4189
|
|
|
@@ -3651,12 +4201,13 @@ class KwesPayWidget {
|
|
|
3651
4201
|
) {
|
|
3652
4202
|
this.config.currency = newCurrency;
|
|
3653
4203
|
}
|
|
4204
|
+
const display = `${this._displayAmount} ${this.config.currency}`;
|
|
3654
4205
|
document
|
|
3655
4206
|
.querySelectorAll(
|
|
3656
4207
|
'[id*="paymentAmount"], [id*="summaryFiatAmount"], [id*="txFiatAmount"], [id*="reviewFiatAmount"]'
|
|
3657
4208
|
)
|
|
3658
4209
|
.forEach((el) => {
|
|
3659
|
-
el.textContent =
|
|
4210
|
+
el.textContent = display;
|
|
3660
4211
|
});
|
|
3661
4212
|
dispatchWidgetEvent("amountUpdated", {
|
|
3662
4213
|
amount: this.config.amount,
|
|
@@ -3669,11 +4220,21 @@ class KwesPayWidget {
|
|
|
3669
4220
|
}
|
|
3670
4221
|
|
|
3671
4222
|
destroy() {
|
|
4223
|
+
this._stopReceiptCountdown();
|
|
3672
4224
|
this._clearQuoteTimer();
|
|
3673
4225
|
document.body.classList.remove("kwespay-open");
|
|
3674
4226
|
this.walletService?.disconnect();
|
|
3675
4227
|
document.getElementById("kwespay-widget-overlay")?.remove();
|
|
3676
4228
|
document.getElementById("kwespay-widget-styles")?.remove();
|
|
4229
|
+
|
|
4230
|
+
if (!this._finalised) {
|
|
4231
|
+
this._paymentReject?.(
|
|
4232
|
+
Object.assign(new Error("Widget destroyed"), {
|
|
4233
|
+
code: "WIDGET_DESTROYED",
|
|
4234
|
+
})
|
|
4235
|
+
);
|
|
4236
|
+
}
|
|
4237
|
+
|
|
3677
4238
|
this.state = null;
|
|
3678
4239
|
this.config = null;
|
|
3679
4240
|
this.walletService = null;
|
|
@@ -3693,9 +4254,103 @@ Object.assign(
|
|
|
3693
4254
|
PaymentMethods
|
|
3694
4255
|
);
|
|
3695
4256
|
|
|
4257
|
+
// Keyed by `${apiKey}::${vendorId}` so a page with multiple vendors/keys
|
|
4258
|
+
// each get their own widget instance, but a single vendor reuses one.
|
|
4259
|
+
|
|
4260
|
+
const _instances = new Map();
|
|
4261
|
+
|
|
4262
|
+
function _cacheKey(apiKey, vendorId) {
|
|
4263
|
+
return `${apiKey}::${vendorId}`;
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4266
|
+
// kwespay()
|
|
4267
|
+
|
|
4268
|
+
/**
|
|
4269
|
+
* Open the KwesPay checkout widget and await the result.
|
|
4270
|
+
*
|
|
4271
|
+
* Handles the full lifecycle — construction, caching, amount updates,
|
|
4272
|
+
* open, and cleanup — so callers need nothing else.
|
|
4273
|
+
*
|
|
4274
|
+
* @param {object} config
|
|
4275
|
+
* @param {string} config.apiKey Your KwesPay public API key.
|
|
4276
|
+
* @param {string} config.vendorId Your vendor / merchant UUID.
|
|
4277
|
+
* @param {number} config.amount Fiat amount to charge (e.g. 49.99).
|
|
4278
|
+
* @param {string} [config.currency] ISO currency code (default: "USD").
|
|
4279
|
+
* @param {string[] | "stablecoins"} [config.acceptedTokens]
|
|
4280
|
+
* Restrict accepted crypto tokens.
|
|
4281
|
+
*
|
|
4282
|
+
* @returns {Promise<PaymentResult>}
|
|
4283
|
+
* Resolves when the payment is confirmed on-chain.
|
|
4284
|
+
* Rejects with an Error whose `.code` is one of:
|
|
4285
|
+
* "USER_CANCELLED" — user closed the widget without paying
|
|
4286
|
+
* "USER_REJECTED" — user rejected the wallet transaction
|
|
4287
|
+
* "SESSION_EXPIRED" — wallet session timed out mid-payment
|
|
4288
|
+
* "WIDGET_DESTROYED" — widget.destroy() was called externally
|
|
4289
|
+
* "UNKNOWN" — unexpected error (check err.message)
|
|
4290
|
+
*
|
|
4291
|
+
* @example
|
|
4292
|
+
* // Minimal usage — everything else is handled internally
|
|
4293
|
+
* try {
|
|
4294
|
+
* const result = await kwespay({
|
|
4295
|
+
* apiKey: "pk_...",
|
|
4296
|
+
* vendorId: "uuid",
|
|
4297
|
+
* amount: total,
|
|
4298
|
+
* currency: "USD",
|
|
4299
|
+
* });
|
|
4300
|
+
* console.log("Paid:", result.transactionHash);
|
|
4301
|
+
* } catch (err) {
|
|
4302
|
+
* if (err.code !== "USER_CANCELLED") console.error(err);
|
|
4303
|
+
* }
|
|
4304
|
+
*/
|
|
4305
|
+
async function kwespay(config) {
|
|
4306
|
+
const { apiKey, vendorId, amount, currency, acceptedTokens } = config;
|
|
4307
|
+
|
|
4308
|
+
if (!apiKey) throw new Error("[kwespay] apiKey is required");
|
|
4309
|
+
if (!vendorId) throw new Error("[kwespay] vendorId is required");
|
|
4310
|
+
if (!amount || parseFloat(amount) <= 0)
|
|
4311
|
+
throw new Error("[kwespay] A valid amount is required");
|
|
4312
|
+
|
|
4313
|
+
const key = _cacheKey(apiKey, vendorId);
|
|
4314
|
+
|
|
4315
|
+
// Reuse existing instance or create a fresh one
|
|
4316
|
+
let widget = _instances.get(key);
|
|
4317
|
+
|
|
4318
|
+
if (!widget) {
|
|
4319
|
+
widget = new KwesPayWidget({
|
|
4320
|
+
apiKey,
|
|
4321
|
+
vendorId,
|
|
4322
|
+
amount,
|
|
4323
|
+
currency,
|
|
4324
|
+
acceptedTokens,
|
|
4325
|
+
});
|
|
4326
|
+
_instances.set(key, widget);
|
|
4327
|
+
} else {
|
|
4328
|
+
// Sync amount/currency in case they changed since last call
|
|
4329
|
+
widget.updateAmount(amount, currency);
|
|
4330
|
+
}
|
|
4331
|
+
|
|
4332
|
+
try {
|
|
4333
|
+
// open() returns a Promise that resolves/rejects when the payment settles.
|
|
4334
|
+
const result = await widget.open();
|
|
4335
|
+
return result;
|
|
4336
|
+
} finally {
|
|
4337
|
+
// Always evict after a settled payment or cancellation so the next call
|
|
4338
|
+
// starts fresh (new quote, clean state). The DOM is cleaned up by close(),
|
|
4339
|
+
// which open() calls internally on success; we just clear our cache ref.
|
|
4340
|
+
_instances.delete(key);
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
|
|
3696
4344
|
/**
|
|
3697
|
-
*
|
|
3698
|
-
* @
|
|
4345
|
+
* @typedef {object} PaymentResult
|
|
4346
|
+
* @property {string} transactionHash On-chain tx hash.
|
|
4347
|
+
* @property {string} transactionReference KwesPay internal payment reference.
|
|
4348
|
+
* @property {string} paymentIdBytes32 On-chain payment ID (bytes32).
|
|
4349
|
+
* @property {string} transactionStatus "completed" | "unconfirmed"
|
|
4350
|
+
* @property {number} fiatAmount Charged fiat amount.
|
|
4351
|
+
* @property {string} currency ISO currency code.
|
|
4352
|
+
* @property {string} token Crypto token used (e.g. "USDC").
|
|
4353
|
+
* @property {string} network Chain used (e.g. "polygon").
|
|
3699
4354
|
*/
|
|
3700
4355
|
|
|
3701
|
-
export { KwesPayWidget as default };
|
|
4356
|
+
export { KwesPayWidget, KwesPayWidget as default, kwespay };
|