@kwespay/widget 1.0.7 → 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 +118 -28
- 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 +1200 -546
- package/dist/index.d.ts +52 -5
- package/dist/kwespay-widget.js +46648 -53965
- 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://
|
|
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 = {
|
|
@@ -97,7 +140,6 @@ const TOKEN_CONFIGS = {
|
|
|
97
140
|
decimals: 18,
|
|
98
141
|
coingeckoId: "ethereum",
|
|
99
142
|
binanceSymbol: "ETHUSDT",
|
|
100
|
-
|
|
101
143
|
},
|
|
102
144
|
{
|
|
103
145
|
symbol: "USDT",
|
|
@@ -117,6 +159,15 @@ const TOKEN_CONFIGS = {
|
|
|
117
159
|
coingeckoId: "usd-coin",
|
|
118
160
|
binanceSymbol: "USDCUSDT",
|
|
119
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
|
+
},
|
|
120
171
|
],
|
|
121
172
|
polygon: [
|
|
122
173
|
{
|
|
@@ -137,6 +188,15 @@ const TOKEN_CONFIGS = {
|
|
|
137
188
|
coingeckoId: "tether",
|
|
138
189
|
binanceSymbol: "USDTUSDT",
|
|
139
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
|
+
},
|
|
140
200
|
],
|
|
141
201
|
base: [
|
|
142
202
|
{
|
|
@@ -157,38 +217,172 @@ const TOKEN_CONFIGS = {
|
|
|
157
217
|
coingeckoId: "usd-coin",
|
|
158
218
|
binanceSymbol: "USDCUSDT",
|
|
159
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
|
+
},
|
|
160
229
|
],
|
|
161
230
|
lisk: [
|
|
162
231
|
{
|
|
163
232
|
symbol: "ETH",
|
|
164
|
-
name: "
|
|
165
|
-
icon: "https://arthuremma2.github.io/img-hosting/
|
|
233
|
+
name: "Ethereum",
|
|
234
|
+
icon: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
166
235
|
address: "0x0000000000000000000000000000000000000000",
|
|
167
236
|
decimals: 18,
|
|
168
237
|
coingeckoId: "ethereum",
|
|
169
238
|
binanceSymbol: "ETHUSDT",
|
|
170
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
|
+
},
|
|
171
249
|
],
|
|
172
|
-
|
|
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: [
|
|
173
280
|
{
|
|
174
281
|
symbol: "ETH",
|
|
175
|
-
name: "
|
|
282
|
+
name: "Ethereum",
|
|
176
283
|
icon: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
177
284
|
address: "0x0000000000000000000000000000000000000000",
|
|
178
285
|
decimals: 18,
|
|
179
286
|
coingeckoId: "ethereum",
|
|
180
287
|
binanceSymbol: "ETHUSDT",
|
|
181
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
|
+
},
|
|
182
298
|
{
|
|
183
299
|
symbol: "USDT",
|
|
184
|
-
name: "
|
|
300
|
+
name: "Tether USD",
|
|
185
301
|
icon: "https://move-flow.github.io/assets/tether-usdt-logo.svg",
|
|
186
|
-
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",
|
|
187
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,
|
|
188
333
|
coingeckoId: "tether",
|
|
189
334
|
binanceSymbol: "USDTUSDT",
|
|
190
335
|
},
|
|
191
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
|
+
],
|
|
192
386
|
polygonAmoy: [
|
|
193
387
|
{
|
|
194
388
|
symbol: "MATIC",
|
|
@@ -200,13 +394,13 @@ const TOKEN_CONFIGS = {
|
|
|
200
394
|
binanceSymbol: "MATICUSDT",
|
|
201
395
|
},
|
|
202
396
|
{
|
|
203
|
-
symbol: "
|
|
204
|
-
name: "Mock
|
|
205
|
-
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",
|
|
206
400
|
address: "0x8B0180f2101c8260d49339abfEe87927412494B4",
|
|
207
401
|
decimals: 6,
|
|
208
|
-
coingeckoId: "
|
|
209
|
-
binanceSymbol: "
|
|
402
|
+
coingeckoId: "usd-coin",
|
|
403
|
+
binanceSymbol: "USDCUSDT",
|
|
210
404
|
},
|
|
211
405
|
],
|
|
212
406
|
baseSepolia: [
|
|
@@ -221,7 +415,7 @@ const TOKEN_CONFIGS = {
|
|
|
221
415
|
},
|
|
222
416
|
{
|
|
223
417
|
symbol: "USDC",
|
|
224
|
-
name: "
|
|
418
|
+
name: "Mock USDC",
|
|
225
419
|
icon: "https://move-flow.github.io/assets/usd-coin-usdc-logo.svg",
|
|
226
420
|
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
227
421
|
decimals: 6,
|
|
@@ -229,24 +423,33 @@ const TOKEN_CONFIGS = {
|
|
|
229
423
|
binanceSymbol: "USDCUSDT",
|
|
230
424
|
},
|
|
231
425
|
],
|
|
232
|
-
|
|
426
|
+
arbitrumSepolia: [
|
|
233
427
|
{
|
|
234
428
|
symbol: "ETH",
|
|
235
|
-
name: "
|
|
236
|
-
icon: "https://arthuremma2.github.io/img-hosting/
|
|
429
|
+
name: "Arbitrum Sepolia ETH",
|
|
430
|
+
icon: "https://arthuremma2.github.io/img-hosting/ethereum-eth.svg",
|
|
237
431
|
address: "0x0000000000000000000000000000000000000000",
|
|
238
432
|
decimals: 18,
|
|
239
433
|
coingeckoId: "ethereum",
|
|
240
434
|
binanceSymbol: "ETHUSDT",
|
|
241
435
|
},
|
|
242
436
|
{
|
|
243
|
-
symbol: "
|
|
244
|
-
name: "
|
|
245
|
-
icon: "https://
|
|
246
|
-
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",
|
|
247
450
|
decimals: 18,
|
|
248
|
-
coingeckoId: "
|
|
249
|
-
binanceSymbol: "
|
|
451
|
+
coingeckoId: "arbitrum",
|
|
452
|
+
binanceSymbol: "ARBUSDT",
|
|
250
453
|
},
|
|
251
454
|
],
|
|
252
455
|
};
|
|
@@ -810,36 +1013,36 @@ class PaymentService {
|
|
|
810
1013
|
constructor(apiKey, graphqlEndpoint) {
|
|
811
1014
|
this.apiKey = apiKey;
|
|
812
1015
|
this.graphqlEndpoint = graphqlEndpoint;
|
|
1016
|
+
this._graphqlEndpoint = graphqlEndpoint;
|
|
1017
|
+
this._apiKey = apiKey;
|
|
813
1018
|
this.client = null;
|
|
814
|
-
|
|
815
|
-
console.log("[KwesPay] PaymentService initialized", {
|
|
816
|
-
apiKey: this.apiKey?.slice(0, 6) + "...",
|
|
817
|
-
});
|
|
818
1019
|
}
|
|
819
1020
|
|
|
820
1021
|
async _initClient() {
|
|
821
1022
|
if (this.client) return;
|
|
822
|
-
|
|
823
|
-
console.log("[KwesPay] Initializing SDK client...");
|
|
824
|
-
|
|
825
|
-
const { KwesPayClient } = await import('./index-338l55OY.js');
|
|
826
|
-
|
|
1023
|
+
const { KwesPayClient } = await import('./index-BDgXWGKX.js');
|
|
827
1024
|
this.client = new KwesPayClient({ apiKey: this.apiKey });
|
|
1025
|
+
}
|
|
828
1026
|
|
|
829
|
-
|
|
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();
|
|
830
1037
|
}
|
|
831
1038
|
|
|
832
1039
|
async validateAPIKey() {
|
|
833
1040
|
try {
|
|
834
1041
|
await this._initClient();
|
|
835
|
-
|
|
836
1042
|
const result = await this.client.validateKey();
|
|
837
|
-
|
|
838
1043
|
if (!result.isValid) {
|
|
839
|
-
console.error("[KwesPay] Invalid API key:", result.error);
|
|
840
1044
|
return { valid: false, error: result.error ?? "Invalid access key" };
|
|
841
1045
|
}
|
|
842
|
-
|
|
843
1046
|
return {
|
|
844
1047
|
valid: true,
|
|
845
1048
|
keyId: result.keyId,
|
|
@@ -856,94 +1059,94 @@ class PaymentService {
|
|
|
856
1059
|
|
|
857
1060
|
async getQuote(params) {
|
|
858
1061
|
await this._initClient();
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const quote = await this.client.quote({
|
|
863
|
-
vendorIdentifier: params.vendorId,
|
|
1062
|
+
return this.client.getQuote({
|
|
1063
|
+
vendorIdentifier: params.vendorIdentifier,
|
|
864
1064
|
fiatAmount: params.fiatAmount,
|
|
865
1065
|
fiatCurrency: params.fiatCurrency || "USD",
|
|
866
1066
|
cryptoCurrency: params.cryptoCurrency,
|
|
867
1067
|
network: params.network,
|
|
868
|
-
payerWalletAddress: params.payerWalletAddress,
|
|
869
1068
|
});
|
|
870
|
-
|
|
871
|
-
console.log("[KwesPay] Quote received:", quote);
|
|
872
|
-
|
|
873
|
-
return quote;
|
|
874
1069
|
}
|
|
875
1070
|
|
|
876
1071
|
async createPayment({ payload, walletProvider, onStatusUpdate }) {
|
|
877
1072
|
await this._initClient();
|
|
878
1073
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
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
|
+
);
|
|
898
1099
|
|
|
899
|
-
|
|
1100
|
+
const ct = rawTx?.data?.createTransaction;
|
|
900
1101
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
blockNumber: result.blockNumber,
|
|
904
|
-
transactionReference: result.transactionReference,
|
|
905
|
-
paymentIdBytes32: result.paymentIdBytes32,
|
|
906
|
-
};
|
|
907
|
-
} catch (err) {
|
|
908
|
-
console.error("[KwesPay] ❌ createPayment error:", {
|
|
909
|
-
message: err?.message,
|
|
910
|
-
code: err?.code,
|
|
911
|
-
reason: err?.reason,
|
|
912
|
-
data: err?.data,
|
|
913
|
-
transaction: err?.transaction
|
|
914
|
-
? {
|
|
915
|
-
to: err.transaction?.to,
|
|
916
|
-
from: err.transaction?.from,
|
|
917
|
-
value: err.transaction?.value?.toString(),
|
|
918
|
-
gasLimit: err.transaction?.gasLimit?.toString(),
|
|
919
|
-
data: err.transaction?.data,
|
|
920
|
-
}
|
|
921
|
-
: undefined,
|
|
922
|
-
receipt: err?.receipt
|
|
923
|
-
? {
|
|
924
|
-
status: err.receipt?.status,
|
|
925
|
-
gasUsed: err.receipt?.gasUsed?.toString(),
|
|
926
|
-
blockNumber: err.receipt?.blockNumber,
|
|
927
|
-
transactionHash: err.receipt?.transactionHash,
|
|
928
|
-
}
|
|
929
|
-
: undefined,
|
|
930
|
-
stack: err?.stack,
|
|
931
|
-
raw: err,
|
|
932
|
-
});
|
|
933
|
-
throw err;
|
|
1102
|
+
if (!ct?.success) {
|
|
1103
|
+
throw new Error(ct?.message ?? "Transaction creation failed");
|
|
934
1104
|
}
|
|
935
|
-
}
|
|
936
1105
|
|
|
937
|
-
|
|
938
|
-
|
|
1106
|
+
if (!ct.deadline) {
|
|
1107
|
+
throw new Error("Backend did not return a deadline");
|
|
1108
|
+
}
|
|
939
1109
|
|
|
940
|
-
|
|
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
|
+
};
|
|
941
1132
|
|
|
942
|
-
const
|
|
1133
|
+
const result = await this.client.pay({
|
|
1134
|
+
provider: walletProvider,
|
|
1135
|
+
payload: txPayload,
|
|
1136
|
+
onStatus: (title, detail) => onStatusUpdate?.(title, detail),
|
|
1137
|
+
});
|
|
943
1138
|
|
|
944
|
-
|
|
1139
|
+
return {
|
|
1140
|
+
hash: result.txHash,
|
|
1141
|
+
blockNumber: result.blockNumber,
|
|
1142
|
+
transactionReference: result.transactionReference,
|
|
1143
|
+
paymentIdBytes32: result.paymentIdBytes32,
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
945
1146
|
|
|
946
|
-
|
|
1147
|
+
async getTransactionStatus(transactionReference) {
|
|
1148
|
+
await this._initClient();
|
|
1149
|
+
return this.client.getTransactionStatus(transactionReference);
|
|
947
1150
|
}
|
|
948
1151
|
|
|
949
1152
|
async pollTransactionStatus(
|
|
@@ -951,28 +1154,13 @@ class PaymentService {
|
|
|
951
1154
|
{ onStatus, intervalMs = 4000, maxAttempts = 60 } = {}
|
|
952
1155
|
) {
|
|
953
1156
|
await this._initClient();
|
|
954
|
-
|
|
955
|
-
console.log("[KwesPay] Starting polling...", {
|
|
956
|
-
transactionReference,
|
|
957
|
-
intervalMs,
|
|
958
|
-
});
|
|
959
|
-
|
|
960
1157
|
let attempts = 0;
|
|
961
|
-
|
|
962
1158
|
return new Promise((resolve, reject) => {
|
|
963
1159
|
const id = setInterval(async () => {
|
|
964
1160
|
attempts++;
|
|
965
|
-
|
|
966
1161
|
try {
|
|
967
1162
|
const status = await this.getTransactionStatus(transactionReference);
|
|
968
|
-
|
|
969
|
-
console.log(
|
|
970
|
-
`[KwesPay] Poll attempt ${attempts}:`,
|
|
971
|
-
status.transactionStatus
|
|
972
|
-
);
|
|
973
|
-
|
|
974
1163
|
onStatus?.(status.transactionStatus);
|
|
975
|
-
|
|
976
1164
|
const terminal = [
|
|
977
1165
|
"completed",
|
|
978
1166
|
"failed",
|
|
@@ -981,22 +1169,14 @@ class PaymentService {
|
|
|
981
1169
|
"overpaid",
|
|
982
1170
|
"refunded",
|
|
983
1171
|
];
|
|
984
|
-
|
|
985
1172
|
if (terminal.includes(status.transactionStatus)) {
|
|
986
|
-
console.log(
|
|
987
|
-
"[KwesPay] Final status reached:",
|
|
988
|
-
status.transactionStatus
|
|
989
|
-
);
|
|
990
1173
|
clearInterval(id);
|
|
991
1174
|
resolve(status);
|
|
992
1175
|
} else if (attempts >= maxAttempts) {
|
|
993
|
-
console.error("[KwesPay] Polling timeout");
|
|
994
1176
|
clearInterval(id);
|
|
995
1177
|
reject(new Error("Transaction status polling timed out."));
|
|
996
1178
|
}
|
|
997
1179
|
} catch (err) {
|
|
998
|
-
console.error("[KwesPay] Polling error:", err);
|
|
999
|
-
|
|
1000
1180
|
if (attempts >= maxAttempts) {
|
|
1001
1181
|
clearInterval(id);
|
|
1002
1182
|
reject(err);
|
|
@@ -1027,6 +1207,18 @@ function truncateHash(hash, startChars = 10, endChars = 8) {
|
|
|
1027
1207
|
return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
|
|
1028
1208
|
}
|
|
1029
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
|
+
|
|
1030
1222
|
function getErrorType(error) {
|
|
1031
1223
|
const msg = error?.message ?? "";
|
|
1032
1224
|
if (
|
|
@@ -1091,20 +1283,20 @@ const WIDGET_STYLES = `
|
|
|
1091
1283
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1092
1284
|
|
|
1093
1285
|
:root {
|
|
1094
|
-
--kp-bg:
|
|
1095
|
-
--kp-surface:
|
|
1096
|
-
--kp-surface-2:
|
|
1097
|
-
--kp-border:
|
|
1286
|
+
--kp-bg: #0a0a0f;
|
|
1287
|
+
--kp-surface: #0f0f18;
|
|
1288
|
+
--kp-surface-2: #16161f;
|
|
1289
|
+
--kp-border: rgba(255,255,255,0.06);
|
|
1098
1290
|
--kp-border-active: rgba(99,102,241,0.5);
|
|
1099
|
-
--kp-accent:
|
|
1100
|
-
--kp-accent-dim:
|
|
1101
|
-
--kp-accent-glow:
|
|
1102
|
-
--kp-green:
|
|
1103
|
-
--kp-red:
|
|
1104
|
-
--kp-text:
|
|
1105
|
-
--kp-muted:
|
|
1106
|
-
--kp-mono:
|
|
1107
|
-
--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;
|
|
1108
1300
|
}
|
|
1109
1301
|
|
|
1110
1302
|
body.kwespay-open {
|
|
@@ -1129,19 +1321,17 @@ const WIDGET_STYLES = `
|
|
|
1129
1321
|
|
|
1130
1322
|
.kwespay-close-btn {
|
|
1131
1323
|
position: absolute;
|
|
1132
|
-
top:
|
|
1324
|
+
top: 12px; right: 12px;
|
|
1133
1325
|
background: rgba(255,255,255,0.06);
|
|
1134
1326
|
border: 1px solid var(--kp-border);
|
|
1135
1327
|
color: var(--kp-muted);
|
|
1136
|
-
width:
|
|
1137
|
-
border-radius:
|
|
1328
|
+
width: 28px; height: 28px;
|
|
1329
|
+
border-radius: 7px;
|
|
1138
1330
|
cursor: pointer;
|
|
1139
|
-
display: flex;
|
|
1140
|
-
|
|
1141
|
-
justify-content: center;
|
|
1142
|
-
font-size: 18px;
|
|
1331
|
+
display: flex; align-items: center; justify-content: center;
|
|
1332
|
+
font-size: 16px;
|
|
1143
1333
|
transition: all 0.15s;
|
|
1144
|
-
z-index:
|
|
1334
|
+
z-index: 10;
|
|
1145
1335
|
}
|
|
1146
1336
|
|
|
1147
1337
|
.kwespay-close-btn:hover {
|
|
@@ -1150,6 +1340,12 @@ const WIDGET_STYLES = `
|
|
|
1150
1340
|
border-color: var(--kp-border-active);
|
|
1151
1341
|
}
|
|
1152
1342
|
|
|
1343
|
+
.kwespay-steps-wrapper {
|
|
1344
|
+
position: relative;
|
|
1345
|
+
flex: 1;
|
|
1346
|
+
overflow: hidden;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1153
1349
|
.material-symbols-outlined {
|
|
1154
1350
|
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
|
|
1155
1351
|
}
|
|
@@ -1208,6 +1404,8 @@ const WIDGET_STYLES = `
|
|
|
1208
1404
|
to { opacity: 0; }
|
|
1209
1405
|
}
|
|
1210
1406
|
|
|
1407
|
+
|
|
1408
|
+
|
|
1211
1409
|
.kp-topbar {
|
|
1212
1410
|
display: flex;
|
|
1213
1411
|
align-items: center;
|
|
@@ -1224,11 +1422,8 @@ const WIDGET_STYLES = `
|
|
|
1224
1422
|
}
|
|
1225
1423
|
|
|
1226
1424
|
.kp-back-btn {
|
|
1227
|
-
display: flex;
|
|
1228
|
-
|
|
1229
|
-
justify-content: center;
|
|
1230
|
-
width: 28px;
|
|
1231
|
-
height: 28px;
|
|
1425
|
+
display: flex; align-items: center; justify-content: center;
|
|
1426
|
+
width: 28px; height: 28px;
|
|
1232
1427
|
border-radius: 7px;
|
|
1233
1428
|
background: rgba(255,255,255,0.04);
|
|
1234
1429
|
border: 1px solid var(--kp-border);
|
|
@@ -1253,11 +1448,12 @@ const WIDGET_STYLES = `
|
|
|
1253
1448
|
background: var(--kp-accent);
|
|
1254
1449
|
box-shadow: 0 0 8px var(--kp-accent-glow);
|
|
1255
1450
|
animation: kpPulse 2s ease infinite;
|
|
1451
|
+
flex-shrink: 0;
|
|
1256
1452
|
}
|
|
1257
1453
|
|
|
1258
1454
|
@keyframes kpPulse {
|
|
1259
1455
|
0%, 100% { opacity: 1; }
|
|
1260
|
-
50%
|
|
1456
|
+
50% { opacity: 0.4; }
|
|
1261
1457
|
}
|
|
1262
1458
|
|
|
1263
1459
|
.kp-topbar-name {
|
|
@@ -1268,9 +1464,7 @@ const WIDGET_STYLES = `
|
|
|
1268
1464
|
}
|
|
1269
1465
|
|
|
1270
1466
|
.kp-topbar-secure {
|
|
1271
|
-
display: flex;
|
|
1272
|
-
align-items: center;
|
|
1273
|
-
gap: 5px;
|
|
1467
|
+
display: flex; align-items: center; gap: 5px;
|
|
1274
1468
|
font-family: var(--kp-mono);
|
|
1275
1469
|
font-size: 10px;
|
|
1276
1470
|
color: var(--kp-muted);
|
|
@@ -1278,10 +1472,8 @@ const WIDGET_STYLES = `
|
|
|
1278
1472
|
font-weight: 500;
|
|
1279
1473
|
}
|
|
1280
1474
|
|
|
1281
|
-
.kp-topbar-secure .material-symbols-outlined {
|
|
1282
|
-
|
|
1283
|
-
color: var(--kp-green);
|
|
1284
|
-
}
|
|
1475
|
+
.kp-topbar-secure .material-symbols-outlined { font-size: 13px; color: var(--kp-green); }
|
|
1476
|
+
|
|
1285
1477
|
|
|
1286
1478
|
.kp-amount-block {
|
|
1287
1479
|
padding: 20px 20px 16px;
|
|
@@ -1328,6 +1520,8 @@ const WIDGET_STYLES = `
|
|
|
1328
1520
|
|
|
1329
1521
|
.kp-amount-crypto.loading { opacity: 0.4; }
|
|
1330
1522
|
|
|
1523
|
+
/* ── Progress ───────────────────────────────────────────────────────────── */
|
|
1524
|
+
|
|
1331
1525
|
.progress-section {
|
|
1332
1526
|
padding: 14px 20px 12px;
|
|
1333
1527
|
border-bottom: 1px solid var(--kp-border);
|
|
@@ -1335,9 +1529,7 @@ const WIDGET_STYLES = `
|
|
|
1335
1529
|
}
|
|
1336
1530
|
|
|
1337
1531
|
.progress-info {
|
|
1338
|
-
display: flex;
|
|
1339
|
-
align-items: center;
|
|
1340
|
-
justify-content: space-between;
|
|
1532
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
1341
1533
|
margin-bottom: 8px;
|
|
1342
1534
|
}
|
|
1343
1535
|
|
|
@@ -1360,8 +1552,7 @@ const WIDGET_STYLES = `
|
|
|
1360
1552
|
.progress-bars { display: flex; width: 100%; gap: 5px; }
|
|
1361
1553
|
|
|
1362
1554
|
.progress-bar {
|
|
1363
|
-
height: 2px;
|
|
1364
|
-
flex: 1;
|
|
1555
|
+
height: 2px; flex: 1;
|
|
1365
1556
|
border-radius: 999px;
|
|
1366
1557
|
background: var(--kp-surface-2);
|
|
1367
1558
|
transition: background 0.3s ease;
|
|
@@ -1372,6 +1563,8 @@ const WIDGET_STYLES = `
|
|
|
1372
1563
|
box-shadow: 0 0 8px var(--kp-accent-glow);
|
|
1373
1564
|
}
|
|
1374
1565
|
|
|
1566
|
+
/* ── Scrollable content ─────────────────────────────────────────────────── */
|
|
1567
|
+
|
|
1375
1568
|
.section-hint {
|
|
1376
1569
|
font-size: 12px;
|
|
1377
1570
|
color: var(--kp-muted);
|
|
@@ -1395,10 +1588,10 @@ const WIDGET_STYLES = `
|
|
|
1395
1588
|
border-radius: 10px;
|
|
1396
1589
|
}
|
|
1397
1590
|
|
|
1591
|
+
/* ── Network list ───────────────────────────────────────────────────────── */
|
|
1592
|
+
|
|
1398
1593
|
.item-list {
|
|
1399
|
-
display: flex;
|
|
1400
|
-
flex-direction: column;
|
|
1401
|
-
gap: 6px;
|
|
1594
|
+
display: flex; flex-direction: column; gap: 6px;
|
|
1402
1595
|
margin-bottom: 6px;
|
|
1403
1596
|
}
|
|
1404
1597
|
|
|
@@ -1417,9 +1610,7 @@ const WIDGET_STYLES = `
|
|
|
1417
1610
|
}
|
|
1418
1611
|
|
|
1419
1612
|
.list-item {
|
|
1420
|
-
display: flex;
|
|
1421
|
-
align-items: center;
|
|
1422
|
-
gap: 12px;
|
|
1613
|
+
display: flex; align-items: center; gap: 12px;
|
|
1423
1614
|
background: var(--kp-surface);
|
|
1424
1615
|
padding: 12px 14px;
|
|
1425
1616
|
border-radius: 12px;
|
|
@@ -1444,15 +1635,11 @@ const WIDGET_STYLES = `
|
|
|
1444
1635
|
flex-shrink: 0;
|
|
1445
1636
|
}
|
|
1446
1637
|
|
|
1447
|
-
.item-icon img { width:
|
|
1638
|
+
.item-icon img { width: 28px; height: 28px; object-fit: contain; }
|
|
1448
1639
|
.item-info { display: flex; flex-direction: column; flex: 1; }
|
|
1449
1640
|
.item-name-row { display: flex; align-items: center; gap: 7px; }
|
|
1450
1641
|
|
|
1451
|
-
.item-name {
|
|
1452
|
-
font-size: 13px;
|
|
1453
|
-
font-weight: 600;
|
|
1454
|
-
color: var(--kp-text);
|
|
1455
|
-
}
|
|
1642
|
+
.item-name { font-size: 13px; font-weight: 600; color: var(--kp-text); }
|
|
1456
1643
|
|
|
1457
1644
|
.item-badge {
|
|
1458
1645
|
padding: 1px 6px;
|
|
@@ -1480,10 +1667,10 @@ const WIDGET_STYLES = `
|
|
|
1480
1667
|
|
|
1481
1668
|
.item-chevron { color: var(--kp-muted); font-size: 16px; }
|
|
1482
1669
|
|
|
1670
|
+
/* ── Token list ─────────────────────────────────────────────────────────── */
|
|
1671
|
+
|
|
1483
1672
|
.token-item {
|
|
1484
|
-
display: flex;
|
|
1485
|
-
align-items: center;
|
|
1486
|
-
gap: 12px;
|
|
1673
|
+
display: flex; align-items: center; gap: 12px;
|
|
1487
1674
|
padding: 13px 14px;
|
|
1488
1675
|
cursor: pointer;
|
|
1489
1676
|
transition: all 0.15s;
|
|
@@ -1496,14 +1683,24 @@ const WIDGET_STYLES = `
|
|
|
1496
1683
|
}
|
|
1497
1684
|
|
|
1498
1685
|
.token-item:last-child { margin-bottom: 0; }
|
|
1499
|
-
|
|
1500
|
-
.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
|
+
|
|
1501
1697
|
.token-item.selected::after {
|
|
1502
1698
|
content: '';
|
|
1503
1699
|
position: absolute; inset: 0;
|
|
1504
1700
|
background: linear-gradient(90deg, rgba(99,102,241,0.08) 0%, transparent 100%);
|
|
1505
1701
|
pointer-events: none;
|
|
1506
1702
|
}
|
|
1703
|
+
|
|
1507
1704
|
.token-item.selected .token-symbol { color: var(--kp-accent); }
|
|
1508
1705
|
|
|
1509
1706
|
.token-left { display: flex; align-items: center; gap: 12px; flex: 1; }
|
|
@@ -1513,11 +1710,8 @@ const WIDGET_STYLES = `
|
|
|
1513
1710
|
border-radius: 10px;
|
|
1514
1711
|
background: var(--kp-surface-2);
|
|
1515
1712
|
border: 1px solid var(--kp-border);
|
|
1516
|
-
overflow: hidden;
|
|
1517
|
-
flex-
|
|
1518
|
-
display: flex;
|
|
1519
|
-
align-items: center;
|
|
1520
|
-
justify-content: center;
|
|
1713
|
+
overflow: hidden; flex-shrink: 0;
|
|
1714
|
+
display: flex; align-items: center; justify-content: center;
|
|
1521
1715
|
}
|
|
1522
1716
|
|
|
1523
1717
|
.token-icon img { width: 22px; height: 22px; object-fit: contain; }
|
|
@@ -1543,6 +1737,8 @@ const WIDGET_STYLES = `
|
|
|
1543
1737
|
.token-chevron { color: var(--kp-muted); font-size: 16px; flex-shrink: 0; }
|
|
1544
1738
|
#kwespay-tokenList { display: flex; flex-direction: column; }
|
|
1545
1739
|
|
|
1740
|
+
/* ── Buttons ────────────────────────────────────────────────────────────── */
|
|
1741
|
+
|
|
1546
1742
|
.bottom-action {
|
|
1547
1743
|
padding: 12px 20px 16px;
|
|
1548
1744
|
background: var(--kp-bg);
|
|
@@ -1584,6 +1780,7 @@ const WIDGET_STYLES = `
|
|
|
1584
1780
|
}
|
|
1585
1781
|
|
|
1586
1782
|
.action-btn.secondary::before { display: none; }
|
|
1783
|
+
|
|
1587
1784
|
.action-btn.secondary:hover {
|
|
1588
1785
|
background: var(--kp-surface-2);
|
|
1589
1786
|
border-color: var(--kp-border-active);
|
|
@@ -1593,14 +1790,13 @@ const WIDGET_STYLES = `
|
|
|
1593
1790
|
|
|
1594
1791
|
.action-btn:disabled { opacity: 0.3; cursor: not-allowed; box-shadow: none; }
|
|
1595
1792
|
|
|
1793
|
+
/* ── Footer ─────────────────────────────────────────────────────────────── */
|
|
1794
|
+
|
|
1596
1795
|
.kp-footer {
|
|
1597
1796
|
padding: 10px 20px 12px;
|
|
1598
1797
|
background: var(--kp-bg);
|
|
1599
1798
|
border-top: 1px solid var(--kp-border);
|
|
1600
|
-
display: flex;
|
|
1601
|
-
align-items: center;
|
|
1602
|
-
justify-content: center;
|
|
1603
|
-
gap: 6px;
|
|
1799
|
+
display: flex; align-items: center; justify-content: center; gap: 6px;
|
|
1604
1800
|
flex-shrink: 0;
|
|
1605
1801
|
}
|
|
1606
1802
|
|
|
@@ -1616,11 +1812,11 @@ const WIDGET_STYLES = `
|
|
|
1616
1812
|
|
|
1617
1813
|
.kp-footer-text span { color: var(--kp-text); font-weight: 500; }
|
|
1618
1814
|
|
|
1815
|
+
/* ── Spinner / loading ──────────────────────────────────────────────────── */
|
|
1816
|
+
|
|
1619
1817
|
.loading-container {
|
|
1620
|
-
display: flex;
|
|
1621
|
-
|
|
1622
|
-
align-items: center;
|
|
1623
|
-
justify-content: center;
|
|
1818
|
+
display: flex; flex-direction: column;
|
|
1819
|
+
align-items: center; justify-content: center;
|
|
1624
1820
|
padding: 24px 20px;
|
|
1625
1821
|
flex: 1;
|
|
1626
1822
|
}
|
|
@@ -1650,6 +1846,21 @@ const WIDGET_STYLES = `
|
|
|
1650
1846
|
animation: kpSpin 1.2s linear infinite reverse;
|
|
1651
1847
|
}
|
|
1652
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
|
+
|
|
1653
1864
|
@keyframes kpSpin { to { transform: rotate(360deg); } }
|
|
1654
1865
|
|
|
1655
1866
|
.spinner-icon {
|
|
@@ -1683,6 +1894,39 @@ const WIDGET_STYLES = `
|
|
|
1683
1894
|
font-weight: 400;
|
|
1684
1895
|
}
|
|
1685
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
|
+
|
|
1686
1930
|
.success-icon {
|
|
1687
1931
|
width: 64px; height: 64px;
|
|
1688
1932
|
border-radius: 16px;
|
|
@@ -1694,6 +1938,8 @@ const WIDGET_STYLES = `
|
|
|
1694
1938
|
|
|
1695
1939
|
.success-icon .material-symbols-outlined { font-size: 36px; color: var(--kp-green); }
|
|
1696
1940
|
|
|
1941
|
+
/* ── Error icon + hint ──────────────────────────────────────────────────── */
|
|
1942
|
+
|
|
1697
1943
|
.error-icon {
|
|
1698
1944
|
width: 64px; height: 64px;
|
|
1699
1945
|
border-radius: 16px;
|
|
@@ -1705,12 +1951,28 @@ const WIDGET_STYLES = `
|
|
|
1705
1951
|
|
|
1706
1952
|
.error-icon .material-symbols-outlined { font-size: 36px; color: var(--kp-red); }
|
|
1707
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
|
+
|
|
1708
1972
|
.network-status { padding: 14px 20px 0; }
|
|
1709
1973
|
|
|
1710
1974
|
.status-card {
|
|
1711
|
-
display: flex;
|
|
1712
|
-
align-items: center;
|
|
1713
|
-
gap: 10px;
|
|
1975
|
+
display: flex; align-items: center; gap: 10px;
|
|
1714
1976
|
background: var(--kp-accent-dim);
|
|
1715
1977
|
border: 1px solid rgba(99,102,241,0.2);
|
|
1716
1978
|
border-radius: 10px;
|
|
@@ -1728,6 +1990,8 @@ const WIDGET_STYLES = `
|
|
|
1728
1990
|
.status-icon img { width: 16px; height: 16px; object-fit: contain; }
|
|
1729
1991
|
.status-text { color: var(--kp-accent); font-size: 12px; font-weight: 600; flex: 1; }
|
|
1730
1992
|
|
|
1993
|
+
/* ── Review body ────────────────────────────────────────────────────────── */
|
|
1994
|
+
|
|
1731
1995
|
.kp-review-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
|
|
1732
1996
|
.kp-review-body::-webkit-scrollbar { width: 3px; }
|
|
1733
1997
|
.kp-review-body::-webkit-scrollbar-thumb { background: rgba(99,102,241,0.2); border-radius: 10px; }
|
|
@@ -1759,6 +2023,8 @@ const WIDGET_STYLES = `
|
|
|
1759
2023
|
|
|
1760
2024
|
.kp-review-crypto-line.loading { opacity: 0.4; }
|
|
1761
2025
|
|
|
2026
|
+
/* ── Detail / fee blocks ────────────────────────────────────────────────── */
|
|
2027
|
+
|
|
1762
2028
|
.kp-detail-block {
|
|
1763
2029
|
background: var(--kp-surface);
|
|
1764
2030
|
border: 1px solid var(--kp-border);
|
|
@@ -1768,9 +2034,7 @@ const WIDGET_STYLES = `
|
|
|
1768
2034
|
}
|
|
1769
2035
|
|
|
1770
2036
|
.kp-detail-row {
|
|
1771
|
-
display: flex;
|
|
1772
|
-
justify-content: space-between;
|
|
1773
|
-
align-items: center;
|
|
2037
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
1774
2038
|
padding: 11px 16px;
|
|
1775
2039
|
border-bottom: 1px solid var(--kp-border);
|
|
1776
2040
|
}
|
|
@@ -1794,7 +2058,7 @@ const WIDGET_STYLES = `
|
|
|
1794
2058
|
}
|
|
1795
2059
|
|
|
1796
2060
|
.kp-detail-val.accent { color: var(--kp-accent); }
|
|
1797
|
-
.kp-detail-val.green
|
|
2061
|
+
.kp-detail-val.green { color: var(--kp-green); }
|
|
1798
2062
|
|
|
1799
2063
|
.kp-fee-block {
|
|
1800
2064
|
background: rgba(16,185,129,0.04);
|
|
@@ -1824,6 +2088,8 @@ const WIDGET_STYLES = `
|
|
|
1824
2088
|
|
|
1825
2089
|
.kp-fee-header .material-symbols-outlined { font-size: 13px; color: var(--kp-green); }
|
|
1826
2090
|
|
|
2091
|
+
/* ── Transaction receipt ────────────────────────────────────────────────── */
|
|
2092
|
+
|
|
1827
2093
|
.tx-details {
|
|
1828
2094
|
width: 100%;
|
|
1829
2095
|
background: var(--kp-surface);
|
|
@@ -1834,17 +2100,15 @@ const WIDGET_STYLES = `
|
|
|
1834
2100
|
}
|
|
1835
2101
|
|
|
1836
2102
|
.tx-row {
|
|
1837
|
-
display: flex;
|
|
1838
|
-
justify-content: space-between;
|
|
1839
|
-
align-items: center;
|
|
2103
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
1840
2104
|
padding: 10px 16px;
|
|
1841
2105
|
border-bottom: 1px solid var(--kp-border);
|
|
1842
2106
|
}
|
|
1843
2107
|
|
|
1844
2108
|
.tx-row:last-child { border-bottom: none; }
|
|
1845
2109
|
|
|
1846
|
-
.tx-label
|
|
1847
|
-
.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); }
|
|
1848
2112
|
.tx-hash-row { display: flex; align-items: center; gap: 8px; }
|
|
1849
2113
|
|
|
1850
2114
|
.explorer-link {
|
|
@@ -1861,26 +2125,57 @@ const WIDGET_STYLES = `
|
|
|
1861
2125
|
.explorer-link:hover { background: rgba(99,102,241,0.2); }
|
|
1862
2126
|
.explorer-link .material-symbols-outlined { font-size: 12px; color: var(--kp-accent); }
|
|
1863
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
|
+
|
|
1864
2161
|
.mobile-instruction {
|
|
1865
2162
|
background: var(--kp-accent-dim);
|
|
1866
2163
|
border: 1px solid rgba(99,102,241,0.2);
|
|
1867
2164
|
border-radius: 10px;
|
|
1868
2165
|
padding: 12px;
|
|
1869
2166
|
margin: 12px 0 0;
|
|
1870
|
-
display: flex;
|
|
1871
|
-
align-items: flex-start;
|
|
1872
|
-
gap: 10px;
|
|
2167
|
+
display: flex; align-items: flex-start; gap: 10px;
|
|
1873
2168
|
}
|
|
1874
2169
|
|
|
1875
2170
|
.mobile-instruction-icon { color: var(--kp-accent); font-size: 18px; flex-shrink: 0; margin-top: 1px; }
|
|
1876
2171
|
.mobile-instruction-text { flex: 1; }
|
|
1877
2172
|
.mobile-instruction-title { color: var(--kp-text); font-size: 12px; font-weight: 600; margin-bottom: 3px; }
|
|
1878
|
-
.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 ────────────────────────────────────────────────────────── */
|
|
1879
2176
|
|
|
1880
2177
|
.kp-quote-timer {
|
|
1881
|
-
display: flex;
|
|
1882
|
-
align-items: center;
|
|
1883
|
-
gap: 5px;
|
|
2178
|
+
display: flex; align-items: center; gap: 5px;
|
|
1884
2179
|
font-family: var(--kp-mono);
|
|
1885
2180
|
font-size: 10px;
|
|
1886
2181
|
color: var(--kp-muted);
|
|
@@ -1889,9 +2184,33 @@ const WIDGET_STYLES = `
|
|
|
1889
2184
|
}
|
|
1890
2185
|
|
|
1891
2186
|
.kp-quote-timer .material-symbols-outlined { font-size: 12px; }
|
|
1892
|
-
.kp-quote-timer.urgent
|
|
2187
|
+
.kp-quote-timer.urgent { color: #fb923c; }
|
|
1893
2188
|
.kp-quote-timer.expired { color: var(--kp-red); }
|
|
1894
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
|
+
|
|
2213
|
+
|
|
1895
2214
|
@media (max-width: 480px) {
|
|
1896
2215
|
.kwespay-overlay {
|
|
1897
2216
|
padding: 0;
|
|
@@ -1916,79 +2235,38 @@ const WIDGET_STYLES = `
|
|
|
1916
2235
|
animation: kpSheetDown 0.28s ease forwards;
|
|
1917
2236
|
}
|
|
1918
2237
|
|
|
1919
|
-
.kwespay-container::before {
|
|
1920
|
-
content: '';
|
|
1921
|
-
display: block;
|
|
1922
|
-
position: absolute;
|
|
1923
|
-
top: 8px; left: 50%;
|
|
1924
|
-
transform: translateX(-50%);
|
|
1925
|
-
width: 28px; height: 3px;
|
|
1926
|
-
border-radius: 2px;
|
|
1927
|
-
background: rgba(255,255,255,0.12);
|
|
1928
|
-
z-index: 10;
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
.kp-mobile-connect-status {
|
|
1934
|
-
display: flex;
|
|
1935
|
-
align-items: center;
|
|
1936
|
-
gap: 12px;
|
|
1937
|
-
background: var(--kp-accent-dim);
|
|
1938
|
-
border: 1px solid rgba(99,102,241,0.2);
|
|
1939
|
-
border-radius: 12px;
|
|
1940
|
-
padding: 14px 16px;
|
|
1941
|
-
margin-bottom: 4px;
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
|
-
.kp-mobile-status-icon {
|
|
1945
|
-
width: 28px; height: 28px;
|
|
1946
|
-
display: flex; align-items: center; justify-content: center;
|
|
1947
|
-
flex-shrink: 0;
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
.kp-mobile-status-text { flex: 1; }
|
|
1951
|
-
|
|
1952
|
-
.kp-mobile-status-title {
|
|
1953
|
-
font-size: 13px;
|
|
1954
|
-
font-weight: 600;
|
|
1955
|
-
color: var(--kp-text);
|
|
1956
|
-
margin-bottom: 2px;
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
.kp-mobile-status-desc {
|
|
1960
|
-
font-family: var(--kp-mono);
|
|
1961
|
-
font-size: 11px;
|
|
1962
|
-
color: var(--kp-muted);
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
.kp-wallet-option:active {
|
|
1966
|
-
transform: scale(0.98);
|
|
1967
|
-
background: var(--kp-accent-dim);
|
|
1968
|
-
border-color: var(--kp-border-active);
|
|
1969
|
-
}
|
|
1970
|
-
|
|
2238
|
+
.kwespay-container::before {
|
|
2239
|
+
content: '';
|
|
2240
|
+
display: block;
|
|
2241
|
+
position: absolute;
|
|
2242
|
+
top: 8px; left: 50%;
|
|
2243
|
+
transform: translateX(-50%);
|
|
2244
|
+
width: 28px; height: 3px;
|
|
2245
|
+
border-radius: 2px;
|
|
2246
|
+
background: rgba(255,255,255,0.12);
|
|
2247
|
+
z-index: 10;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
1971
2250
|
|
|
1972
2251
|
@keyframes kpSheetUp {
|
|
1973
2252
|
from { transform: translateY(100%); opacity: 0.8; }
|
|
1974
|
-
to { transform: translateY(0);
|
|
2253
|
+
to { transform: translateY(0); opacity: 1; }
|
|
1975
2254
|
}
|
|
1976
2255
|
|
|
1977
2256
|
@keyframes kpSheetDown {
|
|
1978
|
-
from { transform: translateY(0);
|
|
2257
|
+
from { transform: translateY(0); opacity: 1; }
|
|
1979
2258
|
to { transform: translateY(100%); opacity: 0; }
|
|
1980
2259
|
}
|
|
1981
2260
|
`;
|
|
1982
2261
|
|
|
1983
2262
|
function getStepTemplates(fiatAmount, currency) {
|
|
1984
2263
|
return `
|
|
2264
|
+
<!-- Step 0: Initialising -->
|
|
1985
2265
|
<div class="step active" id="kwespay-step0">
|
|
1986
2266
|
<div class="kp-topbar">
|
|
1987
2267
|
<div class="kp-topbar-brand">
|
|
1988
|
-
<div class="kp-topbar-dot"></div>
|
|
1989
2268
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
1990
2269
|
</div>
|
|
1991
|
-
|
|
1992
2270
|
</div>
|
|
1993
2271
|
<div class="loading-container" style="flex:1">
|
|
1994
2272
|
<div class="spinner-wrapper">
|
|
@@ -2007,10 +2285,10 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2007
2285
|
</div>
|
|
2008
2286
|
</div>
|
|
2009
2287
|
|
|
2288
|
+
<!-- Step 0.5: API key invalid / network error on init -->
|
|
2010
2289
|
<div class="step" id="kwespay-step0-invalid">
|
|
2011
2290
|
<div class="kp-topbar">
|
|
2012
2291
|
<div class="kp-topbar-brand">
|
|
2013
|
-
<div class="kp-topbar-dot" style="background:var(--kp-red);box-shadow:none;animation:none"></div>
|
|
2014
2292
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2015
2293
|
</div>
|
|
2016
2294
|
</div>
|
|
@@ -2026,13 +2304,12 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2026
2304
|
</div>
|
|
2027
2305
|
</div>
|
|
2028
2306
|
|
|
2307
|
+
<!-- Step 1: Select Network -->
|
|
2029
2308
|
<div class="step" id="kwespay-step1">
|
|
2030
2309
|
<div class="kp-topbar">
|
|
2031
2310
|
<div class="kp-topbar-brand">
|
|
2032
|
-
<div class="kp-topbar-dot"></div>
|
|
2033
2311
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2034
2312
|
</div>
|
|
2035
|
-
|
|
2036
2313
|
</div>
|
|
2037
2314
|
<div class="kp-amount-block">
|
|
2038
2315
|
<div class="kp-amount-label">Total due</div>
|
|
@@ -2060,21 +2337,19 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2060
2337
|
</div>
|
|
2061
2338
|
</div>
|
|
2062
2339
|
|
|
2340
|
+
<!-- Step 2: Select Token -->
|
|
2063
2341
|
<div class="step" id="kwespay-step2">
|
|
2064
2342
|
<div class="kp-topbar">
|
|
2065
2343
|
<div class="kp-topbar-brand">
|
|
2066
2344
|
<button class="kp-back-btn" id="kwespay-back2">
|
|
2067
2345
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2068
2346
|
</button>
|
|
2069
|
-
<div class="kp-topbar-dot"></div>
|
|
2070
2347
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2071
2348
|
</div>
|
|
2072
|
-
|
|
2073
2349
|
</div>
|
|
2074
2350
|
<div class="kp-amount-block">
|
|
2075
2351
|
<div class="kp-amount-label">Total due</div>
|
|
2076
2352
|
<div class="kp-amount-value">${fiatAmount} ${currency}</div>
|
|
2077
|
-
<div class="kp-amount-hint">Select a token — exact amount shown at review</div>
|
|
2078
2353
|
</div>
|
|
2079
2354
|
<div class="progress-section">
|
|
2080
2355
|
<div class="progress-info">
|
|
@@ -2103,16 +2378,15 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2103
2378
|
</div>
|
|
2104
2379
|
</div>
|
|
2105
2380
|
|
|
2381
|
+
<!-- Step 3: Review & Pay -->
|
|
2106
2382
|
<div class="step" id="kwespay-step3">
|
|
2107
2383
|
<div class="kp-topbar">
|
|
2108
2384
|
<div class="kp-topbar-brand">
|
|
2109
2385
|
<button class="kp-back-btn" id="kwespay-back3">
|
|
2110
2386
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2111
2387
|
</button>
|
|
2112
|
-
<div class="kp-topbar-dot"></div>
|
|
2113
2388
|
<span class="kp-topbar-name">KwesPay Checkout</span>
|
|
2114
2389
|
</div>
|
|
2115
|
-
|
|
2116
2390
|
</div>
|
|
2117
2391
|
<div class="progress-section">
|
|
2118
2392
|
<div class="progress-info">
|
|
@@ -2134,37 +2408,35 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2134
2408
|
<span id="kwespay-quoteTimerText">—</span>
|
|
2135
2409
|
</div>
|
|
2136
2410
|
</div>
|
|
2137
|
-
|
|
2138
2411
|
<div class="kp-detail-block">
|
|
2139
2412
|
<div class="kp-detail-row">
|
|
2140
|
-
<span class="kp-detail-key">
|
|
2413
|
+
<span class="kp-detail-key">Wallet</span>
|
|
2141
2414
|
<span class="kp-detail-val" id="kwespay-connectedWalletAddress">—</span>
|
|
2142
2415
|
</div>
|
|
2143
2416
|
<div class="kp-detail-row">
|
|
2144
|
-
<span class="kp-detail-key">
|
|
2417
|
+
<span class="kp-detail-key">Network</span>
|
|
2145
2418
|
<span class="kp-detail-val" id="kwespay-summaryNetwork">—</span>
|
|
2146
2419
|
</div>
|
|
2147
2420
|
<div class="kp-detail-row">
|
|
2148
|
-
<span class="kp-detail-key">
|
|
2421
|
+
<span class="kp-detail-key">Token</span>
|
|
2149
2422
|
<span class="kp-detail-val accent" id="kwespay-summaryToken">—</span>
|
|
2150
2423
|
</div>
|
|
2151
2424
|
</div>
|
|
2152
|
-
|
|
2153
2425
|
<div class="kp-fee-block">
|
|
2154
2426
|
<div class="kp-fee-header">
|
|
2155
2427
|
<span class="material-symbols-outlined">receipt_long</span>
|
|
2156
2428
|
<span class="kp-fee-header-text">Fee Breakdown</span>
|
|
2157
2429
|
</div>
|
|
2158
2430
|
<div class="kp-detail-row">
|
|
2159
|
-
<span class="kp-detail-key">
|
|
2431
|
+
<span class="kp-detail-key">Payment amount</span>
|
|
2160
2432
|
<span class="kp-detail-val" id="kwespay-feePaymentAmount">—</span>
|
|
2161
2433
|
</div>
|
|
2162
2434
|
<div class="kp-detail-row">
|
|
2163
|
-
<span class="kp-detail-key">
|
|
2435
|
+
<span class="kp-detail-key">Platform fee (0.25%)</span>
|
|
2164
2436
|
<span class="kp-detail-val" id="kwespay-feePlatformFee">—</span>
|
|
2165
2437
|
</div>
|
|
2166
2438
|
<div class="kp-detail-row">
|
|
2167
|
-
<span class="kp-detail-key">
|
|
2439
|
+
<span class="kp-detail-key">Vendor receives</span>
|
|
2168
2440
|
<span class="kp-detail-val green" id="kwespay-feeVendorAmount">—</span>
|
|
2169
2441
|
</div>
|
|
2170
2442
|
</div>
|
|
@@ -2175,14 +2447,18 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2175
2447
|
</div>
|
|
2176
2448
|
</div>
|
|
2177
2449
|
|
|
2450
|
+
<!-- Step 4: Processing / Confirming / Success (mutating sub-views) -->
|
|
2178
2451
|
<div class="step" id="kwespay-step4">
|
|
2179
2452
|
<div class="kp-topbar">
|
|
2180
2453
|
<div class="kp-topbar-brand">
|
|
2181
|
-
<div class="kp-topbar-dot"></div>
|
|
2182
|
-
<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>
|
|
2183
2456
|
</div>
|
|
2457
|
+
|
|
2184
2458
|
</div>
|
|
2185
|
-
|
|
2459
|
+
|
|
2460
|
+
<!-- Sub-view: processing (wallet approval + on-chain) -->
|
|
2461
|
+
<div id="kwespay-view-processing" class="loading-container" style="flex:1">
|
|
2186
2462
|
<div class="spinner-wrapper">
|
|
2187
2463
|
<div class="spinner-ring"></div>
|
|
2188
2464
|
<div class="spinner-ring-2"></div>
|
|
@@ -2200,62 +2476,92 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2200
2476
|
</div>
|
|
2201
2477
|
</div>
|
|
2202
2478
|
</div>
|
|
2203
|
-
</div>
|
|
2204
2479
|
|
|
2205
|
-
|
|
2206
|
-
<div class="
|
|
2207
|
-
<div class="
|
|
2208
|
-
<div class="
|
|
2209
|
-
<
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
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>
|
|
2214
2488
|
</div>
|
|
2489
|
+
<h2 class="headline">Confirming Payment</h2>
|
|
2490
|
+
<p class="body-text" id="kwespay-confirmingText">Waiting for network confirmation.</p>
|
|
2215
2491
|
</div>
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
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>
|
|
2219
2500
|
</div>
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
<div class="
|
|
2223
|
-
<div class="
|
|
2224
|
-
<span class="
|
|
2225
|
-
<div class="tx-hash-row">
|
|
2226
|
-
<span class="tx-value" id="kwespay-txHash">—</span>
|
|
2227
|
-
<a class="explorer-link" id="kwespay-explorerLink" target="_blank" rel="noopener noreferrer">
|
|
2228
|
-
<span class="material-symbols-outlined">open_in_new</span>
|
|
2229
|
-
</a>
|
|
2230
|
-
</div>
|
|
2231
|
-
</div>
|
|
2232
|
-
<div class="tx-row">
|
|
2233
|
-
<span class="tx-label">amount paid</span>
|
|
2234
|
-
<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>
|
|
2235
2506
|
</div>
|
|
2236
|
-
<
|
|
2237
|
-
|
|
2238
|
-
|
|
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>
|
|
2239
2540
|
</div>
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
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>
|
|
2243
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>
|
|
2244
2556
|
</div>
|
|
2245
|
-
</div>
|
|
2246
|
-
<div class="bottom-action">
|
|
2247
|
-
<button class="action-btn" id="kwespay-closeSuccessBtn">Done</button>
|
|
2248
|
-
</div>
|
|
2249
|
-
<div class="kp-footer">
|
|
2250
|
-
<span class="material-symbols-outlined kp-footer-lock" style="font-size:12px">lock</span>
|
|
2251
|
-
<span class="kp-footer-text">Secured by <span>KwesPay</span> · On-chain verified</span>
|
|
2252
2557
|
</div>
|
|
2253
2558
|
</div>
|
|
2254
2559
|
|
|
2255
|
-
|
|
2560
|
+
<!-- Step 5: Error / Failed -->
|
|
2561
|
+
<div class="step" id="kwespay-step5">
|
|
2256
2562
|
<div class="kp-topbar">
|
|
2257
2563
|
<div class="kp-topbar-brand">
|
|
2258
|
-
<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>
|
|
2259
2565
|
<span class="kp-topbar-name">Payment Failed</span>
|
|
2260
2566
|
</div>
|
|
2261
2567
|
</div>
|
|
@@ -2265,6 +2571,12 @@ function getStepTemplates(fiatAmount, currency) {
|
|
|
2265
2571
|
</div>
|
|
2266
2572
|
<h2 class="headline" id="kwespay-errorTitle">Something went wrong</h2>
|
|
2267
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>
|
|
2268
2580
|
</div>
|
|
2269
2581
|
<div class="bottom-action">
|
|
2270
2582
|
<button class="action-btn" id="kwespay-retryPayment">Try Again</button>
|
|
@@ -2311,27 +2623,29 @@ const DomMethods = {
|
|
|
2311
2623
|
overlay.className = "kwespay-overlay";
|
|
2312
2624
|
overlay.id = "kwespay-widget-overlay";
|
|
2313
2625
|
|
|
2626
|
+
const container = document.createElement("div");
|
|
2627
|
+
container.className = "kwespay-container";
|
|
2628
|
+
container.id = "kwespay-widget-container";
|
|
2629
|
+
|
|
2314
2630
|
const closeBtn = document.createElement("button");
|
|
2315
2631
|
closeBtn.className = "kwespay-close-btn";
|
|
2316
2632
|
closeBtn.innerHTML = "×";
|
|
2317
2633
|
closeBtn.onclick = () => this.close();
|
|
2318
|
-
|
|
2634
|
+
container.appendChild(closeBtn);
|
|
2319
2635
|
|
|
2320
|
-
const
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
this.config.amount,
|
|
2636
|
+
const stepsWrapper = document.createElement("div");
|
|
2637
|
+
stepsWrapper.className = "kwespay-steps-wrapper";
|
|
2638
|
+
stepsWrapper.innerHTML = getStepTemplates(
|
|
2639
|
+
this._displayAmount,
|
|
2325
2640
|
this.config.currency
|
|
2326
2641
|
);
|
|
2642
|
+
container.appendChild(stepsWrapper);
|
|
2327
2643
|
|
|
2328
2644
|
overlay.appendChild(container);
|
|
2329
2645
|
document.body.appendChild(overlay);
|
|
2330
2646
|
|
|
2331
2647
|
this._setupEventListeners();
|
|
2332
2648
|
this._setupSwipeToClose(container);
|
|
2333
|
-
|
|
2334
|
-
// Clicking outside the container does NOT close the widget (intentional)
|
|
2335
2649
|
},
|
|
2336
2650
|
|
|
2337
2651
|
_setupSwipeToClose(container) {
|
|
@@ -2502,8 +2816,11 @@ const NavMethods = {
|
|
|
2502
2816
|
targetStep = document.getElementById(`kwespay-step-${stepNumber}`);
|
|
2503
2817
|
else targetStep = document.getElementById(`kwespay-step${stepNumber}`);
|
|
2504
2818
|
|
|
2505
|
-
if (targetStep)
|
|
2506
|
-
|
|
2819
|
+
if (targetStep) {
|
|
2820
|
+
targetStep.classList.add("active");
|
|
2821
|
+
} else {
|
|
2822
|
+
console.warn(`[KwesPayWidget] Step not found: ${stepNumber}`);
|
|
2823
|
+
}
|
|
2507
2824
|
},
|
|
2508
2825
|
|
|
2509
2826
|
_removeCustomStep(id) {
|
|
@@ -2513,13 +2830,16 @@ const NavMethods = {
|
|
|
2513
2830
|
_showError(title, message) {
|
|
2514
2831
|
const titleEl = document.getElementById("kwespay-errorTitle");
|
|
2515
2832
|
const msgEl = document.getElementById("kwespay-errorMessage");
|
|
2516
|
-
if (titleEl) titleEl.textContent = title;
|
|
2517
|
-
if (msgEl)
|
|
2518
|
-
|
|
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);
|
|
2519
2838
|
},
|
|
2520
2839
|
|
|
2521
2840
|
_reset() {
|
|
2522
2841
|
this._clearQuoteTimer();
|
|
2842
|
+
this._stopReceiptCountdown();
|
|
2523
2843
|
this._removeCustomStep("kwespay-step-wallet-picker");
|
|
2524
2844
|
this._removeCustomStep("kwespay-step-wc");
|
|
2525
2845
|
this.state.selectedNetwork = null;
|
|
@@ -2587,23 +2907,13 @@ const NetworkMethods = {
|
|
|
2587
2907
|
const listItem = document.createElement("div");
|
|
2588
2908
|
listItem.className = "list-item";
|
|
2589
2909
|
listItem.innerHTML = `
|
|
2590
|
-
<div class="item-icon"><img src="${network.logo}" alt="${
|
|
2591
|
-
network.name
|
|
2592
|
-
}" /></div>
|
|
2910
|
+
<div class="item-icon"><img src="${network.logo}" alt="${network.name}" /></div>
|
|
2593
2911
|
<div class="item-info">
|
|
2594
2912
|
<div class="item-name-row">
|
|
2595
2913
|
<p class="item-name">${network.name}</p>
|
|
2596
|
-
|
|
2597
|
-
network.type === "testnet"
|
|
2598
|
-
? '<span class="item-badge badge-testnet">Testnet</span>'
|
|
2599
|
-
: ""
|
|
2600
|
-
}
|
|
2914
|
+
|
|
2601
2915
|
</div>
|
|
2602
|
-
|
|
2603
|
-
network.type === "mainnet"
|
|
2604
|
-
? "Mainnet · Production"
|
|
2605
|
-
: "Testnet · Development"
|
|
2606
|
-
}</p>
|
|
2916
|
+
|
|
2607
2917
|
</div>
|
|
2608
2918
|
<span class="material-symbols-outlined item-chevron">chevron_right</span>
|
|
2609
2919
|
`;
|
|
@@ -2742,7 +3052,7 @@ const WalletMethods = {
|
|
|
2742
3052
|
<button class="kp-back-btn" id="kwespay-picker-back">
|
|
2743
3053
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2744
3054
|
</button>
|
|
2745
|
-
|
|
3055
|
+
|
|
2746
3056
|
<span class="kp-topbar-name">Connect Wallet</span>
|
|
2747
3057
|
</div>
|
|
2748
3058
|
</div>
|
|
@@ -2912,7 +3222,7 @@ const WalletMethods = {
|
|
|
2912
3222
|
<button class="kp-back-btn" id="kwespay-wc-back">
|
|
2913
3223
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2914
3224
|
</button>
|
|
2915
|
-
|
|
3225
|
+
|
|
2916
3226
|
<span class="kp-topbar-name">Scan QR Code</span>
|
|
2917
3227
|
</div>
|
|
2918
3228
|
</div>
|
|
@@ -2952,7 +3262,7 @@ const WalletMethods = {
|
|
|
2952
3262
|
<button class="kp-back-btn" id="kwespay-wc-back">
|
|
2953
3263
|
<span class="material-symbols-outlined">arrow_back</span>
|
|
2954
3264
|
</button>
|
|
2955
|
-
|
|
3265
|
+
|
|
2956
3266
|
<span class="kp-topbar-name">Connect Wallet</span>
|
|
2957
3267
|
</div>
|
|
2958
3268
|
</div>
|
|
@@ -3102,8 +3412,6 @@ const WalletMethods = {
|
|
|
3102
3412
|
},
|
|
3103
3413
|
};
|
|
3104
3414
|
|
|
3105
|
-
const PLATFORM_FEE_BPS = 25;
|
|
3106
|
-
|
|
3107
3415
|
function formatUnits(rawBigInt, decimals) {
|
|
3108
3416
|
const divisor = BigInt(10 ** decimals);
|
|
3109
3417
|
const whole = rawBigInt / divisor;
|
|
@@ -3131,6 +3439,31 @@ function formatUnits(rawBigInt, decimals) {
|
|
|
3131
3439
|
return `0.${fracFull.slice(0, firstSig) + sigSlice}`;
|
|
3132
3440
|
}
|
|
3133
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
|
+
|
|
3134
3467
|
const QuoteMethods = {
|
|
3135
3468
|
async _loadReviewStep() {
|
|
3136
3469
|
this._clearQuoteTimer();
|
|
@@ -3143,47 +3476,65 @@ const QuoteMethods = {
|
|
|
3143
3476
|
const proceedBtn = document.getElementById("kwespay-proceedToPayment");
|
|
3144
3477
|
|
|
3145
3478
|
if (cryptoLine) {
|
|
3146
|
-
cryptoLine.textContent = "loading
|
|
3479
|
+
cryptoLine.textContent = "loading...";
|
|
3147
3480
|
cryptoLine.classList.add("loading");
|
|
3148
3481
|
}
|
|
3149
3482
|
if (timerEl) timerEl.style.display = "none";
|
|
3150
3483
|
if (proceedBtn) proceedBtn.disabled = true;
|
|
3151
3484
|
|
|
3152
3485
|
try {
|
|
3153
|
-
const
|
|
3154
|
-
|
|
3486
|
+
const quote = await this.paymentService.getQuote({
|
|
3487
|
+
vendorIdentifier: this.config.vendorId,
|
|
3155
3488
|
cryptoCurrency: this.state.selectedToken,
|
|
3156
3489
|
fiatAmount: this.config.amount,
|
|
3157
3490
|
fiatCurrency: this.config.currency,
|
|
3158
3491
|
network: this.state.selectedNetwork,
|
|
3159
|
-
payerWalletAddress: this.walletService.getAddress(),
|
|
3160
3492
|
});
|
|
3161
3493
|
|
|
3162
|
-
this.state.
|
|
3163
|
-
|
|
3164
|
-
const decimals = this.state.selectedTokenConfig?.decimals ?? 6;
|
|
3165
|
-
const amountBig = BigInt(payload.amountBaseUnits);
|
|
3166
|
-
const feeNum = (Number(amountBig) * PLATFORM_FEE_BPS) / 10000;
|
|
3167
|
-
const feeBig = BigInt(Math.max(1, Math.round(feeNum)));
|
|
3168
|
-
const vendorBig = amountBig - feeBig;
|
|
3494
|
+
const decimals = this.state.selectedTokenConfig?.decimals ?? 18;
|
|
3169
3495
|
const sym = this.state.selectedToken;
|
|
3170
|
-
|
|
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;
|
|
3171
3523
|
|
|
3172
3524
|
if (cryptoLine) {
|
|
3173
|
-
cryptoLine.textContent = fmt(
|
|
3525
|
+
cryptoLine.textContent = fmt(totalBig);
|
|
3174
3526
|
cryptoLine.classList.remove("loading");
|
|
3175
3527
|
}
|
|
3176
|
-
if (feeAmount) feeAmount.textContent = fmt(
|
|
3528
|
+
if (feeAmount) feeAmount.textContent = fmt(totalBig);
|
|
3177
3529
|
if (feePlatform) feePlatform.textContent = fmt(feeBig);
|
|
3178
|
-
if (feeVendor)
|
|
3179
|
-
feeVendor.textContent = fmt(vendorBig < 0n ? 0n : vendorBig);
|
|
3530
|
+
if (feeVendor) feeVendor.textContent = fmt(amountBig);
|
|
3180
3531
|
if (proceedBtn) proceedBtn.disabled = false;
|
|
3181
3532
|
|
|
3182
|
-
this._startQuoteTimer(
|
|
3533
|
+
this._startQuoteTimer(quote.expiresAt);
|
|
3183
3534
|
} catch (err) {
|
|
3184
|
-
console.error("[
|
|
3535
|
+
console.error("[KwesPay] Quote fetch failed:", err.message);
|
|
3185
3536
|
if (cryptoLine) {
|
|
3186
|
-
cryptoLine.textContent = "Could not load
|
|
3537
|
+
cryptoLine.textContent = "Could not load please try again.";
|
|
3187
3538
|
cryptoLine.classList.remove("loading");
|
|
3188
3539
|
}
|
|
3189
3540
|
if (proceedBtn) proceedBtn.disabled = true;
|
|
@@ -3201,7 +3552,8 @@ const QuoteMethods = {
|
|
|
3201
3552
|
const secs = Math.ceil(remaining / 1000);
|
|
3202
3553
|
const mins = Math.floor(secs / 60);
|
|
3203
3554
|
const s = secs % 60;
|
|
3204
|
-
|
|
3555
|
+
|
|
3556
|
+
timerText.textContent = `Quote expires in ${mins}:${String(s).padStart(
|
|
3205
3557
|
2,
|
|
3206
3558
|
"0"
|
|
3207
3559
|
)}`;
|
|
@@ -3211,7 +3563,7 @@ const QuoteMethods = {
|
|
|
3211
3563
|
(secs <= 0 ? " expired" : "");
|
|
3212
3564
|
|
|
3213
3565
|
if (secs <= 0) {
|
|
3214
|
-
timerText.textContent = "Refreshing your rate
|
|
3566
|
+
timerText.textContent = "Refreshing your rate...";
|
|
3215
3567
|
const proceedBtn = document.getElementById("kwespay-proceedToPayment");
|
|
3216
3568
|
if (proceedBtn) proceedBtn.disabled = true;
|
|
3217
3569
|
this._clearQuoteTimer();
|
|
@@ -3231,6 +3583,8 @@ const QuoteMethods = {
|
|
|
3231
3583
|
},
|
|
3232
3584
|
};
|
|
3233
3585
|
|
|
3586
|
+
const RECEIPT_DWELL_MS = 10_000;
|
|
3587
|
+
|
|
3234
3588
|
const PaymentMethods = {
|
|
3235
3589
|
async _handlePaymentProcessing() {
|
|
3236
3590
|
if (!this.state.currentPayload) {
|
|
@@ -3244,6 +3598,7 @@ const PaymentMethods = {
|
|
|
3244
3598
|
try {
|
|
3245
3599
|
this._clearQuoteTimer();
|
|
3246
3600
|
this._goToStep(4);
|
|
3601
|
+
this._setProcessingView("processing");
|
|
3247
3602
|
|
|
3248
3603
|
const setStatus = (title, text) => {
|
|
3249
3604
|
const titleEl = document.getElementById("kwespay-processingTitle");
|
|
@@ -3254,27 +3609,34 @@ const PaymentMethods = {
|
|
|
3254
3609
|
|
|
3255
3610
|
const isWC = this.walletService.connectionType === "walletconnect";
|
|
3256
3611
|
const isMobile = this.walletService.isMobile();
|
|
3257
|
-
const strictMobile = isWC && isMobile;
|
|
3612
|
+
const strictMobile = isWC && isMobile;
|
|
3258
3613
|
const provider = this.walletService.getProvider();
|
|
3259
3614
|
const targetChainId = this.state.selectedChainId;
|
|
3260
3615
|
|
|
3261
|
-
if (!provider)
|
|
3262
|
-
|
|
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
|
+
}
|
|
3263
3626
|
|
|
3264
|
-
const alive = await this.walletService
|
|
3627
|
+
const alive = await this.walletService
|
|
3628
|
+
.isSessionAlive()
|
|
3629
|
+
.catch(() => false);
|
|
3265
3630
|
if (!alive) {
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
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" }
|
|
3272
3637
|
);
|
|
3273
|
-
err.code = "SESSION_EXPIRED";
|
|
3274
|
-
throw err;
|
|
3275
3638
|
}
|
|
3276
3639
|
|
|
3277
|
-
|
|
3278
3640
|
if (isMobile) {
|
|
3279
3641
|
document
|
|
3280
3642
|
.getElementById("kwespay-mobileTransactionInstruction")
|
|
@@ -3282,17 +3644,20 @@ const PaymentMethods = {
|
|
|
3282
3644
|
}
|
|
3283
3645
|
|
|
3284
3646
|
if (strictMobile) {
|
|
3285
|
-
|
|
3286
3647
|
await this._assertMobileChain(provider, targetChainId);
|
|
3287
3648
|
} else {
|
|
3288
|
-
|
|
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
|
+
}
|
|
3289
3660
|
const currentChainId = parseInt(rawChain, 16);
|
|
3290
|
-
|
|
3291
|
-
console.log("[KwesPay] Desktop chain check —", {
|
|
3292
|
-
currentChainId,
|
|
3293
|
-
targetChainId,
|
|
3294
|
-
});
|
|
3295
|
-
|
|
3296
3661
|
if (currentChainId !== targetChainId) {
|
|
3297
3662
|
setStatus(
|
|
3298
3663
|
"Switching network…",
|
|
@@ -3305,11 +3670,9 @@ const PaymentMethods = {
|
|
|
3305
3670
|
this.state.selectedToken,
|
|
3306
3671
|
this.state.selectedTokenConfig.decimals
|
|
3307
3672
|
);
|
|
3308
|
-
console.log("[KwesPay] Network switched");
|
|
3309
3673
|
}
|
|
3310
3674
|
}
|
|
3311
3675
|
|
|
3312
|
-
|
|
3313
3676
|
if (strictMobile) {
|
|
3314
3677
|
setStatus(
|
|
3315
3678
|
"Opening your wallet…",
|
|
@@ -3324,56 +3687,111 @@ const PaymentMethods = {
|
|
|
3324
3687
|
);
|
|
3325
3688
|
}
|
|
3326
3689
|
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
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
|
+
}
|
|
3332
3714
|
|
|
3333
|
-
|
|
3334
3715
|
document
|
|
3335
3716
|
.getElementById("kwespay-mobileTransactionInstruction")
|
|
3336
3717
|
?.style.setProperty("display", "none");
|
|
3337
3718
|
|
|
3338
|
-
|
|
3339
|
-
const decimals = this.state.selectedTokenConfig?.decimals ?? 6;
|
|
3340
|
-
const amountBig = BigInt(this.state.currentPayload.amountBaseUnits);
|
|
3341
|
-
const cryptoDisplay = `${formatUnits(amountBig, decimals)} ${
|
|
3342
|
-
this.state.selectedToken
|
|
3343
|
-
}`;
|
|
3719
|
+
this._setProcessingView("confirming");
|
|
3344
3720
|
|
|
3345
|
-
|
|
3346
|
-
receipt.hash
|
|
3347
|
-
);
|
|
3348
|
-
document.getElementById(
|
|
3349
|
-
"kwespay-txFiatAmount"
|
|
3350
|
-
).textContent = `${this.config.amount} ${this.config.currency}`;
|
|
3351
|
-
document.getElementById("kwespay-txCryptoAmount").textContent =
|
|
3352
|
-
cryptoDisplay;
|
|
3353
|
-
document.getElementById("kwespay-txNetwork").textContent =
|
|
3354
|
-
this.state.selectedNetworkName;
|
|
3355
|
-
document.getElementById("kwespay-explorerLink").href =
|
|
3356
|
-
NETWORK_CONFIGS[this.state.selectedNetwork].explorer + receipt.hash;
|
|
3357
|
-
|
|
3358
|
-
this._goToStep(5);
|
|
3359
|
-
|
|
3360
|
-
dispatchWidgetEvent("paymentSuccess", {
|
|
3721
|
+
const onChainPayload = {
|
|
3361
3722
|
transactionReference: receipt.transactionReference,
|
|
3362
3723
|
paymentIdBytes32: receipt.paymentIdBytes32,
|
|
3363
3724
|
transactionHash: receipt.hash,
|
|
3725
|
+
transactionStatus: "pending",
|
|
3364
3726
|
fiatAmount: this.config.amount,
|
|
3365
3727
|
currency: this.config.currency,
|
|
3366
3728
|
token: this.state.selectedToken,
|
|
3367
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",
|
|
3368
3749
|
});
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
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}`
|
|
3375
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
|
+
};
|
|
3376
3792
|
|
|
3793
|
+
this._finalisePayment(finalPayload, false);
|
|
3794
|
+
} catch (error) {
|
|
3377
3795
|
document
|
|
3378
3796
|
.getElementById("kwespay-mobileTransactionInstruction")
|
|
3379
3797
|
?.style.setProperty("display", "none");
|
|
@@ -3382,71 +3800,171 @@ const PaymentMethods = {
|
|
|
3382
3800
|
let title = "Payment Failed";
|
|
3383
3801
|
let message = getErrorMessage(error, { token: this.state.selectedToken });
|
|
3384
3802
|
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
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
|
+
}
|
|
3396
3834
|
}
|
|
3397
3835
|
|
|
3836
|
+
console.error("[KwesPayWidget] Payment error:", {
|
|
3837
|
+
title,
|
|
3838
|
+
message,
|
|
3839
|
+
code: error.code,
|
|
3840
|
+
error,
|
|
3841
|
+
});
|
|
3842
|
+
|
|
3398
3843
|
this._showError(title, message);
|
|
3399
|
-
|
|
3844
|
+
this._failPayment(message, errorType);
|
|
3400
3845
|
}
|
|
3401
3846
|
},
|
|
3402
3847
|
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
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
|
+
|
|
3410
3944
|
async _assertMobileChain(provider, targetChainId) {
|
|
3411
3945
|
const MAX_ATTEMPTS = 3;
|
|
3412
3946
|
const DELAY_MS = 1000;
|
|
3413
3947
|
|
|
3414
3948
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
3415
3949
|
let currentChainId = null;
|
|
3416
|
-
|
|
3417
3950
|
try {
|
|
3418
3951
|
const raw = await provider.request({ method: "eth_chainId" });
|
|
3419
3952
|
currentChainId = parseInt(raw, 16);
|
|
3420
|
-
} catch
|
|
3421
|
-
console.error(
|
|
3422
|
-
`[KwesPay] eth_chainId RPC failed (attempt ${attempt}/${MAX_ATTEMPTS}):`,
|
|
3423
|
-
err.message
|
|
3424
|
-
);
|
|
3425
|
-
}
|
|
3426
|
-
|
|
3427
|
-
console.log(
|
|
3428
|
-
`[KwesPay] Chain check attempt ${attempt}/${MAX_ATTEMPTS} —`,
|
|
3429
|
-
{ currentChainId, targetChainId }
|
|
3430
|
-
);
|
|
3431
|
-
|
|
3432
|
-
if (currentChainId === targetChainId) {
|
|
3433
|
-
console.log("[KwesPay] Chain confirmed ✅", currentChainId);
|
|
3434
|
-
return;
|
|
3435
|
-
}
|
|
3953
|
+
} catch {}
|
|
3436
3954
|
|
|
3437
|
-
if (
|
|
3955
|
+
if (currentChainId === targetChainId) return;
|
|
3956
|
+
if (attempt < MAX_ATTEMPTS)
|
|
3438
3957
|
await new Promise((r) => setTimeout(r, DELAY_MS));
|
|
3439
|
-
}
|
|
3440
3958
|
}
|
|
3441
3959
|
|
|
3442
|
-
|
|
3443
|
-
|
|
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" }
|
|
3444
3965
|
);
|
|
3445
|
-
err.code = "WRONG_NETWORK";
|
|
3446
|
-
throw err;
|
|
3447
3966
|
},
|
|
3448
3967
|
|
|
3449
|
-
|
|
3450
3968
|
async _switchNetworkSafe(
|
|
3451
3969
|
chainId,
|
|
3452
3970
|
networkName,
|
|
@@ -3454,9 +3972,7 @@ const PaymentMethods = {
|
|
|
3454
3972
|
tokenSymbol,
|
|
3455
3973
|
tokenDecimals
|
|
3456
3974
|
) {
|
|
3457
|
-
const switchNetwork = this.walletService.switchNetwork;
|
|
3458
3975
|
const provider = this.walletService.getProvider();
|
|
3459
|
-
|
|
3460
3976
|
if (!provider) throw new Error("[WalletService] No provider connected");
|
|
3461
3977
|
|
|
3462
3978
|
const toHex = (val) => {
|
|
@@ -3481,15 +3997,10 @@ const PaymentMethods = {
|
|
|
3481
3997
|
await provider.request({ method: "eth_chainId" })
|
|
3482
3998
|
);
|
|
3483
3999
|
if (currentHex && currentHex === targetHex) return;
|
|
3484
|
-
} catch
|
|
3485
|
-
console.warn(
|
|
3486
|
-
"[KwesPay] Could not read chainId before switch:",
|
|
3487
|
-
err.message
|
|
3488
|
-
);
|
|
3489
|
-
}
|
|
4000
|
+
} catch {}
|
|
3490
4001
|
|
|
3491
4002
|
try {
|
|
3492
|
-
await switchNetwork(
|
|
4003
|
+
await this.walletService.switchNetwork(
|
|
3493
4004
|
chainId,
|
|
3494
4005
|
networkName,
|
|
3495
4006
|
rpcUrl,
|
|
@@ -3497,15 +4008,9 @@ const PaymentMethods = {
|
|
|
3497
4008
|
tokenDecimals
|
|
3498
4009
|
);
|
|
3499
4010
|
} catch (err) {
|
|
3500
|
-
if (err.code === 4001) throw err;
|
|
3501
|
-
// Some wallets throw even on success — continue to verify below
|
|
3502
|
-
console.warn(
|
|
3503
|
-
"[KwesPay] switchNetwork threw (verifying anyway):",
|
|
3504
|
-
err.message
|
|
3505
|
-
);
|
|
4011
|
+
if (err.code === 4001) throw err;
|
|
3506
4012
|
}
|
|
3507
4013
|
|
|
3508
|
-
// Poll until confirmed or 15s timeout
|
|
3509
4014
|
const POLL_MS = 500;
|
|
3510
4015
|
const TIMEOUT_MS = 15_000;
|
|
3511
4016
|
const started = Date.now();
|
|
@@ -3516,17 +4021,8 @@ const PaymentMethods = {
|
|
|
3516
4021
|
const currentHex = toHex(
|
|
3517
4022
|
await provider.request({ method: "eth_chainId" })
|
|
3518
4023
|
);
|
|
3519
|
-
if (currentHex && currentHex === targetHex)
|
|
3520
|
-
|
|
3521
|
-
`[KwesPay] Network switch confirmed after ${
|
|
3522
|
-
Date.now() - started
|
|
3523
|
-
}ms ✅`
|
|
3524
|
-
);
|
|
3525
|
-
return;
|
|
3526
|
-
}
|
|
3527
|
-
} catch (err) {
|
|
3528
|
-
console.warn("[KwesPay] Poll eth_chainId error:", err.message);
|
|
3529
|
-
}
|
|
4024
|
+
if (currentHex && currentHex === targetHex) return;
|
|
4025
|
+
} catch {}
|
|
3530
4026
|
}
|
|
3531
4027
|
|
|
3532
4028
|
throw new Error(
|
|
@@ -3544,6 +4040,14 @@ function resolveAcceptedTokens(input) {
|
|
|
3544
4040
|
return null;
|
|
3545
4041
|
}
|
|
3546
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
|
+
|
|
3547
4051
|
class KwesPayWidget {
|
|
3548
4052
|
constructor(config) {
|
|
3549
4053
|
if (!config.apiKey) throw new Error("[KwesPayWidget] apiKey is required");
|
|
@@ -3559,6 +4063,10 @@ class KwesPayWidget {
|
|
|
3559
4063
|
currency: config.currency || DEFAULT_CONFIG.currency,
|
|
3560
4064
|
graphqlEndpoint: DEFAULT_CONFIG.graphqlEndpoint,
|
|
3561
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,
|
|
3562
4070
|
};
|
|
3563
4071
|
|
|
3564
4072
|
if (!Object.values(SUPPORTED_CURRENCIES).includes(this.config.currency)) {
|
|
@@ -3591,21 +4099,61 @@ class KwesPayWidget {
|
|
|
3591
4099
|
wcUri: null,
|
|
3592
4100
|
};
|
|
3593
4101
|
|
|
4102
|
+
this._paymentResolve = null;
|
|
4103
|
+
this._paymentReject = null;
|
|
4104
|
+
this._finalised = false;
|
|
4105
|
+
this._receiptCountdownInterval = null;
|
|
4106
|
+
|
|
3594
4107
|
this._init();
|
|
3595
4108
|
}
|
|
3596
4109
|
|
|
3597
|
-
|
|
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
|
+
|
|
3598
4121
|
const overlay = document.getElementById("kwespay-widget-overlay");
|
|
3599
4122
|
const container = document.getElementById("kwespay-widget-container");
|
|
3600
|
-
if (
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
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
|
+
}
|
|
3606
4129
|
|
|
3607
|
-
|
|
4130
|
+
this._validateAPIKey();
|
|
3608
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);
|
|
3609
4157
|
}
|
|
3610
4158
|
|
|
3611
4159
|
close() {
|
|
@@ -3613,28 +4161,29 @@ class KwesPayWidget {
|
|
|
3613
4161
|
const container = document.getElementById("kwespay-widget-container");
|
|
3614
4162
|
if (!overlay || !container) return;
|
|
3615
4163
|
|
|
4164
|
+
this._stopReceiptCountdown();
|
|
3616
4165
|
this._clearQuoteTimer();
|
|
3617
|
-
|
|
4166
|
+
|
|
4167
|
+
const closedAfterSuccess = this.state.currentStep === 4;
|
|
3618
4168
|
const mobile = window.innerWidth <= 480;
|
|
3619
4169
|
|
|
3620
|
-
|
|
3621
|
-
container.classList.add("closing");
|
|
3622
|
-
setTimeout(() => {
|
|
3623
|
-
container.classList.remove("closing");
|
|
3624
|
-
overlay.classList.remove("open");
|
|
3625
|
-
document.body.classList.remove("kwespay-open");
|
|
3626
|
-
this.state.isOpen = false;
|
|
3627
|
-
dispatchWidgetEvent("widgetClosed", {
|
|
3628
|
-
completedPayment: closedAfterSuccess,
|
|
3629
|
-
});
|
|
3630
|
-
}, 300);
|
|
3631
|
-
} else {
|
|
4170
|
+
const finish = () => {
|
|
3632
4171
|
overlay.classList.remove("open");
|
|
3633
4172
|
document.body.classList.remove("kwespay-open");
|
|
3634
4173
|
this.state.isOpen = false;
|
|
3635
4174
|
dispatchWidgetEvent("widgetClosed", {
|
|
3636
4175
|
completedPayment: closedAfterSuccess,
|
|
3637
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();
|
|
3638
4187
|
}
|
|
3639
4188
|
}
|
|
3640
4189
|
|
|
@@ -3652,12 +4201,13 @@ class KwesPayWidget {
|
|
|
3652
4201
|
) {
|
|
3653
4202
|
this.config.currency = newCurrency;
|
|
3654
4203
|
}
|
|
4204
|
+
const display = `${this._displayAmount} ${this.config.currency}`;
|
|
3655
4205
|
document
|
|
3656
4206
|
.querySelectorAll(
|
|
3657
4207
|
'[id*="paymentAmount"], [id*="summaryFiatAmount"], [id*="txFiatAmount"], [id*="reviewFiatAmount"]'
|
|
3658
4208
|
)
|
|
3659
4209
|
.forEach((el) => {
|
|
3660
|
-
el.textContent =
|
|
4210
|
+
el.textContent = display;
|
|
3661
4211
|
});
|
|
3662
4212
|
dispatchWidgetEvent("amountUpdated", {
|
|
3663
4213
|
amount: this.config.amount,
|
|
@@ -3670,11 +4220,21 @@ class KwesPayWidget {
|
|
|
3670
4220
|
}
|
|
3671
4221
|
|
|
3672
4222
|
destroy() {
|
|
4223
|
+
this._stopReceiptCountdown();
|
|
3673
4224
|
this._clearQuoteTimer();
|
|
3674
4225
|
document.body.classList.remove("kwespay-open");
|
|
3675
4226
|
this.walletService?.disconnect();
|
|
3676
4227
|
document.getElementById("kwespay-widget-overlay")?.remove();
|
|
3677
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
|
+
|
|
3678
4238
|
this.state = null;
|
|
3679
4239
|
this.config = null;
|
|
3680
4240
|
this.walletService = null;
|
|
@@ -3694,9 +4254,103 @@ Object.assign(
|
|
|
3694
4254
|
PaymentMethods
|
|
3695
4255
|
);
|
|
3696
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
|
+
|
|
3697
4344
|
/**
|
|
3698
|
-
*
|
|
3699
|
-
* @
|
|
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").
|
|
3700
4354
|
*/
|
|
3701
4355
|
|
|
3702
|
-
export { KwesPayWidget as default };
|
|
4356
|
+
export { KwesPayWidget, KwesPayWidget as default, kwespay };
|