@pandait.tech/payment-nuvei 0.1.0
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 +211 -0
- package/dist/handlers/index.cjs +1671 -0
- package/dist/handlers/index.cjs.map +1 -0
- package/dist/handlers/index.d.cts +413 -0
- package/dist/handlers/index.d.ts +413 -0
- package/dist/handlers/index.js +1656 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/index.cjs +170 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +115 -0
- package/dist/index.d.ts +115 -0
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -0
- package/dist/payment-links/index.cjs +58 -0
- package/dist/payment-links/index.cjs.map +1 -0
- package/dist/payment-links/index.d.cts +60 -0
- package/dist/payment-links/index.d.ts +60 -0
- package/dist/payment-links/index.js +49 -0
- package/dist/payment-links/index.js.map +1 -0
- package/dist/ui/index.cjs +1236 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.cts +148 -0
- package/dist/ui/index.d.ts +148 -0
- package/dist/ui/index.js +1222 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,1236 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var fa = require('react-icons/fa');
|
|
5
|
+
var Script = require('next/script');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var Script__default = /*#__PURE__*/_interopDefault(Script);
|
|
10
|
+
|
|
11
|
+
// src/ui/CardVisual.tsx
|
|
12
|
+
|
|
13
|
+
// src/ui/bin-lookup.ts
|
|
14
|
+
var STORAGE_KEY = "binDatabase_v1";
|
|
15
|
+
var memoryCache = null;
|
|
16
|
+
var inflight = null;
|
|
17
|
+
function readFromStorage() {
|
|
18
|
+
if (typeof window === "undefined") return null;
|
|
19
|
+
try {
|
|
20
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
21
|
+
if (!raw) return null;
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
if (parsed && parsed.data && typeof parsed.data === "object") return parsed;
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function writeToStorage(db) {
|
|
29
|
+
if (typeof window === "undefined") return;
|
|
30
|
+
try {
|
|
31
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(db));
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function fetchDatabase(endpoint) {
|
|
36
|
+
if (typeof window === "undefined") return null;
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch(endpoint, { cache: "default" });
|
|
39
|
+
if (!res.ok) return null;
|
|
40
|
+
return await res.json();
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function getBinDatabase(endpoint = "/api/bins") {
|
|
46
|
+
if (memoryCache) return memoryCache;
|
|
47
|
+
const cached = readFromStorage();
|
|
48
|
+
if (cached) {
|
|
49
|
+
memoryCache = cached;
|
|
50
|
+
if (!inflight) {
|
|
51
|
+
inflight = fetchDatabase(endpoint).then((fresh) => {
|
|
52
|
+
if (fresh && fresh.version !== cached.version) {
|
|
53
|
+
memoryCache = fresh;
|
|
54
|
+
writeToStorage(fresh);
|
|
55
|
+
}
|
|
56
|
+
inflight = null;
|
|
57
|
+
return fresh;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return cached;
|
|
61
|
+
}
|
|
62
|
+
if (!inflight) {
|
|
63
|
+
inflight = fetchDatabase(endpoint).then((fresh) => {
|
|
64
|
+
if (fresh) {
|
|
65
|
+
memoryCache = fresh;
|
|
66
|
+
writeToStorage(fresh);
|
|
67
|
+
}
|
|
68
|
+
inflight = null;
|
|
69
|
+
return fresh;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return inflight;
|
|
73
|
+
}
|
|
74
|
+
function lookupBinSync(cardNumber, db) {
|
|
75
|
+
if (!db || !cardNumber) return null;
|
|
76
|
+
const digits = cardNumber.replace(/\D/g, "");
|
|
77
|
+
if (digits.length < 6) return null;
|
|
78
|
+
for (const len of [8, 7, 6]) {
|
|
79
|
+
const prefix = digits.slice(0, len);
|
|
80
|
+
const hit = db.data[prefix];
|
|
81
|
+
if (hit) return hit;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
async function lookupBin(cardNumber, endpoint = "/api/bins") {
|
|
86
|
+
const db = await getBinDatabase(endpoint);
|
|
87
|
+
return lookupBinSync(cardNumber, db);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/ui/bank-colors.ts
|
|
91
|
+
var BANK_PALETTES = {
|
|
92
|
+
// ── Bancos comerciales con 3 variantes ──
|
|
93
|
+
bancopichincha: {
|
|
94
|
+
credit: { primary: "#FFCB05", secondary: "#a87f00" },
|
|
95
|
+
debit: { primary: "#FFCB05", secondary: "#a87f00" },
|
|
96
|
+
prepaid: { primary: "#4A4A4A", secondary: "#1a1a1a" }
|
|
97
|
+
},
|
|
98
|
+
pichincha: {
|
|
99
|
+
credit: { primary: "#FFCB05", secondary: "#a87f00" },
|
|
100
|
+
debit: { primary: "#FFCB05", secondary: "#a87f00" },
|
|
101
|
+
prepaid: { primary: "#4A4A4A", secondary: "#1a1a1a" }
|
|
102
|
+
},
|
|
103
|
+
bancodelpacifico: {
|
|
104
|
+
credit: { primary: "#0A4664", secondary: "#031f30" },
|
|
105
|
+
debit: { primary: "#0082C8", secondary: "#003e63" },
|
|
106
|
+
prepaid: { primary: "#145A50", secondary: "#062b25" }
|
|
107
|
+
},
|
|
108
|
+
pacifico: {
|
|
109
|
+
credit: { primary: "#0A4664", secondary: "#031f30" },
|
|
110
|
+
debit: { primary: "#0082C8", secondary: "#003e63" },
|
|
111
|
+
prepaid: { primary: "#145A50", secondary: "#062b25" }
|
|
112
|
+
},
|
|
113
|
+
bancoguayaquil: {
|
|
114
|
+
credit: { primary: "#201858", secondary: "#0a081f" },
|
|
115
|
+
debit: { primary: "#101040", secondary: "#06061f" }
|
|
116
|
+
},
|
|
117
|
+
guayaquil: {
|
|
118
|
+
credit: { primary: "#201858", secondary: "#0a081f" },
|
|
119
|
+
debit: { primary: "#101040", secondary: "#06061f" }
|
|
120
|
+
},
|
|
121
|
+
bancointernacional: {
|
|
122
|
+
credit: { primary: "#F08820", secondary: "#8a460a" },
|
|
123
|
+
debit: { primary: "#F08820", secondary: "#8a460a" },
|
|
124
|
+
prepaid: { primary: "#F08820", secondary: "#8a460a" }
|
|
125
|
+
},
|
|
126
|
+
internacional: {
|
|
127
|
+
credit: { primary: "#F08820", secondary: "#8a460a" },
|
|
128
|
+
debit: { primary: "#F08820", secondary: "#8a460a" },
|
|
129
|
+
prepaid: { primary: "#F08820", secondary: "#8a460a" }
|
|
130
|
+
},
|
|
131
|
+
bancodelaproduccionsaprodubanco: {
|
|
132
|
+
credit: { primary: "#004828", secondary: "#001f10" },
|
|
133
|
+
debit: { primary: "#004828", secondary: "#001f10" },
|
|
134
|
+
prepaid: { primary: "#D0D028", secondary: "#7a7a14" }
|
|
135
|
+
},
|
|
136
|
+
produbanco: {
|
|
137
|
+
credit: { primary: "#004828", secondary: "#001f10" },
|
|
138
|
+
debit: { primary: "#004828", secondary: "#001f10" },
|
|
139
|
+
prepaid: { primary: "#D0D028", secondary: "#7a7a14" }
|
|
140
|
+
},
|
|
141
|
+
bancobolivarianoca: {
|
|
142
|
+
credit: { primary: "#B8B8B8", secondary: "#5e5e5e" },
|
|
143
|
+
debit: { primary: "#009B8A", secondary: "#004540" }
|
|
144
|
+
},
|
|
145
|
+
bolivariano: {
|
|
146
|
+
credit: { primary: "#B8B8B8", secondary: "#5e5e5e" },
|
|
147
|
+
debit: { primary: "#009B8A", secondary: "#004540" }
|
|
148
|
+
},
|
|
149
|
+
bancodiners: {
|
|
150
|
+
credit: { primary: "#1C1C1C", secondary: "#000000" },
|
|
151
|
+
debit: { primary: "#1C1C1C", secondary: "#000000" },
|
|
152
|
+
prepaid: { primary: "#1C1C1C", secondary: "#000000" }
|
|
153
|
+
},
|
|
154
|
+
diners: {
|
|
155
|
+
credit: { primary: "#1C1C1C", secondary: "#000000" },
|
|
156
|
+
debit: { primary: "#1C1C1C", secondary: "#000000" },
|
|
157
|
+
prepaid: { primary: "#1C1C1C", secondary: "#000000" }
|
|
158
|
+
},
|
|
159
|
+
cooperativadeahorroycreditojuventudecuatorianaprogresistaltdaecuador: {
|
|
160
|
+
credit: { primary: "#2C4A8C", secondary: "#101e3f" },
|
|
161
|
+
debit: { primary: "#1E3A7A", secondary: "#0a1838" },
|
|
162
|
+
prepaid: { primary: "#1E3A7A", secondary: "#0a1838" }
|
|
163
|
+
},
|
|
164
|
+
cooperativajep: {
|
|
165
|
+
credit: { primary: "#2C4A8C", secondary: "#101e3f" },
|
|
166
|
+
debit: { primary: "#1E3A7A", secondary: "#0a1838" },
|
|
167
|
+
prepaid: { primary: "#1E3A7A", secondary: "#0a1838" }
|
|
168
|
+
},
|
|
169
|
+
jep: {
|
|
170
|
+
credit: { primary: "#2C4A8C", secondary: "#101e3f" },
|
|
171
|
+
debit: { primary: "#1E3A7A", secondary: "#0a1838" },
|
|
172
|
+
prepaid: { primary: "#1E3A7A", secondary: "#0a1838" }
|
|
173
|
+
},
|
|
174
|
+
cooperativadeahorroycreditopolicianacional: {
|
|
175
|
+
credit: { primary: "#003087", secondary: "#001138" },
|
|
176
|
+
debit: { primary: "#003087", secondary: "#001138" },
|
|
177
|
+
prepaid: { primary: "#003087", secondary: "#001138" }
|
|
178
|
+
},
|
|
179
|
+
cooperativapolicianacional: {
|
|
180
|
+
credit: { primary: "#003087", secondary: "#001138" },
|
|
181
|
+
debit: { primary: "#003087", secondary: "#001138" }
|
|
182
|
+
},
|
|
183
|
+
// ── Bancos con 2 variantes ──
|
|
184
|
+
bancodelaustro: {
|
|
185
|
+
credit: { primary: "#C0161A", secondary: "#5e0809" },
|
|
186
|
+
debit: { primary: "#C0161A", secondary: "#5e0809" }
|
|
187
|
+
},
|
|
188
|
+
austro: {
|
|
189
|
+
credit: { primary: "#C0161A", secondary: "#5e0809" },
|
|
190
|
+
debit: { primary: "#C0161A", secondary: "#5e0809" }
|
|
191
|
+
},
|
|
192
|
+
bancosolidario: {
|
|
193
|
+
credit: { primary: "#E07820", secondary: "#7a3e0a" },
|
|
194
|
+
debit: { primary: "#78B830", secondary: "#3f6515" }
|
|
195
|
+
},
|
|
196
|
+
solidario: {
|
|
197
|
+
credit: { primary: "#E07820", secondary: "#7a3e0a" },
|
|
198
|
+
debit: { primary: "#78B830", secondary: "#3f6515" }
|
|
199
|
+
},
|
|
200
|
+
bancoamazonassa: {
|
|
201
|
+
credit: { primary: "#1A1A1A", secondary: "#000000" },
|
|
202
|
+
debit: { primary: "#1A1A1A", secondary: "#000000" }
|
|
203
|
+
},
|
|
204
|
+
amazonas: {
|
|
205
|
+
credit: { primary: "#1A1A1A", secondary: "#000000" },
|
|
206
|
+
debit: { primary: "#1A1A1A", secondary: "#000000" }
|
|
207
|
+
},
|
|
208
|
+
bancoprocredit: {
|
|
209
|
+
credit: { primary: "#3A7830", secondary: "#1a3614" },
|
|
210
|
+
debit: { primary: "#3A7830", secondary: "#1a3614" }
|
|
211
|
+
},
|
|
212
|
+
bancoprocreditecuadorsa: {
|
|
213
|
+
credit: { primary: "#3A7830", secondary: "#1a3614" },
|
|
214
|
+
debit: { primary: "#3A7830", secondary: "#1a3614" }
|
|
215
|
+
},
|
|
216
|
+
procredit: {
|
|
217
|
+
credit: { primary: "#3A7830", secondary: "#1a3614" },
|
|
218
|
+
debit: { primary: "#3A7830", secondary: "#1a3614" }
|
|
219
|
+
},
|
|
220
|
+
bancodelojasa: {
|
|
221
|
+
credit: { primary: "#1E1E1E", secondary: "#000000" },
|
|
222
|
+
debit: { primary: "#1E1E1E", secondary: "#000000" }
|
|
223
|
+
},
|
|
224
|
+
loja: {
|
|
225
|
+
credit: { primary: "#1E1E1E", secondary: "#000000" },
|
|
226
|
+
debit: { primary: "#1E1E1E", secondary: "#000000" }
|
|
227
|
+
},
|
|
228
|
+
bancodemachalasa: {
|
|
229
|
+
credit: { primary: "#1B5E38", secondary: "#0a2c1a" },
|
|
230
|
+
debit: { primary: "#1B5E38", secondary: "#0a2c1a" }
|
|
231
|
+
},
|
|
232
|
+
machala: {
|
|
233
|
+
credit: { primary: "#1B5E38", secondary: "#0a2c1a" },
|
|
234
|
+
debit: { primary: "#1B5E38", secondary: "#0a2c1a" }
|
|
235
|
+
},
|
|
236
|
+
bancocomercialdemanabisa: {
|
|
237
|
+
credit: { primary: "#004F9F", secondary: "#001f44" },
|
|
238
|
+
debit: { primary: "#004F9F", secondary: "#001f44" }
|
|
239
|
+
},
|
|
240
|
+
bancogeneralrumi\u00F1ahuisa: {
|
|
241
|
+
credit: { primary: "#E03020", secondary: "#7a1610" },
|
|
242
|
+
debit: { primary: "#101010", secondary: "#000000" }
|
|
243
|
+
},
|
|
244
|
+
bancogeneralruminahuisa: {
|
|
245
|
+
credit: { primary: "#E03020", secondary: "#7a1610" },
|
|
246
|
+
debit: { primary: "#101010", secondary: "#000000" }
|
|
247
|
+
},
|
|
248
|
+
rumi\u00F1ahui: {
|
|
249
|
+
credit: { primary: "#E03020", secondary: "#7a1610" },
|
|
250
|
+
debit: { primary: "#101010", secondary: "#000000" }
|
|
251
|
+
},
|
|
252
|
+
ruminahui: {
|
|
253
|
+
credit: { primary: "#E03020", secondary: "#7a1610" },
|
|
254
|
+
debit: { primary: "#101010", secondary: "#000000" }
|
|
255
|
+
},
|
|
256
|
+
// ── Bancos con 1 variante ──
|
|
257
|
+
banecuador: {
|
|
258
|
+
debit: { primary: "#2CC4B0", secondary: "#0e5e54" }
|
|
259
|
+
},
|
|
260
|
+
bancoatlantida: {
|
|
261
|
+
debit: { primary: "#003087", secondary: "#001138" }
|
|
262
|
+
},
|
|
263
|
+
atlantida: {
|
|
264
|
+
debit: { primary: "#003087", secondary: "#001138" }
|
|
265
|
+
},
|
|
266
|
+
bancoatlantidadeecuador: {
|
|
267
|
+
debit: { primary: "#003087", secondary: "#001138" }
|
|
268
|
+
},
|
|
269
|
+
bancodellitoral: {
|
|
270
|
+
debit: { primary: "#0070B8", secondary: "#003056" }
|
|
271
|
+
},
|
|
272
|
+
bancocodesarrollo: {
|
|
273
|
+
debit: { primary: "#006837", secondary: "#002e18" }
|
|
274
|
+
},
|
|
275
|
+
codesarrollo: {
|
|
276
|
+
debit: { primary: "#006837", secondary: "#002e18" }
|
|
277
|
+
},
|
|
278
|
+
bancovisionfund: {
|
|
279
|
+
debit: { primary: "#FF6600", secondary: "#7a2f00" }
|
|
280
|
+
},
|
|
281
|
+
// ── Mutualistas ──
|
|
282
|
+
mutualistapichincha: {
|
|
283
|
+
debit: { primary: "#FFCB05", secondary: "#a87f00" }
|
|
284
|
+
},
|
|
285
|
+
asociacionmutualistadeahorroycreditoparalaviviendapichincha: {
|
|
286
|
+
debit: { primary: "#FFCB05", secondary: "#a87f00" }
|
|
287
|
+
},
|
|
288
|
+
asociacionmutualistadeahorroycreditoparalaviviendaazuay: {
|
|
289
|
+
debit: { primary: "#0057A8", secondary: "#00264a" }
|
|
290
|
+
},
|
|
291
|
+
mutualistaazuay: {
|
|
292
|
+
debit: { primary: "#0057A8", secondary: "#00264a" }
|
|
293
|
+
},
|
|
294
|
+
// ── Cooperativas grandes ──
|
|
295
|
+
cooperativa29deoctubre: {
|
|
296
|
+
credit: { primary: "#B8001C", secondary: "#5e000e" },
|
|
297
|
+
debit: { primary: "#B8001C", secondary: "#5e000e" }
|
|
298
|
+
},
|
|
299
|
+
coop29deoctubre: {
|
|
300
|
+
credit: { primary: "#B8001C", secondary: "#5e000e" },
|
|
301
|
+
debit: { primary: "#B8001C", secondary: "#5e000e" }
|
|
302
|
+
},
|
|
303
|
+
cooperativadeahorroycredito29deoctubreltda: {
|
|
304
|
+
credit: { primary: "#B8001C", secondary: "#5e000e" },
|
|
305
|
+
debit: { primary: "#B8001C", secondary: "#5e000e" }
|
|
306
|
+
},
|
|
307
|
+
cooperativa23dejuliofinancoop: {
|
|
308
|
+
credit: { primary: "#005B9A", secondary: "#00263e" },
|
|
309
|
+
debit: { primary: "#005B9A", secondary: "#00263e" }
|
|
310
|
+
},
|
|
311
|
+
cooperativadaquilemafinancoop: {
|
|
312
|
+
credit: { primary: "#C8001C", secondary: "#5e000e" },
|
|
313
|
+
debit: { primary: "#C8001C", secondary: "#5e000e" }
|
|
314
|
+
},
|
|
315
|
+
cooperativamegofinancoop: {
|
|
316
|
+
credit: { primary: "#005038", secondary: "#001f15" },
|
|
317
|
+
debit: { primary: "#005038", secondary: "#001f15" }
|
|
318
|
+
},
|
|
319
|
+
cooperativamushucrunafinancoop: {
|
|
320
|
+
credit: { primary: "#5CB800", secondary: "#2e5e00" },
|
|
321
|
+
debit: { primary: "#5CB800", secondary: "#2e5e00" }
|
|
322
|
+
},
|
|
323
|
+
cooperativacacpeco: {
|
|
324
|
+
credit: { primary: "#F0F0F0", secondary: "#888888" },
|
|
325
|
+
debit: { primary: "#F0F0F0", secondary: "#888888" }
|
|
326
|
+
},
|
|
327
|
+
cooperativadeahorroycreditocacspmec: {
|
|
328
|
+
credit: { primary: "#003087", secondary: "#001138" },
|
|
329
|
+
debit: { primary: "#003087", secondary: "#001138" }
|
|
330
|
+
},
|
|
331
|
+
cacpmec: {
|
|
332
|
+
credit: { primary: "#003087", secondary: "#001138" },
|
|
333
|
+
debit: { primary: "#003087", secondary: "#001138" }
|
|
334
|
+
},
|
|
335
|
+
cooperativadeahorroycreditoandalucia: {
|
|
336
|
+
credit: { primary: "#006837", secondary: "#002e18" },
|
|
337
|
+
debit: { primary: "#006837", secondary: "#002e18" }
|
|
338
|
+
},
|
|
339
|
+
andalucia: {
|
|
340
|
+
credit: { primary: "#006837", secondary: "#002e18" },
|
|
341
|
+
debit: { primary: "#006837", secondary: "#002e18" }
|
|
342
|
+
},
|
|
343
|
+
cooperativadeahorroycreditochibuleo: {
|
|
344
|
+
credit: { primary: "#CC1020", secondary: "#5e0810" },
|
|
345
|
+
debit: { primary: "#CC1020", secondary: "#5e0810" }
|
|
346
|
+
},
|
|
347
|
+
chibuleo: {
|
|
348
|
+
credit: { primary: "#CC1020", secondary: "#5e0810" },
|
|
349
|
+
debit: { primary: "#CC1020", secondary: "#5e0810" }
|
|
350
|
+
},
|
|
351
|
+
cooperativadeahorroycreditocooprogresoltda: {
|
|
352
|
+
credit: { primary: "#005B9A", secondary: "#00263e" },
|
|
353
|
+
debit: { primary: "#005B9A", secondary: "#00263e" }
|
|
354
|
+
},
|
|
355
|
+
cooprogreso: {
|
|
356
|
+
credit: { primary: "#005B9A", secondary: "#00263e" },
|
|
357
|
+
debit: { primary: "#005B9A", secondary: "#00263e" }
|
|
358
|
+
},
|
|
359
|
+
cooperativadeahorroycreditodelapequenaempresadepastazaltda: {
|
|
360
|
+
credit: { primary: "#1E8449", secondary: "#0d4621" },
|
|
361
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
362
|
+
},
|
|
363
|
+
cooperativadeahorroycreditooscus: {
|
|
364
|
+
credit: { primary: "#C8C8C8", secondary: "#666666" },
|
|
365
|
+
debit: { primary: "#C8C8C8", secondary: "#666666" }
|
|
366
|
+
},
|
|
367
|
+
oscus: {
|
|
368
|
+
credit: { primary: "#C8C8C8", secondary: "#666666" },
|
|
369
|
+
debit: { primary: "#C8C8C8", secondary: "#666666" }
|
|
370
|
+
},
|
|
371
|
+
cooperativadeahorroycreditopablomunozvega: {
|
|
372
|
+
credit: { primary: "#006837", secondary: "#002e18" },
|
|
373
|
+
debit: { primary: "#006837", secondary: "#002e18" }
|
|
374
|
+
},
|
|
375
|
+
cooperativapablomunozvega: {
|
|
376
|
+
credit: { primary: "#006837", secondary: "#002e18" },
|
|
377
|
+
debit: { primary: "#006837", secondary: "#002e18" }
|
|
378
|
+
},
|
|
379
|
+
cooperativadeahorroycreditotulcan: {
|
|
380
|
+
credit: { primary: "#005B9A", secondary: "#00263e" },
|
|
381
|
+
debit: { primary: "#005B9A", secondary: "#00263e" }
|
|
382
|
+
},
|
|
383
|
+
tulcan: {
|
|
384
|
+
credit: { primary: "#005B9A", secondary: "#00263e" },
|
|
385
|
+
debit: { primary: "#005B9A", secondary: "#00263e" }
|
|
386
|
+
},
|
|
387
|
+
cooperativajardinazuayo: {
|
|
388
|
+
credit: { primary: "#1A3A6A", secondary: "#0a182e" },
|
|
389
|
+
debit: { primary: "#1A3A6A", secondary: "#0a182e" }
|
|
390
|
+
},
|
|
391
|
+
jardinazuayo: {
|
|
392
|
+
credit: { primary: "#1A3A6A", secondary: "#0a182e" },
|
|
393
|
+
debit: { primary: "#1A3A6A", secondary: "#0a182e" }
|
|
394
|
+
},
|
|
395
|
+
cooperativadeahorroycreditoalianzadelvalleltda: {
|
|
396
|
+
credit: { primary: "#004F9F", secondary: "#001f44" },
|
|
397
|
+
debit: { primary: "#004F9F", secondary: "#001f44" }
|
|
398
|
+
},
|
|
399
|
+
alianzadelvalle: {
|
|
400
|
+
credit: { primary: "#004F9F", secondary: "#001f44" },
|
|
401
|
+
debit: { primary: "#004F9F", secondary: "#001f44" }
|
|
402
|
+
},
|
|
403
|
+
cooperativaatuntaquifinancoop: {
|
|
404
|
+
credit: { primary: "#C8001C", secondary: "#5e000e" },
|
|
405
|
+
debit: { primary: "#C8001C", secondary: "#5e000e" }
|
|
406
|
+
},
|
|
407
|
+
cooperativasagrariofinancoop: {
|
|
408
|
+
credit: { primary: "#005FAA", secondary: "#002850" },
|
|
409
|
+
debit: { primary: "#005FAA", secondary: "#002850" }
|
|
410
|
+
},
|
|
411
|
+
financoop: {
|
|
412
|
+
debit: { primary: "#005FAA", secondary: "#002850" }
|
|
413
|
+
},
|
|
414
|
+
// ── Cooperativas pequeñas (verde genérico cooperativista) ──
|
|
415
|
+
cooperativa15deabril: {
|
|
416
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
417
|
+
},
|
|
418
|
+
cooperativacacpepastaza: {
|
|
419
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
420
|
+
},
|
|
421
|
+
cooperativacrea: {
|
|
422
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
423
|
+
},
|
|
424
|
+
cooperativadeahorroycreditocrea: {
|
|
425
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
426
|
+
},
|
|
427
|
+
crea: {
|
|
428
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
429
|
+
},
|
|
430
|
+
cooperativadeahorroycreditodelapequenaempresabiblianltda: {
|
|
431
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
432
|
+
},
|
|
433
|
+
biblian: {
|
|
434
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
435
|
+
},
|
|
436
|
+
cooperativaeducadoreschimborazo: {
|
|
437
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
438
|
+
},
|
|
439
|
+
cooperativaerco: {
|
|
440
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
441
|
+
},
|
|
442
|
+
cooperativagualaquiza: {
|
|
443
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
444
|
+
},
|
|
445
|
+
cooperativaimbaburapakfinancoop: {
|
|
446
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
447
|
+
},
|
|
448
|
+
cooperativajulianlorentefinancoop: {
|
|
449
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
450
|
+
},
|
|
451
|
+
cooperativalamerced: {
|
|
452
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
453
|
+
},
|
|
454
|
+
cooperativasantarosafinancoop: {
|
|
455
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
456
|
+
},
|
|
457
|
+
cooperativaambatofinancoop: {
|
|
458
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
459
|
+
},
|
|
460
|
+
cooperativacredilfinancoop: {
|
|
461
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
462
|
+
},
|
|
463
|
+
cooperativapuellarofinancoop: {
|
|
464
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
465
|
+
},
|
|
466
|
+
cooperativasanmigueldelosbancos: {
|
|
467
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
468
|
+
},
|
|
469
|
+
coemisorcooperativalasnaves: {
|
|
470
|
+
debit: { primary: "#1E8449", secondary: "#0d4621" }
|
|
471
|
+
},
|
|
472
|
+
// ── Otros / procesadores ──
|
|
473
|
+
creditia: {
|
|
474
|
+
credit: { primary: "#666666", secondary: "#333333" },
|
|
475
|
+
debit: { primary: "#666666", secondary: "#333333" }
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
function normalize(name) {
|
|
479
|
+
return name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z]/g, "");
|
|
480
|
+
}
|
|
481
|
+
function lookupBankPalette(bankName, cardType) {
|
|
482
|
+
if (!bankName) return null;
|
|
483
|
+
const norm = normalize(bankName);
|
|
484
|
+
if (!norm) return null;
|
|
485
|
+
let bankByType = BANK_PALETTES[norm];
|
|
486
|
+
if (!bankByType) {
|
|
487
|
+
for (const key of Object.keys(BANK_PALETTES)) {
|
|
488
|
+
if (norm.includes(key) && key.length >= 6) {
|
|
489
|
+
bankByType = BANK_PALETTES[key];
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (!bankByType) return null;
|
|
495
|
+
if (cardType && bankByType[cardType]) return bankByType[cardType];
|
|
496
|
+
if (bankByType.credit) return bankByType.credit;
|
|
497
|
+
if (bankByType.debit) return bankByType.debit;
|
|
498
|
+
if (bankByType.prepaid) return bankByType.prepaid;
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/ui/types.ts
|
|
503
|
+
var CARD_BRAND_NAMES = {
|
|
504
|
+
vi: "Visa",
|
|
505
|
+
mc: "Mastercard",
|
|
506
|
+
ax: "American Express",
|
|
507
|
+
di: "Diners Club",
|
|
508
|
+
dc: "Discover",
|
|
509
|
+
up: "UnionPay",
|
|
510
|
+
cs: "Credisensa",
|
|
511
|
+
unknown: "Tarjeta"
|
|
512
|
+
};
|
|
513
|
+
var CARD_TYPE_NAMES = {
|
|
514
|
+
credit: "Cr\xE9dito",
|
|
515
|
+
debit: "D\xE9bito",
|
|
516
|
+
prepaid: "Prepago",
|
|
517
|
+
unknown: ""
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// src/ui/CardVisual.tsx
|
|
521
|
+
var BRAND_GRADIENT = {
|
|
522
|
+
vi: { from: "#1a1f71", to: "#0d1240" },
|
|
523
|
+
mc: { from: "#cc4d2e", to: "#561a0e" },
|
|
524
|
+
ax: { from: "#016fcf", to: "#003f7a" },
|
|
525
|
+
di: { from: "#2c3e50", to: "#11161e" },
|
|
526
|
+
dc: { from: "#ff6f00", to: "#a83a00" },
|
|
527
|
+
default: { from: "#3a4632", to: "#1a2014" }
|
|
528
|
+
};
|
|
529
|
+
function BrandMark({ brand }) {
|
|
530
|
+
const text = CARD_BRAND_NAMES[brand] || "CARD";
|
|
531
|
+
if (brand === "mc") {
|
|
532
|
+
return /* @__PURE__ */ React.createElement("div", { className: "flex items-center -space-x-2" }, /* @__PURE__ */ React.createElement("span", { className: "w-4 h-4 rounded-full bg-[#eb001b]" }), /* @__PURE__ */ React.createElement("span", { className: "w-4 h-4 rounded-full bg-[#f79e1b]/90 mix-blend-screen" }));
|
|
533
|
+
}
|
|
534
|
+
return /* @__PURE__ */ React.createElement("span", { className: "text-white text-[11px] font-bold tracking-wider uppercase italic" }, text);
|
|
535
|
+
}
|
|
536
|
+
function CardVisual({
|
|
537
|
+
bin,
|
|
538
|
+
brand,
|
|
539
|
+
last4,
|
|
540
|
+
holderName,
|
|
541
|
+
expiryMonth,
|
|
542
|
+
expiryYear,
|
|
543
|
+
variant = "compact",
|
|
544
|
+
binInfo: preResolvedBinInfo,
|
|
545
|
+
binDatabaseEndpoint = "/api/bins"
|
|
546
|
+
}) {
|
|
547
|
+
const [binInfo, setBinInfo] = react.useState(
|
|
548
|
+
preResolvedBinInfo ?? null
|
|
549
|
+
);
|
|
550
|
+
react.useEffect(() => {
|
|
551
|
+
if (preResolvedBinInfo || binDatabaseEndpoint === null || !bin) return;
|
|
552
|
+
let cancelled = false;
|
|
553
|
+
getBinDatabase(binDatabaseEndpoint).then((db) => {
|
|
554
|
+
if (cancelled || !db) return;
|
|
555
|
+
const info = lookupBinSync(bin, db);
|
|
556
|
+
if (info) setBinInfo(info);
|
|
557
|
+
});
|
|
558
|
+
return () => {
|
|
559
|
+
cancelled = true;
|
|
560
|
+
};
|
|
561
|
+
}, [bin, binDatabaseEndpoint, preResolvedBinInfo]);
|
|
562
|
+
const bankName = binInfo?.bank ?? "";
|
|
563
|
+
const bankPalette = lookupBankPalette(bankName, binInfo?.type);
|
|
564
|
+
const brandFallback = BRAND_GRADIENT[brand] ?? BRAND_GRADIENT.default;
|
|
565
|
+
const gradientFrom = bankPalette?.primary ?? brandFallback.from;
|
|
566
|
+
const gradientTo = bankPalette?.secondary ?? brandFallback.to;
|
|
567
|
+
const gradientStyle = {
|
|
568
|
+
background: `linear-gradient(135deg, ${gradientFrom} 0%, ${gradientTo} 100%)`
|
|
569
|
+
};
|
|
570
|
+
const cardType = binInfo?.type ? CARD_TYPE_NAMES[binInfo.type] || "" : "";
|
|
571
|
+
const expiryShort = `${expiryMonth.padStart(2, "0")}/${String(expiryYear).slice(-2)}`;
|
|
572
|
+
if (variant === "compact") {
|
|
573
|
+
return /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 min-w-0" }, /* @__PURE__ */ React.createElement(
|
|
574
|
+
"div",
|
|
575
|
+
{
|
|
576
|
+
style: gradientStyle,
|
|
577
|
+
className: "shrink-0 relative w-22 h-14 rounded-md shadow-md overflow-hidden"
|
|
578
|
+
},
|
|
579
|
+
/* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-linear-to-br from-white/10 via-transparent to-black/20 pointer-events-none" }),
|
|
580
|
+
/* @__PURE__ */ React.createElement("div", { className: "absolute top-2 left-2 w-3.5 h-2.5 rounded-sm bg-linear-to-br from-yellow-200/80 to-yellow-600/80 border border-yellow-700/40" }),
|
|
581
|
+
/* @__PURE__ */ React.createElement("div", { className: "absolute top-1.5 right-2" }, /* @__PURE__ */ React.createElement(BrandMark, { brand })),
|
|
582
|
+
/* @__PURE__ */ React.createElement("div", { className: "absolute bottom-1.5 left-2 right-2 flex justify-between items-end" }, /* @__PURE__ */ React.createElement("span", { className: "text-white/95 text-[11px] font-mono tracking-[0.05em] font-semibold drop-shadow" }, "\xB7\xB7\xB7\xB7", last4), /* @__PURE__ */ React.createElement("span", { className: "text-white/75 text-[8px] font-mono" }, expiryShort))
|
|
583
|
+
), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-[var(--color-text-main)] truncate" }, bankName || CARD_BRAND_NAMES[brand] || "Tarjeta"), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-[var(--color-text-main)]/50 truncate" }, cardType ? `${cardType} \xB7 ` : "", holderName || "\u2014")));
|
|
584
|
+
}
|
|
585
|
+
return /* @__PURE__ */ React.createElement(
|
|
586
|
+
"div",
|
|
587
|
+
{
|
|
588
|
+
style: gradientStyle,
|
|
589
|
+
className: "relative overflow-hidden rounded-2xl shadow-xl p-5 aspect-[1.586/1] w-full max-w-sm"
|
|
590
|
+
},
|
|
591
|
+
/* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-linear-to-br from-white/10 via-transparent to-black/30 pointer-events-none" }),
|
|
592
|
+
/* @__PURE__ */ React.createElement("div", { className: "relative flex justify-between items-start" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-medium tracking-[0.2em] uppercase text-white/50" }, cardType || "Tarjeta"), bankName && /* @__PURE__ */ React.createElement("p", { className: "text-xs text-white/85 mt-0.5 font-medium" }, bankName)), /* @__PURE__ */ React.createElement(BrandMark, { brand })),
|
|
593
|
+
/* @__PURE__ */ React.createElement("div", { className: "relative mt-6 w-9 h-7 rounded-md bg-linear-to-br from-yellow-200 to-yellow-600 border border-yellow-700/40 shadow-inner" }),
|
|
594
|
+
/* @__PURE__ */ React.createElement("div", { className: "relative mt-4" }, /* @__PURE__ */ React.createElement("p", { className: "font-mono text-lg tracking-wider text-white drop-shadow" }, "\xB7\xB7\xB7\xB7 \xB7\xB7\xB7\xB7 \xB7\xB7\xB7\xB7 ", last4)),
|
|
595
|
+
/* @__PURE__ */ React.createElement("div", { className: "relative mt-3 flex justify-between items-end" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("p", { className: "text-[9px] uppercase tracking-wider text-white/50" }, "Titular"), /* @__PURE__ */ React.createElement("p", { className: "text-white text-sm font-medium truncate uppercase" }, holderName || "\u2014")), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("p", { className: "text-[9px] uppercase tracking-wider text-white/50" }, "Expira"), /* @__PURE__ */ React.createElement("p", { className: "text-white text-sm font-mono font-medium" }, expiryShort)))
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
function SavedCards({
|
|
599
|
+
onSelectCard,
|
|
600
|
+
selectedToken,
|
|
601
|
+
onAddNewCard,
|
|
602
|
+
binDatabaseEndpoint
|
|
603
|
+
}) {
|
|
604
|
+
const [cards, setCards] = react.useState([]);
|
|
605
|
+
const [loading, setLoading] = react.useState(true);
|
|
606
|
+
const [deleting, setDeleting] = react.useState(null);
|
|
607
|
+
const [cvcValues, setCvcValues] = react.useState({});
|
|
608
|
+
const [verifyingToken, setVerifyingToken] = react.useState(null);
|
|
609
|
+
const [verifyValue, setVerifyValue] = react.useState("");
|
|
610
|
+
const [verifyError, setVerifyError] = react.useState(null);
|
|
611
|
+
const [verifySubmitting, setVerifySubmitting] = react.useState(false);
|
|
612
|
+
react.useEffect(() => {
|
|
613
|
+
loadCards();
|
|
614
|
+
}, []);
|
|
615
|
+
async function loadCards() {
|
|
616
|
+
setLoading(true);
|
|
617
|
+
try {
|
|
618
|
+
const res = await fetch("/api/nuvei/cards");
|
|
619
|
+
if (res.ok) {
|
|
620
|
+
const data = await res.json();
|
|
621
|
+
const allCards = data.cards || [];
|
|
622
|
+
setCards(allCards);
|
|
623
|
+
if (!selectedToken) {
|
|
624
|
+
const firstValid = allCards.find((c) => c.status === "valid");
|
|
625
|
+
if (firstValid) onSelectCard(firstValid, "");
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
} catch (err) {
|
|
629
|
+
console.error("Error loading cards:", err);
|
|
630
|
+
} finally {
|
|
631
|
+
setLoading(false);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async function handleDelete(token) {
|
|
635
|
+
setDeleting(token);
|
|
636
|
+
try {
|
|
637
|
+
const res = await fetch("/api/nuvei/cards", {
|
|
638
|
+
method: "DELETE",
|
|
639
|
+
headers: { "Content-Type": "application/json" },
|
|
640
|
+
body: JSON.stringify({ token })
|
|
641
|
+
});
|
|
642
|
+
if (res.ok) {
|
|
643
|
+
setCards((prev) => prev.filter((c) => c.token !== token));
|
|
644
|
+
if (verifyingToken === token) {
|
|
645
|
+
setVerifyingToken(null);
|
|
646
|
+
setVerifyValue("");
|
|
647
|
+
setVerifyError(null);
|
|
648
|
+
}
|
|
649
|
+
if (selectedToken === token) {
|
|
650
|
+
const remaining = cards.filter(
|
|
651
|
+
(c) => c.token !== token && c.status === "valid"
|
|
652
|
+
);
|
|
653
|
+
if (remaining.length > 0) {
|
|
654
|
+
onSelectCard(remaining[0], "");
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
} catch (err) {
|
|
659
|
+
console.error("Error deleting card:", err);
|
|
660
|
+
} finally {
|
|
661
|
+
setDeleting(null);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function handleVerify(card) {
|
|
665
|
+
if (!verifyValue.trim()) return;
|
|
666
|
+
setVerifySubmitting(true);
|
|
667
|
+
setVerifyError(null);
|
|
668
|
+
try {
|
|
669
|
+
const res = await fetch("/api/nuvei/verify", {
|
|
670
|
+
method: "POST",
|
|
671
|
+
headers: { "Content-Type": "application/json" },
|
|
672
|
+
body: JSON.stringify({
|
|
673
|
+
cardToken: card.token,
|
|
674
|
+
transactionReference: card.transaction_reference,
|
|
675
|
+
value: verifyValue.trim()
|
|
676
|
+
})
|
|
677
|
+
});
|
|
678
|
+
const data = await res.json();
|
|
679
|
+
if (data.success) {
|
|
680
|
+
setCards(
|
|
681
|
+
(prev) => prev.map(
|
|
682
|
+
(c) => c.token === card.token ? { ...c, status: "valid" } : c
|
|
683
|
+
)
|
|
684
|
+
);
|
|
685
|
+
setVerifyingToken(null);
|
|
686
|
+
setVerifyValue("");
|
|
687
|
+
onSelectCard({ ...card, status: "valid" }, "");
|
|
688
|
+
} else {
|
|
689
|
+
setVerifyError(
|
|
690
|
+
data.error || "Monto incorrecto. Revisa tu estado de cuenta."
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
} catch {
|
|
694
|
+
setVerifyError("Error de conexi\xF3n. Intenta de nuevo.");
|
|
695
|
+
} finally {
|
|
696
|
+
setVerifySubmitting(false);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (loading) {
|
|
700
|
+
return /* @__PURE__ */ React.createElement("div", { className: "flex justify-center py-6" }, /* @__PURE__ */ React.createElement("div", { className: "w-12 h-12 rounded-full border-2 border-[var(--color-text-main)]/20 border-t-[var(--color-primary)] animate-spin" }));
|
|
701
|
+
}
|
|
702
|
+
const validCards = cards.filter((c) => c.status === "valid");
|
|
703
|
+
const reviewCards = cards.filter(
|
|
704
|
+
(c) => c.status === "review" || c.status === "pending"
|
|
705
|
+
);
|
|
706
|
+
return /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, validCards.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, validCards.map((card) => {
|
|
707
|
+
const isSelected = selectedToken === card.token;
|
|
708
|
+
const cvc = cvcValues[card.token] || "";
|
|
709
|
+
const maxCvc = card.type === "ax" ? 4 : 3;
|
|
710
|
+
return /* @__PURE__ */ React.createElement(
|
|
711
|
+
"div",
|
|
712
|
+
{
|
|
713
|
+
key: card.token,
|
|
714
|
+
onClick: () => {
|
|
715
|
+
onSelectCard(card, cvc);
|
|
716
|
+
if (!isSelected) {
|
|
717
|
+
setCvcValues((prev) => ({ ...prev, [card.token]: "" }));
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
className: `border rounded-lg cursor-pointer transition-all duration-200 ${isSelected ? "border-[var(--color-primary)] bg-[var(--color-primary)]/5 ring-1 ring-[var(--color-primary)]" : "border-[var(--color-border-default)] hover:border-[var(--color-border-strong)]"}`
|
|
721
|
+
},
|
|
722
|
+
/* @__PURE__ */ React.createElement("div", { className: "p-4 flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0 flex-1" }, /* @__PURE__ */ React.createElement(
|
|
723
|
+
CardVisual,
|
|
724
|
+
{
|
|
725
|
+
bin: card.bin,
|
|
726
|
+
brand: card.type,
|
|
727
|
+
last4: card.number,
|
|
728
|
+
holderName: card.holder_name,
|
|
729
|
+
expiryMonth: card.expiry_month,
|
|
730
|
+
expiryYear: card.expiry_year,
|
|
731
|
+
binDatabaseEndpoint
|
|
732
|
+
}
|
|
733
|
+
), isSelected && /* @__PURE__ */ React.createElement(fa.FaCheck, { className: "w-3.5 h-3.5 text-[var(--color-primary)] shrink-0" })), /* @__PURE__ */ React.createElement(
|
|
734
|
+
"button",
|
|
735
|
+
{
|
|
736
|
+
onClick: (e) => {
|
|
737
|
+
e.stopPropagation();
|
|
738
|
+
handleDelete(card.token);
|
|
739
|
+
},
|
|
740
|
+
disabled: deleting === card.token,
|
|
741
|
+
className: "text-[var(--color-text-main)]/30 hover:text-[var(--color-error)] p-1 disabled:opacity-50 transition-colors cursor-pointer shrink-0",
|
|
742
|
+
title: "Eliminar tarjeta"
|
|
743
|
+
},
|
|
744
|
+
/* @__PURE__ */ React.createElement(fa.FaTrash, { className: "w-3 h-3" })
|
|
745
|
+
)),
|
|
746
|
+
isSelected && /* @__PURE__ */ React.createElement("div", { className: "px-4 pb-4 pt-0 border-t border-[var(--color-primary)]/15" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 pt-3" }, /* @__PURE__ */ React.createElement(fa.FaLock, { className: "w-3 h-3 text-[var(--color-text-main)]/30 shrink-0" }), /* @__PURE__ */ React.createElement(
|
|
747
|
+
"label",
|
|
748
|
+
{
|
|
749
|
+
htmlFor: `cvc-${card.token}`,
|
|
750
|
+
className: "text-xs text-[var(--color-text-main)]/50 shrink-0"
|
|
751
|
+
},
|
|
752
|
+
"C\xF3digo de seguridad"
|
|
753
|
+
), /* @__PURE__ */ React.createElement(
|
|
754
|
+
"input",
|
|
755
|
+
{
|
|
756
|
+
id: `cvc-${card.token}`,
|
|
757
|
+
type: "text",
|
|
758
|
+
inputMode: "numeric",
|
|
759
|
+
maxLength: maxCvc,
|
|
760
|
+
autoComplete: "cc-csc",
|
|
761
|
+
placeholder: maxCvc === 4 ? "\xB7\xB7\xB7\xB7" : "\xB7\xB7\xB7",
|
|
762
|
+
value: cvc,
|
|
763
|
+
onClick: (e) => e.stopPropagation(),
|
|
764
|
+
onChange: (e) => {
|
|
765
|
+
const val = e.target.value.replace(/\D/g, "");
|
|
766
|
+
setCvcValues((prev) => ({
|
|
767
|
+
...prev,
|
|
768
|
+
[card.token]: val
|
|
769
|
+
}));
|
|
770
|
+
onSelectCard(card, val);
|
|
771
|
+
},
|
|
772
|
+
autoFocus: true,
|
|
773
|
+
className: "w-16 text-center font-mono text-sm tracking-[0.25em] border border-[var(--color-border-default)] rounded-lg py-2 bg-[var(--color-background)] text-[var(--color-text-main)] placeholder:text-[var(--color-text-main)]/20 focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)]/50 focus:border-[var(--color-primary)] transition-all"
|
|
774
|
+
}
|
|
775
|
+
)))
|
|
776
|
+
);
|
|
777
|
+
})), reviewCards.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, reviewCards.map((card) => {
|
|
778
|
+
const isExpanded = verifyingToken === card.token;
|
|
779
|
+
return /* @__PURE__ */ React.createElement(
|
|
780
|
+
"div",
|
|
781
|
+
{
|
|
782
|
+
key: card.token,
|
|
783
|
+
className: "border border-[var(--color-warning)]/30 bg-[var(--color-warning)]/5 rounded-lg overflow-hidden transition-all duration-200"
|
|
784
|
+
},
|
|
785
|
+
/* @__PURE__ */ React.createElement("div", { className: "p-4 flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0 flex-1" }, /* @__PURE__ */ React.createElement(
|
|
786
|
+
CardVisual,
|
|
787
|
+
{
|
|
788
|
+
bin: card.bin,
|
|
789
|
+
brand: card.type,
|
|
790
|
+
last4: card.number,
|
|
791
|
+
holderName: card.holder_name,
|
|
792
|
+
expiryMonth: card.expiry_month,
|
|
793
|
+
expiryYear: card.expiry_year,
|
|
794
|
+
binDatabaseEndpoint
|
|
795
|
+
}
|
|
796
|
+
), /* @__PURE__ */ React.createElement("span", { className: "text-[10px] font-medium uppercase tracking-wider bg-[var(--color-warning)]/15 text-[var(--color-warning)] px-1.5 py-0.5 rounded shrink-0 inline-flex items-center gap-1" }, /* @__PURE__ */ React.createElement(fa.FaExclamationTriangle, { className: "w-2.5 h-2.5" }), "Verificar")), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 shrink-0" }, !isExpanded && /* @__PURE__ */ React.createElement(
|
|
797
|
+
"button",
|
|
798
|
+
{
|
|
799
|
+
onClick: () => {
|
|
800
|
+
setVerifyingToken(card.token);
|
|
801
|
+
setVerifyValue("");
|
|
802
|
+
setVerifyError(null);
|
|
803
|
+
},
|
|
804
|
+
className: "text-xs font-medium text-[var(--color-primary)] hover:text-[var(--color-primary-hover)] underline underline-offset-2 transition-colors cursor-pointer"
|
|
805
|
+
},
|
|
806
|
+
"Verificar"
|
|
807
|
+
), /* @__PURE__ */ React.createElement(
|
|
808
|
+
"button",
|
|
809
|
+
{
|
|
810
|
+
onClick: () => handleDelete(card.token),
|
|
811
|
+
disabled: deleting === card.token,
|
|
812
|
+
className: "text-[var(--color-text-main)]/30 hover:text-[var(--color-error)] p-1 disabled:opacity-50 transition-colors cursor-pointer",
|
|
813
|
+
title: "Eliminar tarjeta"
|
|
814
|
+
},
|
|
815
|
+
/* @__PURE__ */ React.createElement(fa.FaTrash, { className: "w-3 h-3" })
|
|
816
|
+
))),
|
|
817
|
+
isExpanded && /* @__PURE__ */ React.createElement("div", { className: "px-4 pb-4 border-t border-[var(--color-warning)]/20" }, /* @__PURE__ */ React.createElement("div", { className: "pt-3 space-y-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-2.5" }, /* @__PURE__ */ React.createElement(fa.FaShieldAlt, { className: "w-3.5 h-3.5 text-[var(--color-primary)] mt-0.5 shrink-0" }), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-[var(--color-text-main)]/60 leading-relaxed" }, "Se realiz\xF3 un micro-cobro a tu tarjeta. Revisa tu estado de cuenta bancario e ingresa el monto exacto para verificarla.")), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "relative flex-1" }, /* @__PURE__ */ React.createElement("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-main)]/40 text-sm" }, "$"), /* @__PURE__ */ React.createElement(
|
|
818
|
+
"input",
|
|
819
|
+
{
|
|
820
|
+
type: "text",
|
|
821
|
+
inputMode: "decimal",
|
|
822
|
+
value: verifyValue,
|
|
823
|
+
onChange: (e) => {
|
|
824
|
+
const val = e.target.value.replace(
|
|
825
|
+
/[^0-9.]/g,
|
|
826
|
+
""
|
|
827
|
+
);
|
|
828
|
+
setVerifyValue(val);
|
|
829
|
+
},
|
|
830
|
+
placeholder: "0.00",
|
|
831
|
+
className: "w-full pl-7 pr-3 py-2.5 text-sm border border-[var(--color-border-default)] rounded-lg bg-[var(--color-background)] text-[var(--color-text-main)] placeholder:text-[var(--color-text-main)]/30 focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)]/50 focus:border-[var(--color-primary)] transition-all",
|
|
832
|
+
autoFocus: true,
|
|
833
|
+
onKeyDown: (e) => {
|
|
834
|
+
if (e.key === "Enter") handleVerify(card);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
)), /* @__PURE__ */ React.createElement(
|
|
838
|
+
"button",
|
|
839
|
+
{
|
|
840
|
+
onClick: () => handleVerify(card),
|
|
841
|
+
disabled: verifySubmitting || !verifyValue.trim(),
|
|
842
|
+
className: "px-4 py-2.5 bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-white text-sm font-medium rounded-lg transition-colors disabled:bg-[var(--color-surface-elevated)] disabled:text-[var(--color-text-main)]/30 cursor-pointer"
|
|
843
|
+
},
|
|
844
|
+
verifySubmitting ? /* @__PURE__ */ React.createElement("div", { className: "w-4 h-4 rounded-full border-2 border-white border-b-transparent animate-spin" }) : "Verificar"
|
|
845
|
+
)), verifyError && /* @__PURE__ */ React.createElement("p", { className: "text-xs text-[var(--color-error)]" }, verifyError), /* @__PURE__ */ React.createElement(
|
|
846
|
+
"button",
|
|
847
|
+
{
|
|
848
|
+
onClick: () => {
|
|
849
|
+
setVerifyingToken(null);
|
|
850
|
+
setVerifyValue("");
|
|
851
|
+
setVerifyError(null);
|
|
852
|
+
},
|
|
853
|
+
className: "text-xs text-[var(--color-text-main)]/40 hover:text-[var(--color-text-main)]/60 transition-colors cursor-pointer"
|
|
854
|
+
},
|
|
855
|
+
"Cancelar"
|
|
856
|
+
)))
|
|
857
|
+
);
|
|
858
|
+
})), /* @__PURE__ */ React.createElement(
|
|
859
|
+
"button",
|
|
860
|
+
{
|
|
861
|
+
onClick: onAddNewCard,
|
|
862
|
+
className: "w-full flex items-center justify-center gap-2 p-3 border-2 border-dashed border-[var(--color-border-default)] rounded-lg text-sm text-[var(--color-text-main)]/50 hover:border-[var(--color-primary)] hover:text-[var(--color-primary)] transition-colors cursor-pointer"
|
|
863
|
+
},
|
|
864
|
+
/* @__PURE__ */ React.createElement(fa.FaPlus, { className: "w-3 h-3" }),
|
|
865
|
+
cards.length > 0 ? "Usar otra tarjeta" : "Agregar tarjeta"
|
|
866
|
+
));
|
|
867
|
+
}
|
|
868
|
+
var SDK_URL = "https://cdn.paymentez.com/ccapi/sdk/payment_sdk_stable.min.js";
|
|
869
|
+
var CONTAINER_ID = "nuvei-tokenize-container";
|
|
870
|
+
var DEFAULT_EXCLUSIVE_TYPES = ["vi", "mc", "ax", "di"];
|
|
871
|
+
function readCssVar(name, fallback) {
|
|
872
|
+
if (typeof window === "undefined") return fallback;
|
|
873
|
+
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
874
|
+
return value || fallback;
|
|
875
|
+
}
|
|
876
|
+
function NuveiPaymentForm({
|
|
877
|
+
uid,
|
|
878
|
+
email,
|
|
879
|
+
onTokenSuccess,
|
|
880
|
+
disabled,
|
|
881
|
+
buttonLabel,
|
|
882
|
+
showSaveCardCheckbox = true,
|
|
883
|
+
processingLabel = "Verificando\u2026",
|
|
884
|
+
nuveiAppCode,
|
|
885
|
+
nuveiAppKey,
|
|
886
|
+
nuveiEnv,
|
|
887
|
+
defaultCountry = "ECU",
|
|
888
|
+
exclusiveCardTypes,
|
|
889
|
+
iconColor
|
|
890
|
+
}) {
|
|
891
|
+
const [sdkReady, setSdkReady] = react.useState(false);
|
|
892
|
+
const [sdkTimedOut, setSdkTimedOut] = react.useState(false);
|
|
893
|
+
const [isProcessing, setIsProcessing] = react.useState(false);
|
|
894
|
+
const [isDeletingDuplicate, setIsDeletingDuplicate] = react.useState(false);
|
|
895
|
+
const [error, setError] = react.useState(null);
|
|
896
|
+
const [saveCard, setSaveCard] = react.useState(showSaveCardCheckbox);
|
|
897
|
+
const sdkInitRef = react.useRef(false);
|
|
898
|
+
const pgSdkRef = react.useRef(null);
|
|
899
|
+
const saveCardRef = react.useRef(showSaveCardCheckbox);
|
|
900
|
+
react.useEffect(() => {
|
|
901
|
+
saveCardRef.current = saveCard;
|
|
902
|
+
}, [saveCard]);
|
|
903
|
+
react.useEffect(() => {
|
|
904
|
+
if (sdkReady) return;
|
|
905
|
+
const timer = setTimeout(() => {
|
|
906
|
+
if (!sdkReady) setSdkTimedOut(true);
|
|
907
|
+
}, 15e3);
|
|
908
|
+
return () => clearTimeout(timer);
|
|
909
|
+
}, [sdkReady]);
|
|
910
|
+
function buildError(rawMsg) {
|
|
911
|
+
const msg = rawMsg.toLowerCase();
|
|
912
|
+
if (msg.includes("already")) {
|
|
913
|
+
return {
|
|
914
|
+
variant: "duplicate",
|
|
915
|
+
title: "Tarjeta ya registrada",
|
|
916
|
+
message: "Esta tarjeta ya est\xE1 en tu cuenta. Selecci\xF3nala desde tus tarjetas guardadas."
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
if (msg.includes("blacklist") || msg.includes("black list")) {
|
|
920
|
+
return {
|
|
921
|
+
variant: "rejected",
|
|
922
|
+
title: "Tarjeta no aceptada",
|
|
923
|
+
message: "Esta tarjeta est\xE1 en una lista restringida y no puede ser utilizada. Contacta a tu banco o intenta con otra tarjeta."
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
if (msg.includes("fraud")) {
|
|
927
|
+
return {
|
|
928
|
+
variant: "rejected",
|
|
929
|
+
title: "Tarjeta rechazada por seguridad",
|
|
930
|
+
message: "El sistema de seguridad rechaz\xF3 esta tarjeta. Intenta con otra tarjeta."
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
if (msg.includes("reject") || msg.includes("response by mock") || msg.includes("denied") || msg.includes("declined")) {
|
|
934
|
+
return {
|
|
935
|
+
variant: "rejected",
|
|
936
|
+
title: "Tarjeta rechazada",
|
|
937
|
+
message: "El banco emisor rechaz\xF3 esta tarjeta. Verifica los datos o intenta con otra tarjeta."
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
if (msg.includes("expired")) {
|
|
941
|
+
return {
|
|
942
|
+
variant: "rejected",
|
|
943
|
+
title: "Tarjeta vencida",
|
|
944
|
+
message: "Esta tarjeta est\xE1 vencida. Actualiza tu m\xE9todo de pago o usa otra tarjeta."
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
if (msg.includes("invalid") || msg.includes("not valid")) {
|
|
948
|
+
return {
|
|
949
|
+
variant: "rejected",
|
|
950
|
+
title: "Tarjeta inv\xE1lida",
|
|
951
|
+
message: "Los datos de la tarjeta no son v\xE1lidos. Verifica el n\xFAmero, fecha y CVV."
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
return {
|
|
955
|
+
variant: "rejected",
|
|
956
|
+
title: "Tarjeta no aceptada",
|
|
957
|
+
message: "No se pudo registrar esta tarjeta. Verifica los datos o intenta con otra."
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
const responseCallback = react.useCallback(
|
|
961
|
+
(response) => {
|
|
962
|
+
setIsProcessing(false);
|
|
963
|
+
if (response.error) {
|
|
964
|
+
if (process.env.NODE_ENV !== "production") {
|
|
965
|
+
console.log(
|
|
966
|
+
"[NuveiPaymentForm] SDK error response:",
|
|
967
|
+
JSON.stringify(response.error)
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
const errorType = response.error?.type || "";
|
|
971
|
+
if (errorType.toLowerCase().includes("card already added")) {
|
|
972
|
+
const tokenMatch = errorType.match(/Card already added:\s*(\S+)/i);
|
|
973
|
+
setError({
|
|
974
|
+
variant: "duplicate",
|
|
975
|
+
title: "Tarjeta ya registrada",
|
|
976
|
+
message: "Esta tarjeta ya existe en tu cuenta pero no se puede usar. Puedes eliminarla y volver a agregarla.",
|
|
977
|
+
duplicateToken: tokenMatch?.[1]
|
|
978
|
+
});
|
|
979
|
+
} else {
|
|
980
|
+
setError({
|
|
981
|
+
variant: "rejected",
|
|
982
|
+
title: "Tarjeta no aceptada",
|
|
983
|
+
message: "No se pudo registrar esta tarjeta. Verifica los datos o intenta con otra."
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
if (!response.card) {
|
|
989
|
+
setError({
|
|
990
|
+
variant: "system",
|
|
991
|
+
title: "Respuesta inesperada",
|
|
992
|
+
message: "No se recibi\xF3 respuesta del procesador. Intenta de nuevo."
|
|
993
|
+
});
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
if (response.card.status === "valid" || response.card.status === "review") {
|
|
997
|
+
if (response.card.token) {
|
|
998
|
+
setError(null);
|
|
999
|
+
onTokenSuccess(
|
|
1000
|
+
response.card.token,
|
|
1001
|
+
{
|
|
1002
|
+
type: response.card.type,
|
|
1003
|
+
number: response.card.number,
|
|
1004
|
+
expiry_year: response.card.expiry_year,
|
|
1005
|
+
expiry_month: response.card.expiry_month
|
|
1006
|
+
},
|
|
1007
|
+
saveCardRef.current
|
|
1008
|
+
);
|
|
1009
|
+
} else {
|
|
1010
|
+
setError({
|
|
1011
|
+
variant: "system",
|
|
1012
|
+
title: "Token no recibido",
|
|
1013
|
+
message: "No se pudo registrar la tarjeta. Intenta de nuevo."
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
} else {
|
|
1017
|
+
setError(buildError(response.card.message || ""));
|
|
1018
|
+
}
|
|
1019
|
+
},
|
|
1020
|
+
[onTokenSuccess]
|
|
1021
|
+
);
|
|
1022
|
+
const notCompletedCallback = react.useCallback((message) => {
|
|
1023
|
+
setIsProcessing(false);
|
|
1024
|
+
setError({
|
|
1025
|
+
variant: "incomplete",
|
|
1026
|
+
title: "Datos incompletos",
|
|
1027
|
+
message: `Revisa los campos del formulario: ${message}`
|
|
1028
|
+
});
|
|
1029
|
+
}, []);
|
|
1030
|
+
const handlePay = () => {
|
|
1031
|
+
if (!pgSdkRef.current) return;
|
|
1032
|
+
setError(null);
|
|
1033
|
+
setIsProcessing(true);
|
|
1034
|
+
pgSdkRef.current.tokenize();
|
|
1035
|
+
};
|
|
1036
|
+
const handleScriptReady = react.useCallback(() => {
|
|
1037
|
+
try {
|
|
1038
|
+
const PG = new Function("return PaymentGateway")();
|
|
1039
|
+
window.PaymentGateway = PG;
|
|
1040
|
+
setSdkReady(true);
|
|
1041
|
+
} catch {
|
|
1042
|
+
setError({
|
|
1043
|
+
variant: "system",
|
|
1044
|
+
title: "Error de inicializaci\xF3n",
|
|
1045
|
+
message: "No se pudo inicializar el sistema de pagos. Recarga la p\xE1gina."
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
}, []);
|
|
1049
|
+
react.useEffect(() => {
|
|
1050
|
+
if (!sdkReady || sdkInitRef.current) return;
|
|
1051
|
+
sdkInitRef.current = true;
|
|
1052
|
+
const appCode = nuveiAppCode ?? process.env.NEXT_PUBLIC_NUVEI_CLIENT_APP_CODE;
|
|
1053
|
+
const appKey = nuveiAppKey ?? process.env.NEXT_PUBLIC_NUVEI_CLIENT_APP_KEY;
|
|
1054
|
+
if (!appCode || !appKey) {
|
|
1055
|
+
setError({
|
|
1056
|
+
variant: "system",
|
|
1057
|
+
title: "Configuraci\xF3n incompleta",
|
|
1058
|
+
message: "Credenciales de pago no configuradas. Contacta soporte."
|
|
1059
|
+
});
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
const env = nuveiEnv ?? (process.env.NEXT_PUBLIC_NUVEI_ENV === "prod" ? "prod" : "stg");
|
|
1063
|
+
const PGClass = window.PaymentGateway;
|
|
1064
|
+
if (!PGClass) {
|
|
1065
|
+
setError({
|
|
1066
|
+
variant: "system",
|
|
1067
|
+
title: "SDK no disponible",
|
|
1068
|
+
message: "El sistema de pagos no carg\xF3 correctamente. Recarga la p\xE1gina."
|
|
1069
|
+
});
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
const pgSdk = new PGClass(env, appCode, appKey);
|
|
1073
|
+
pgSdkRef.current = pgSdk;
|
|
1074
|
+
const resolvedIconColor = iconColor ?? readCssVar("--color-primary", "#3b82f6");
|
|
1075
|
+
const allowedTypes = exclusiveCardTypes ?? [...DEFAULT_EXCLUSIVE_TYPES];
|
|
1076
|
+
const tokenizeData = {
|
|
1077
|
+
locale: "es",
|
|
1078
|
+
user: {
|
|
1079
|
+
id: uid,
|
|
1080
|
+
email
|
|
1081
|
+
},
|
|
1082
|
+
configuration: {
|
|
1083
|
+
default_country: defaultCountry,
|
|
1084
|
+
icon_colour: resolvedIconColor,
|
|
1085
|
+
use_dropdowns: false,
|
|
1086
|
+
exclusive_types: allowedTypes,
|
|
1087
|
+
invalid_card_type_message: "Tipo de tarjeta no aceptada para esta operaci\xF3n."
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
pgSdk.generate_tokenize(
|
|
1091
|
+
tokenizeData,
|
|
1092
|
+
`#${CONTAINER_ID}`,
|
|
1093
|
+
responseCallback,
|
|
1094
|
+
notCompletedCallback
|
|
1095
|
+
);
|
|
1096
|
+
}, [
|
|
1097
|
+
sdkReady,
|
|
1098
|
+
uid,
|
|
1099
|
+
email,
|
|
1100
|
+
responseCallback,
|
|
1101
|
+
notCompletedCallback,
|
|
1102
|
+
nuveiAppCode,
|
|
1103
|
+
nuveiAppKey,
|
|
1104
|
+
nuveiEnv,
|
|
1105
|
+
defaultCountry,
|
|
1106
|
+
exclusiveCardTypes,
|
|
1107
|
+
iconColor
|
|
1108
|
+
]);
|
|
1109
|
+
return /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React.createElement(
|
|
1110
|
+
Script__default.default,
|
|
1111
|
+
{
|
|
1112
|
+
src: SDK_URL,
|
|
1113
|
+
strategy: "afterInteractive",
|
|
1114
|
+
onReady: handleScriptReady
|
|
1115
|
+
}
|
|
1116
|
+
), error && error.variant !== "incomplete" ? /* @__PURE__ */ React.createElement(
|
|
1117
|
+
"div",
|
|
1118
|
+
{
|
|
1119
|
+
className: `rounded-xl border p-5 text-sm ${error.variant === "duplicate" ? "bg-[var(--color-primary)]/5 border-[var(--color-primary)]/20" : "bg-[var(--color-error)]/5 border-[var(--color-error)]/20"}`
|
|
1120
|
+
},
|
|
1121
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "shrink-0 mt-0.5" }, error.variant === "duplicate" ? /* @__PURE__ */ React.createElement(fa.FaInfoCircle, { className: "w-4 h-4 text-[var(--color-primary)]" }) : /* @__PURE__ */ React.createElement(fa.FaTimesCircle, { className: "w-4 h-4 text-[var(--color-error)]" })), /* @__PURE__ */ React.createElement("div", { className: "grow space-y-1" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-[var(--color-text-main)]" }, error.title), /* @__PURE__ */ React.createElement("p", { className: "text-[var(--color-text-main)]/60 leading-relaxed" }, error.message))),
|
|
1122
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex gap-3 mt-4 pt-3 border-t border-[var(--color-border-subtle)]" }, error.variant === "duplicate" && error.duplicateToken ? /* @__PURE__ */ React.createElement(
|
|
1123
|
+
"button",
|
|
1124
|
+
{
|
|
1125
|
+
type: "button",
|
|
1126
|
+
disabled: isDeletingDuplicate,
|
|
1127
|
+
onClick: async () => {
|
|
1128
|
+
setIsDeletingDuplicate(true);
|
|
1129
|
+
try {
|
|
1130
|
+
const res = await fetch("/api/nuvei/cards", {
|
|
1131
|
+
method: "DELETE",
|
|
1132
|
+
headers: { "Content-Type": "application/json" },
|
|
1133
|
+
body: JSON.stringify({
|
|
1134
|
+
token: error.duplicateToken
|
|
1135
|
+
})
|
|
1136
|
+
});
|
|
1137
|
+
const data = await res.json().catch(() => ({}));
|
|
1138
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1139
|
+
console.log(
|
|
1140
|
+
"[NuveiPaymentForm] Delete response:",
|
|
1141
|
+
res.status,
|
|
1142
|
+
JSON.stringify(data)
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
if (res.ok && !data.error) {
|
|
1146
|
+
setError(null);
|
|
1147
|
+
sdkInitRef.current = false;
|
|
1148
|
+
setSdkReady(false);
|
|
1149
|
+
setTimeout(() => handleScriptReady(), 100);
|
|
1150
|
+
} else {
|
|
1151
|
+
setError({
|
|
1152
|
+
variant: "rejected",
|
|
1153
|
+
title: "No se pudo eliminar",
|
|
1154
|
+
message: "El procesador de pagos no permiti\xF3 eliminar esta tarjeta. Intenta con otra tarjeta o contacta a tu banco."
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
} catch {
|
|
1158
|
+
setError({
|
|
1159
|
+
variant: "system",
|
|
1160
|
+
title: "Error de conexi\xF3n",
|
|
1161
|
+
message: "No se pudo conectar al servidor. Intenta de nuevo."
|
|
1162
|
+
});
|
|
1163
|
+
} finally {
|
|
1164
|
+
setIsDeletingDuplicate(false);
|
|
1165
|
+
}
|
|
1166
|
+
},
|
|
1167
|
+
className: "flex-1 bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-white font-semibold py-2.5 px-4 rounded-lg transition-colors text-sm cursor-pointer inline-flex items-center justify-center gap-2 disabled:opacity-50"
|
|
1168
|
+
},
|
|
1169
|
+
isDeletingDuplicate ? "Eliminando..." : "Eliminar y reintentar"
|
|
1170
|
+
) : /* @__PURE__ */ React.createElement(
|
|
1171
|
+
"button",
|
|
1172
|
+
{
|
|
1173
|
+
type: "button",
|
|
1174
|
+
onClick: () => {
|
|
1175
|
+
setError(null);
|
|
1176
|
+
const container = document.getElementById(CONTAINER_ID);
|
|
1177
|
+
if (container) container.innerHTML = "";
|
|
1178
|
+
sdkInitRef.current = false;
|
|
1179
|
+
setSdkReady(false);
|
|
1180
|
+
setTimeout(() => handleScriptReady(), 100);
|
|
1181
|
+
},
|
|
1182
|
+
className: "flex-1 bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-white font-semibold py-2.5 px-4 rounded-lg transition-colors text-sm cursor-pointer inline-flex items-center justify-center gap-2"
|
|
1183
|
+
},
|
|
1184
|
+
/* @__PURE__ */ React.createElement(fa.FaRedo, { className: "w-3 h-3" }),
|
|
1185
|
+
"Intentar con otra tarjeta"
|
|
1186
|
+
))
|
|
1187
|
+
) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { id: CONTAINER_ID, className: "min-h-16" }), error && /* @__PURE__ */ React.createElement(
|
|
1188
|
+
"div",
|
|
1189
|
+
{
|
|
1190
|
+
className: `rounded-xl border p-4 text-sm ${error.variant === "incomplete" ? "bg-[var(--color-warning)]/5 border-[var(--color-warning)]/20" : "bg-[var(--color-surface-elevated)] border-[var(--color-border-subtle)]"}`
|
|
1191
|
+
},
|
|
1192
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex gap-3" }, /* @__PURE__ */ React.createElement(
|
|
1193
|
+
fa.FaExclamationTriangle,
|
|
1194
|
+
{
|
|
1195
|
+
className: `w-4 h-4 shrink-0 mt-0.5 ${error.variant === "incomplete" ? "text-[var(--color-warning)]" : "text-[var(--color-text-main)]/40"}`
|
|
1196
|
+
}
|
|
1197
|
+
), /* @__PURE__ */ React.createElement("div", { className: "grow" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-[var(--color-text-main)]" }, error.title), /* @__PURE__ */ React.createElement("p", { className: "text-[var(--color-text-main)]/60 leading-relaxed" }, error.message)))
|
|
1198
|
+
), sdkReady && /* @__PURE__ */ React.createElement(React.Fragment, null, showSaveCardCheckbox && /* @__PURE__ */ React.createElement("label", { className: "flex items-center gap-2.5 cursor-pointer select-none" }, /* @__PURE__ */ React.createElement(
|
|
1199
|
+
"input",
|
|
1200
|
+
{
|
|
1201
|
+
type: "checkbox",
|
|
1202
|
+
checked: saveCard,
|
|
1203
|
+
onChange: (e) => setSaveCard(e.target.checked),
|
|
1204
|
+
className: "w-4 h-4 rounded border-[var(--color-border-default)] text-[var(--color-primary)] focus:ring-[var(--color-primary)]/30 cursor-pointer accent-[var(--color-primary)]"
|
|
1205
|
+
}
|
|
1206
|
+
), /* @__PURE__ */ React.createElement("span", { className: "text-sm text-[var(--color-text-main)]/60" }, "Guardar tarjeta para futuras compras")), /* @__PURE__ */ React.createElement(
|
|
1207
|
+
"button",
|
|
1208
|
+
{
|
|
1209
|
+
type: "button",
|
|
1210
|
+
onClick: handlePay,
|
|
1211
|
+
disabled: isProcessing || disabled,
|
|
1212
|
+
className: "w-full bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-white font-semibold py-3 px-4 rounded-lg transition-all duration-200 disabled:bg-[var(--color-surface-elevated)] disabled:text-[var(--color-text-main)]/30 disabled:cursor-not-allowed flex items-center justify-center gap-2 active:scale-[0.97] cursor-pointer"
|
|
1213
|
+
},
|
|
1214
|
+
isProcessing ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "w-5 h-5 rounded-full border-2 border-white border-b-transparent animate-spin" }), processingLabel) : buttonLabel ?? (saveCard ? "Agregar Tarjeta" : "Continuar con pago")
|
|
1215
|
+
))), !sdkReady && /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-center py-4 gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "w-12 h-12 rounded-full border-2 border-[var(--color-text-main)]/20 border-t-[var(--color-primary)] animate-spin" }), sdkTimedOut && /* @__PURE__ */ React.createElement("div", { className: "text-center space-y-2" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs text-[var(--color-text-main)]/50" }, "El sistema de pagos est\xE1 tardando en cargar."), /* @__PURE__ */ React.createElement(
|
|
1216
|
+
"button",
|
|
1217
|
+
{
|
|
1218
|
+
type: "button",
|
|
1219
|
+
onClick: () => window.location.reload(),
|
|
1220
|
+
className: "text-xs font-semibold text-[var(--color-primary)] hover:text-[var(--color-primary-hover)] underline underline-offset-2 transition-colors"
|
|
1221
|
+
},
|
|
1222
|
+
"Recargar p\xE1gina"
|
|
1223
|
+
))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-center gap-2 text-[var(--color-text-main)]/30 text-xs" }, /* @__PURE__ */ React.createElement(fa.FaLock, { className: "w-3 h-3" }), /* @__PURE__ */ React.createElement("span", null, "Pago 100% seguro \xB7 Tokenizaci\xF3n PCI Compliant")));
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
exports.CARD_BRAND_NAMES = CARD_BRAND_NAMES;
|
|
1227
|
+
exports.CARD_TYPE_NAMES = CARD_TYPE_NAMES;
|
|
1228
|
+
exports.CardVisual = CardVisual;
|
|
1229
|
+
exports.NuveiPaymentForm = NuveiPaymentForm;
|
|
1230
|
+
exports.SavedCards = SavedCards;
|
|
1231
|
+
exports.getBinDatabase = getBinDatabase;
|
|
1232
|
+
exports.lookupBankPalette = lookupBankPalette;
|
|
1233
|
+
exports.lookupBin = lookupBin;
|
|
1234
|
+
exports.lookupBinSync = lookupBinSync;
|
|
1235
|
+
//# sourceMappingURL=index.cjs.map
|
|
1236
|
+
//# sourceMappingURL=index.cjs.map
|