@miden-npm/react 0.1.0 → 1.0.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/dist/index.cjs CHANGED
@@ -106,17 +106,25 @@ function usePaymentIntents() {
106
106
  }
107
107
 
108
108
  // src/buzapay-checkout/checkout-button.tsx
109
- var import_react6 = require("react");
109
+ var import_react7 = require("react");
110
110
 
111
111
  // src/utils/object.util.ts
112
112
  var checkObjectTruthy = (obj) => {
113
113
  if (obj == null || typeof obj !== "object") return false;
114
114
  return Object.values(obj).every(Boolean);
115
115
  };
116
+ var getQueryParams = (url) => {
117
+ const params = {};
118
+ const searchParams = new URL(url).searchParams;
119
+ searchParams.forEach((value, key) => {
120
+ params[key] = value;
121
+ });
122
+ return params;
123
+ };
116
124
 
117
125
  // src/utils/api.util.ts
118
126
  var getBaseUrl = (mode) => {
119
- return mode === "sandbox" ? "https://sandbox-api.midencards.io/payment-gateway-api" : "";
127
+ return mode === "sandbox" ? "https://sandbox-api.buzapay.com/payment-gateway-api" : "";
120
128
  };
121
129
 
122
130
  // src/utils/string.util.ts
@@ -168,6 +176,246 @@ var restrictToNumericKeys = (event) => {
168
176
  }
169
177
  };
170
178
 
179
+ // src/utils/error.util.ts
180
+ var validateRegex = (value, regex) => regex.test(value);
181
+ function parseRules(rules) {
182
+ if (!rules) return [];
183
+ return rules.split("|").map((chunk) => {
184
+ const [rule, val] = chunk.split(":");
185
+ return { rule, value: val };
186
+ });
187
+ }
188
+ function getValidationErrorMessage(rulesStr, inputValue, label) {
189
+ const errors = [];
190
+ const rules = parseRules(rulesStr);
191
+ const lowerLabel = (label || "This field").toLowerCase();
192
+ const v = inputValue ?? "";
193
+ const num = Number(v);
194
+ const hasNum = !Number.isNaN(num);
195
+ for (const r of rules) {
196
+ switch (r.rule) {
197
+ case "required":
198
+ if (!v) errors.push(`The ${lowerLabel} field is required`);
199
+ break;
200
+ case "alpha":
201
+ if (!validateRegex(v, /^[a-zA-Z]+$/))
202
+ errors.push(
203
+ `The ${lowerLabel} field must only contain alphabetic characters`
204
+ );
205
+ break;
206
+ case "at_least_one_uppercase":
207
+ if (!validateRegex(v, /.*[A-Z].*/))
208
+ errors.push(
209
+ `The ${lowerLabel} field must contain at least one uppercase character`
210
+ );
211
+ break;
212
+ case "at_least_one_lowercase":
213
+ if (!validateRegex(v, /.*[a-z].*/))
214
+ errors.push(
215
+ `The ${lowerLabel} field must contain at least one lowercase character`
216
+ );
217
+ break;
218
+ case "at_least_one_number":
219
+ if (!validateRegex(v, /.*\d.*/))
220
+ errors.push(
221
+ `The ${lowerLabel} field must contain at least one number`
222
+ );
223
+ break;
224
+ case "alpha_num":
225
+ if (!validateRegex(v, /^[a-z0-9]+$/i))
226
+ errors.push(
227
+ `The ${lowerLabel} field must only contain alphabetic characters and numbers`
228
+ );
229
+ break;
230
+ case "num_spaces":
231
+ if (!validateRegex(v, /^[0-9\s]+$/i))
232
+ errors.push(
233
+ `The ${lowerLabel} field must only contain space and numbers`
234
+ );
235
+ break;
236
+ case "alpha_spaces":
237
+ if (!validateRegex(v, /^[A-Za-z\s]*$/))
238
+ errors.push(
239
+ `The ${lowerLabel} field must only contain alphabetic characters and spaces`
240
+ );
241
+ break;
242
+ case "numeric":
243
+ if (!validateRegex(v, /^\d+$/))
244
+ errors.push(`The ${lowerLabel} field must only contain numbers`);
245
+ break;
246
+ case "no_special":
247
+ if (validateRegex(v, /[^a-zA-Z0-9\s]/))
248
+ errors.push(
249
+ `The ${lowerLabel} field must not contain special characters`
250
+ );
251
+ break;
252
+ case "at_least_one_special":
253
+ if (!validateRegex(v, /[^a-zA-Z0-9\s]/))
254
+ errors.push(
255
+ `The ${lowerLabel} field must contain at least one special character`
256
+ );
257
+ break;
258
+ case "email":
259
+ if (!validateRegex(v, /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/)) {
260
+ errors.push("Please enter a valid email address");
261
+ }
262
+ break;
263
+ case "is":
264
+ if (r.value !== v) errors.push(`Value entered must be ${r.value}`);
265
+ break;
266
+ case "is_not":
267
+ if (r.value === v) errors.push(`Value entered must not be ${r.value}`);
268
+ break;
269
+ case "char_length":
270
+ if (v.length !== Number(r.value))
271
+ errors.push(
272
+ `The ${lowerLabel} field must be ${r.value} characters long`
273
+ );
274
+ break;
275
+ case "min_char_length":
276
+ if (v.length < Number(r.value))
277
+ errors.push(
278
+ `The ${lowerLabel} field must not be less than ${r.value} characters long`
279
+ );
280
+ break;
281
+ case "max_char_length":
282
+ if (v.length > Number(r.value))
283
+ errors.push(
284
+ `The ${lowerLabel} field must not be greater than ${r.value} characters long`
285
+ );
286
+ break;
287
+ // Age rules treat the value as a number (strict numeric compare)
288
+ case "age":
289
+ if (!hasNum || num !== Number(r.value))
290
+ errors.push(`Age entered must be ${r.value}`);
291
+ break;
292
+ case "min_age":
293
+ if (!hasNum || num < Number(r.value))
294
+ errors.push(`Age entered must not be less than ${r.value}`);
295
+ break;
296
+ case "max_age":
297
+ if (!hasNum || num > Number(r.value))
298
+ errors.push(`Age entered must not be greater than ${r.value}`);
299
+ break;
300
+ // Numeric value rules (general numbers)
301
+ case "num_is":
302
+ if (!hasNum || num !== Number(r.value))
303
+ errors.push(`Number entered must be ${r.value}`);
304
+ break;
305
+ case "num_is_not":
306
+ if (hasNum && num === Number(r.value))
307
+ errors.push(`Number entered must not be ${r.value}`);
308
+ break;
309
+ case "min_number":
310
+ if (!hasNum || num < Number(r.value))
311
+ errors.push(`Number entered must not be less than ${r.value}`);
312
+ break;
313
+ case "max_number":
314
+ if (!hasNum || num > Number(r.value))
315
+ errors.push(`Number entered must not be greater than ${r.value}`);
316
+ break;
317
+ default:
318
+ break;
319
+ }
320
+ }
321
+ return errors.length > 0 ? errors[0] : null;
322
+ }
323
+ var validateGroup = (values, rulesMap, prettyLabels) => {
324
+ const out = {};
325
+ for (const [field, rules] of Object.entries(rulesMap)) {
326
+ if (!rules) continue;
327
+ const label = prettyLabels?.[field] ?? field;
328
+ const err = getValidationErrorMessage(rules, values[field] ?? "", label);
329
+ if (err) out[field] = err;
330
+ }
331
+ return out;
332
+ };
333
+
334
+ // src/utils/card-scheme.util.ts
335
+ var CardSchemes = /* @__PURE__ */ ((CardSchemes2) => {
336
+ CardSchemes2[CardSchemes2["Visa"] = 1] = "Visa";
337
+ CardSchemes2[CardSchemes2["MasterCard"] = 2] = "MasterCard";
338
+ CardSchemes2[CardSchemes2["Verve"] = 3] = "Verve";
339
+ CardSchemes2[CardSchemes2["MastercardAndVisa"] = 4] = "MastercardAndVisa";
340
+ CardSchemes2[CardSchemes2["AmericanExpress"] = 5] = "AmericanExpress";
341
+ CardSchemes2[CardSchemes2["Discover"] = 6] = "Discover";
342
+ CardSchemes2[CardSchemes2["JCB"] = 7] = "JCB";
343
+ CardSchemes2[CardSchemes2["DinersClub"] = 8] = "DinersClub";
344
+ CardSchemes2[CardSchemes2["Maestro"] = 9] = "Maestro";
345
+ CardSchemes2[CardSchemes2["UnionPay"] = 10] = "UnionPay";
346
+ CardSchemes2[CardSchemes2["UnionPay3DS"] = 11] = "UnionPay3DS";
347
+ CardSchemes2[CardSchemes2["UnionPayNon3DS"] = 12] = "UnionPayNon3DS";
348
+ CardSchemes2[CardSchemes2["UATP"] = 13] = "UATP";
349
+ CardSchemes2[CardSchemes2["PayPak"] = 14] = "PayPak";
350
+ CardSchemes2[CardSchemes2["Jaywan"] = 15] = "Jaywan";
351
+ CardSchemes2[CardSchemes2["Mada"] = 16] = "Mada";
352
+ CardSchemes2[CardSchemes2["MadaVisa"] = 17] = "MadaVisa";
353
+ CardSchemes2[CardSchemes2["MadaMastercard"] = 18] = "MadaMastercard";
354
+ CardSchemes2[CardSchemes2["Unknown"] = 19] = "Unknown";
355
+ return CardSchemes2;
356
+ })(CardSchemes || {});
357
+ var VISA_RE = /^4\d{12}(\d{3}){0,2}$/;
358
+ var MC_RE = /^(?:5[1-5]\d{14}|2(?:2(?:2[1-9]|[3-9]\d)|[3-6]\d{2}|7(?:[01]\d|20))\d{12})(?:\d{0,3})?$/;
359
+ var AMEX_RE = /^(34|37)\d{13}$/;
360
+ var DISCOVER_RE = /^(?:6011|65|64[4-9]|622(?:12[6-9]|1[3-9]\d|[2-8]\d{2}|9(?:0\d|1\d|2[0-5])))\d{12,15}$/;
361
+ var JCB_RE = /^(?:35(?:2[8-9]|[3-8]\d))\d{12,15}$/;
362
+ var DINERS_RE = /^(?:3(?:0[0-5]\d|095|6\d|[89]\d))\d{11,13}$/;
363
+ var MAESTRO_RE = /^(?:50|5[6-9]|6[0-9])\d{10,17}$/;
364
+ var UNIONPAY_RE = /^62\d{14,17}$/;
365
+ var UATP_RE = /^1\d{14}$/;
366
+ var VERVE_BIN6_RANGES = [
367
+ [506099, 506198],
368
+ [650002, 650027]
369
+ ];
370
+ var VERVE_BIN6_SINGLES = /* @__PURE__ */ new Set([507865, 507866]);
371
+ var PAYPAK_BIN6 = /* @__PURE__ */ new Set([220545, 220543]);
372
+ var JAYWAN_BIN7 = /* @__PURE__ */ new Set([6690109]);
373
+ var MADA_ONLY_BIN6 = /* @__PURE__ */ new Set([968209, 873646]);
374
+ var MADA_VISA_BIN6 = /* @__PURE__ */ new Set([422818, 486094]);
375
+ var MADA_MC_BIN6 = /* @__PURE__ */ new Set([529741, 543357]);
376
+ var UNIONPAY_3DS_BIN6 = /* @__PURE__ */ new Set([620108]);
377
+ var UNIONPAY_NON3DS_BIN6 = /* @__PURE__ */ new Set([621423]);
378
+ function cardTypeHandler(cardNumber) {
379
+ const scheme = detect(cardNumber);
380
+ return CardSchemes[scheme];
381
+ }
382
+ function detect(cardNumber) {
383
+ if (!cardNumber || !cardNumber.trim()) return 19 /* Unknown */;
384
+ const digits = cardNumber.replace(/\D/g, "");
385
+ if (digits.length < 12 || digits.length > 19) return 19 /* Unknown */;
386
+ if (digits.length >= 6) {
387
+ const bin6 = parseInt(digits.slice(0, 6), 10);
388
+ if (MADA_ONLY_BIN6.has(bin6) && digits.length >= 16 && digits.length <= 19)
389
+ return 16 /* Mada */;
390
+ if (MADA_VISA_BIN6.has(bin6) && VISA_RE.test(digits)) return 17 /* MadaVisa */;
391
+ if (MADA_MC_BIN6.has(bin6) && MC_RE.test(digits)) return 18 /* MadaMastercard */;
392
+ if (PAYPAK_BIN6.has(bin6) && digits.length === 16) return 14 /* PayPak */;
393
+ if (UNIONPAY_3DS_BIN6.has(bin6) && UNIONPAY_RE.test(digits)) return 11 /* UnionPay3DS */;
394
+ if (UNIONPAY_NON3DS_BIN6.has(bin6) && UNIONPAY_RE.test(digits))
395
+ return 12 /* UnionPayNon3DS */;
396
+ if (isVerve(bin6, digits.length)) return 3 /* Verve */;
397
+ }
398
+ if (digits.length >= 7) {
399
+ const bin7 = parseInt(digits.slice(0, 7), 10);
400
+ if (JAYWAN_BIN7.has(bin7) && digits.length === 16) return 15 /* Jaywan */;
401
+ }
402
+ if (UATP_RE.test(digits)) return 13 /* UATP */;
403
+ if (AMEX_RE.test(digits)) return 5 /* AmericanExpress */;
404
+ if (DISCOVER_RE.test(digits)) return 6 /* Discover */;
405
+ if (JCB_RE.test(digits)) return 7 /* JCB */;
406
+ if (DINERS_RE.test(digits)) return 8 /* DinersClub */;
407
+ if (MC_RE.test(digits)) return 2 /* MasterCard */;
408
+ if (VISA_RE.test(digits)) return 1 /* Visa */;
409
+ if (UNIONPAY_RE.test(digits)) return 10 /* UnionPay */;
410
+ if (MAESTRO_RE.test(digits)) return 9 /* Maestro */;
411
+ return 19 /* Unknown */;
412
+ }
413
+ function isVerve(bin6, length) {
414
+ if (length < 16 || length > 19) return false;
415
+ const inRange = VERVE_BIN6_RANGES.some(([start, end]) => bin6 >= start && bin6 <= end);
416
+ return inRange || VERVE_BIN6_SINGLES.has(bin6);
417
+ }
418
+
171
419
  // src/apis/checkout.api.ts
172
420
  async function createPaymentLink(paymentObject, environment, secretKey) {
173
421
  try {
@@ -181,7 +429,189 @@ async function createPaymentLink(paymentObject, environment, secretKey) {
181
429
  Accept: "application/json",
182
430
  merchantId: secretKey
183
431
  },
184
- body: JSON.stringify(paymentObject)
432
+ body: JSON.stringify({
433
+ ...paymentObject,
434
+ LinkName: `${Date.now()}-Link`
435
+ })
436
+ }
437
+ );
438
+ return await res.json();
439
+ } catch (error) {
440
+ }
441
+ }
442
+ async function authorizeCardPayment(environment, { merchantId, ...rest }) {
443
+ try {
444
+ const baseUrl = getBaseUrl(environment);
445
+ const res = await fetch(
446
+ `${baseUrl}/api/v1/checkout/authorize-card-3ds-payment-sdk`,
447
+ {
448
+ method: "POST",
449
+ headers: {
450
+ "Content-Type": "application/json",
451
+ Accept: "application/json",
452
+ merchantId
453
+ },
454
+ body: JSON.stringify(rest)
455
+ }
456
+ );
457
+ return await res.json();
458
+ } catch (error) {
459
+ }
460
+ }
461
+ async function getPaymentReferenceDetails(environment, paymentReference) {
462
+ try {
463
+ const baseUrl = getBaseUrl(environment);
464
+ const res = await fetch(
465
+ `${baseUrl}/api/v1/checkout/details/${paymentReference}`,
466
+ {
467
+ method: "GET",
468
+ headers: {
469
+ "Content-Type": "application/json",
470
+ Accept: "application/json"
471
+ }
472
+ }
473
+ );
474
+ return await res.json();
475
+ } catch (error) {
476
+ }
477
+ }
478
+ async function generatePaymentAccount(environment, { merchantId, ...rest }) {
479
+ try {
480
+ const baseUrl = getBaseUrl(environment);
481
+ const res = await fetch(
482
+ `${baseUrl}/api/v1/checkout/generate-payment-account`,
483
+ {
484
+ method: "POST",
485
+ headers: {
486
+ "Content-Type": "application/json",
487
+ Accept: "application/json",
488
+ merchantId
489
+ },
490
+ body: JSON.stringify(rest)
491
+ }
492
+ );
493
+ return await res.json();
494
+ } catch (error) {
495
+ }
496
+ }
497
+ async function generateStableCoinAddress(environment, { merchantId, ...rest }) {
498
+ try {
499
+ const baseUrl = getBaseUrl(environment);
500
+ const res = await fetch(
501
+ `${baseUrl}/api/v1/checkout/generate-payment-walletaddress`,
502
+ {
503
+ method: "POST",
504
+ headers: {
505
+ "Content-Type": "application/json",
506
+ Accept: "application/json",
507
+ merchantId
508
+ },
509
+ body: JSON.stringify(rest)
510
+ }
511
+ );
512
+ return await res.json();
513
+ } catch (error) {
514
+ }
515
+ }
516
+
517
+ // src/apis/encrypt.api.ts
518
+ var import_crypto_js = __toESM(require("crypto-js"), 1);
519
+ function encryptPayload(merchantId, formData = {}) {
520
+ const merchId = merchantId.replace(/-/g, "");
521
+ const first16Key = merchId.slice(0, 16);
522
+ const last16Iv = merchId.slice(-16);
523
+ const key = import_crypto_js.default.enc.Utf8.parse(first16Key);
524
+ const iv = import_crypto_js.default.enc.Utf8.parse(last16Iv);
525
+ const postDataObj = JSON.stringify(formData);
526
+ const encryptedData = import_crypto_js.default.AES.encrypt(
527
+ import_crypto_js.default.enc.Utf8.parse(postDataObj),
528
+ key,
529
+ {
530
+ keySize: 128 / 8,
531
+ iv,
532
+ mode: import_crypto_js.default.mode.CBC,
533
+ padding: import_crypto_js.default.pad.Pkcs7
534
+ }
535
+ );
536
+ return {
537
+ requestParam: encryptedData.toString()
538
+ };
539
+ }
540
+ function decryptPayload(merchantId, payload) {
541
+ const merchId = merchantId.replace(/-/g, "");
542
+ const first16Key = merchId.slice(0, 16);
543
+ const last16Iv = merchId.slice(-16);
544
+ const key = import_crypto_js.default.enc.Utf8.parse(first16Key);
545
+ const iv = import_crypto_js.default.enc.Utf8.parse(last16Iv);
546
+ const decryptedData = import_crypto_js.default.AES.decrypt(payload, key, {
547
+ keySize: 128 / 8,
548
+ iv,
549
+ mode: import_crypto_js.default.mode.CBC,
550
+ padding: import_crypto_js.default.pad.Pkcs7
551
+ });
552
+ const decryptedText = decryptedData.toString(import_crypto_js.default.enc.Utf8);
553
+ return JSON.parse(decryptedText);
554
+ }
555
+
556
+ // src/apis/resources.api.ts
557
+ async function getCountries(environment, secretKey) {
558
+ try {
559
+ const baseUrl = getBaseUrl(environment);
560
+ const res = await fetch(`${baseUrl}/api/v1/countries-iso`, {
561
+ method: "GET",
562
+ headers: {
563
+ "Content-Type": "application/json",
564
+ Accept: "application/json",
565
+ merchantId: secretKey
566
+ }
567
+ });
568
+ return await res.json();
569
+ } catch (error) {
570
+ }
571
+ }
572
+ async function getCountryStates(countryIso3, environment, secretKey) {
573
+ try {
574
+ const baseUrl = getBaseUrl(environment);
575
+ const res = await fetch(
576
+ `${baseUrl}/api/v1/state-by-country/${countryIso3}`,
577
+ {
578
+ method: "GET",
579
+ headers: {
580
+ "Content-Type": "application/json",
581
+ Accept: "application/json",
582
+ merchantId: secretKey
583
+ }
584
+ }
585
+ );
586
+ return await res.json();
587
+ } catch (error) {
588
+ }
589
+ }
590
+ async function getStableCoins(environment) {
591
+ try {
592
+ const baseUrl = getBaseUrl(environment);
593
+ const res = await fetch(`${baseUrl}/api/v1/checkout/stable-coin`, {
594
+ method: "GET",
595
+ headers: {
596
+ "Content-Type": "application/json",
597
+ Accept: "application/json"
598
+ }
599
+ });
600
+ return await res.json();
601
+ } catch (error) {
602
+ }
603
+ }
604
+ async function getStableCoinNetworks(environment, stableCoin) {
605
+ try {
606
+ const baseUrl = getBaseUrl(environment);
607
+ const res = await fetch(
608
+ `${baseUrl}/api/v1/checkout/networks/${stableCoin}`,
609
+ {
610
+ method: "GET",
611
+ headers: {
612
+ "Content-Type": "application/json",
613
+ Accept: "application/json"
614
+ }
185
615
  }
186
616
  );
187
617
  return await res.json();
@@ -839,13 +1269,33 @@ var BaseLabelInfo = ({
839
1269
 
840
1270
  // src/components/base/success.tsx
841
1271
  var import_jsx_runtime20 = require("react/jsx-runtime");
842
- var BaseSuccess = ({}) => {
1272
+ var BaseSuccess = ({
1273
+ amount,
1274
+ currency,
1275
+ redirectUrl,
1276
+ successObject = {
1277
+ paymentDate: "",
1278
+ paymentId: "",
1279
+ paymentStatus: ""
1280
+ }
1281
+ }) => {
1282
+ const formatAmountHandler = formatAmount(amount, currency);
1283
+ const goToRedirectUrl = () => {
1284
+ window.open(redirectUrl, "_self", "noopener,noreferrer");
1285
+ };
1286
+ const paymentDate = new Date(
1287
+ successObject.paymentDate ?? ""
1288
+ ).toLocaleDateString("en-US", {
1289
+ year: "numeric",
1290
+ month: "long",
1291
+ day: "2-digit"
1292
+ });
843
1293
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col gap-8 p-16", children: [
844
1294
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col gap-8", children: [
845
1295
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col gap-8", children: [
846
1296
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(IconCheckCircle, { color: "#F47A1F", className: "mx-auto" }),
847
1297
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col text-center", style: { gap: "2px" }, children: [
848
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-heading-text font-medium text-header-2xl", children: "\u20A62,500.00" }),
1298
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-heading-text font-medium text-header-2xl", children: formatAmountHandler }),
849
1299
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-sub-copy font-regular text-body-3xs", children: "Has been paid successfully" })
850
1300
  ] })
851
1301
  ] }),
@@ -855,7 +1305,7 @@ var BaseSuccess = ({}) => {
855
1305
  {
856
1306
  type: "horizontal",
857
1307
  label: "Order ID",
858
- value: "9900009000-8890-8829hd7"
1308
+ value: successObject.paymentId
859
1309
  }
860
1310
  ) }),
861
1311
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "py-4", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
@@ -863,7 +1313,7 @@ var BaseSuccess = ({}) => {
863
1313
  {
864
1314
  type: "horizontal",
865
1315
  label: "Payment date",
866
- value: "July 24, 2025"
1316
+ value: paymentDate
867
1317
  }
868
1318
  ) })
869
1319
  ] })
@@ -873,7 +1323,8 @@ var BaseSuccess = ({}) => {
873
1323
  {
874
1324
  label: "Return to Merchant Website",
875
1325
  type: "secondary",
876
- customClass: "w-full"
1326
+ customClass: "w-full",
1327
+ onClick: goToRedirectUrl
877
1328
  }
878
1329
  ) })
879
1330
  ] });
@@ -893,6 +1344,7 @@ var BaseSelect = ({
893
1344
  value,
894
1345
  defaultValue,
895
1346
  onChange,
1347
+ onBlur,
896
1348
  placeholder = "Select an option",
897
1349
  hasSearch = true,
898
1350
  disabled = false,
@@ -965,6 +1417,7 @@ var BaseSelect = ({
965
1417
  const commit = (val) => {
966
1418
  if (!isControlled) setInternalValue(val);
967
1419
  onChange?.(val);
1420
+ onBlur?.(val);
968
1421
  closeMenu();
969
1422
  };
970
1423
  const onSearchInput = (e) => {
@@ -1115,6 +1568,7 @@ var BaseSelect = ({
1115
1568
  ref: searchRef,
1116
1569
  type: "text",
1117
1570
  onChange: onSearchInput,
1571
+ onBlur: onSearchInput,
1118
1572
  placeholder: `Search ${label || "options"}`,
1119
1573
  className: "bg-transparent outline-none border-b pb-2 w-full focus:outline-none focus:ring-0 text-body-2xs text-light-copy font-normal"
1120
1574
  }
@@ -1189,105 +1643,240 @@ var BaseCurrencyAmount = ({
1189
1643
  ] });
1190
1644
  };
1191
1645
 
1192
- // src/buzapay-checkout/checkout-iframe.tsx
1193
- var import_react5 = require("react");
1646
+ // src/components/base/input.tsx
1647
+ var import_react5 = __toESM(require("react"), 1);
1648
+ var import_react_imask = require("react-imask");
1194
1649
  var import_jsx_runtime24 = require("react/jsx-runtime");
1195
- function BzpCheckoutIframe({
1196
- style = {
1197
- width: "100%",
1198
- height: "100vh",
1199
- border: "0",
1200
- borderRadius: "6px",
1201
- overflow: "hidden"
1202
- },
1203
- url,
1204
- secretKey,
1205
- environment = "sandbox",
1206
- paymentObject = {
1207
- amount: 0,
1208
- currency: "",
1209
- email: "",
1210
- phoneNumber: "",
1211
- narration: "",
1212
- redirectUrl: ""
1213
- }
1214
- }) {
1215
- const containerRef = (0, import_react5.useRef)(null);
1216
- const iframeRef = (0, import_react5.useRef)(null);
1217
- const [message, setMessage] = (0, import_react5.useState)("");
1218
- const [launchUrl, setLaunchUrl] = (0, import_react5.useState)("");
1219
- const [loading, setLoading] = (0, import_react5.useState)(false);
1220
- const launchIframe = (0, import_react5.useCallback)(
1221
- (url2) => {
1222
- if (!containerRef.current) return;
1223
- if (iframeRef.current && iframeRef.current.parentNode) {
1224
- iframeRef.current.parentNode.removeChild(iframeRef.current);
1225
- }
1226
- const iframe = document.createElement("iframe");
1227
- iframe.src = url2;
1228
- if (style.width) iframe.style.width = style.width;
1229
- if (style.height) iframe.style.height = style.height;
1230
- if (style.border) iframe.style.border = style.border;
1231
- if (style.borderRadius) iframe.style.borderRadius = style.borderRadius;
1232
- if (style.overflow) iframe.style.overflow = style.overflow;
1233
- containerRef.current.appendChild(iframe);
1234
- iframeRef.current = iframe;
1235
- },
1236
- [style]
1650
+ var BaseInput = ({
1651
+ label = "",
1652
+ type = "text",
1653
+ mask,
1654
+ placeholder,
1655
+ validationError = "",
1656
+ hint = "",
1657
+ rules = [],
1658
+ isAmountInput = false,
1659
+ required = false,
1660
+ disabled = false,
1661
+ loading = false,
1662
+ showCopyIcon = false,
1663
+ preventPaste = false,
1664
+ value,
1665
+ defaultValue,
1666
+ onChange,
1667
+ onBlur,
1668
+ prefix,
1669
+ suffix,
1670
+ className = ""
1671
+ }) => {
1672
+ const isControlled = value !== void 0;
1673
+ const [rawValue, setRawValue] = import_react5.default.useState(
1674
+ defaultValue ?? value ?? ""
1237
1675
  );
1238
- const generatePaymentLinkHandler = async () => {
1239
- if (url) {
1240
- launchIframe(url);
1241
- return;
1242
- }
1243
- if (!secretKey) {
1244
- return setMessage("Secret key is required.");
1245
- }
1246
- if (!checkObjectTruthy(paymentObject)) {
1247
- return setMessage("Secret key is required.");
1248
- }
1249
- setLoading(true);
1250
- const response = await createPaymentLink(
1251
- paymentObject,
1252
- environment,
1253
- secretKey
1254
- );
1255
- if (response?.isSuccessful) {
1256
- setLaunchUrl(response.launchUrl ?? "");
1257
- setMessage("Payment link created successfully");
1258
- if (launchUrl) {
1259
- launchIframe(launchUrl);
1260
- }
1261
- } else {
1262
- setMessage("Failed to create payment link");
1676
+ const [localHint, setLocalHint] = import_react5.default.useState("");
1677
+ const [localError, setLocalError] = import_react5.default.useState("");
1678
+ import_react5.default.useEffect(() => {
1679
+ if (isControlled) setRawValue(value ?? "");
1680
+ }, [isControlled, value]);
1681
+ const formattedValue = isAmountInput ? formatAmount(rawValue.replace(/,/g, "")) : rawValue;
1682
+ const handleChange = (e) => {
1683
+ const incoming = e.target.value.replace(/,/g, "");
1684
+ if (!isControlled) setRawValue(incoming);
1685
+ onChange?.(incoming);
1686
+ setTimeout(() => {
1687
+ const el = e.target;
1688
+ el.selectionStart = el.selectionEnd = el.value.length;
1689
+ });
1690
+ };
1691
+ const handleBlur = () => {
1692
+ onBlur?.(rawValue);
1693
+ };
1694
+ const handleKeyDown = (e) => {
1695
+ if (rules.includes("numeric")) restrictToNumericKeys(e);
1696
+ };
1697
+ const handlePaste = (e) => {
1698
+ if (preventPaste) {
1699
+ e.preventDefault();
1700
+ setLocalError("Pasting is disabled for this input");
1263
1701
  }
1264
1702
  };
1265
- (0, import_react5.useEffect)(() => {
1266
- (async () => {
1267
- await generatePaymentLinkHandler();
1268
- })();
1269
- }, []);
1270
- return loading ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "w-full h-48 flex flex-col justify-center items-center", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(IconLoader, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ref: containerRef, style });
1271
- }
1703
+ const containerBg = disabled ? "bg-grey-50 cursor-not-allowed" : "bg-white";
1704
+ const containerBorder = validationError || localError ? "border-red-300 bg-red-50" : "border-grey-100";
1705
+ const copyToClipboard = (text) => {
1706
+ return navigator.clipboard.writeText(text);
1707
+ };
1708
+ const copyHandler = () => {
1709
+ copyToClipboard(rawValue).then(() => {
1710
+ setLocalHint("Text copied to clipboard");
1711
+ }).catch(() => {
1712
+ setLocalError("Failed to copy text to clipboard");
1713
+ });
1714
+ };
1715
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: `flex flex-col gap-2 ${className}`, children: [
1716
+ label ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("p", { className: "mb-0 text-body-2xs font-normal text-heading-text", children: [
1717
+ label,
1718
+ required && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-orange-required", children: " *" })
1719
+ ] }) : null,
1720
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1721
+ "div",
1722
+ {
1723
+ className: `border-c px-3 py-2 flex items-center justify-between rounded-md h-12 ${containerBg} ${containerBorder}`,
1724
+ children: [
1725
+ prefix,
1726
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1727
+ import_react_imask.IMaskInput,
1728
+ {
1729
+ type,
1730
+ mask,
1731
+ value: formattedValue,
1732
+ onChange: handleChange,
1733
+ onBlur: handleBlur,
1734
+ onKeyDown: handleKeyDown,
1735
+ onPaste: handlePaste,
1736
+ disabled,
1737
+ placeholder: placeholder ?? (label ? `Enter ${label.toLowerCase()}` : void 0),
1738
+ inputMode: isAmountInput ? "decimal" : void 0,
1739
+ className: "search-input bg-transparent outline-none border-none focus:outline-none focus:ring-0 text-body-2xs text-light-copy font-normal w-full"
1740
+ }
1741
+ ),
1742
+ !loading ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
1743
+ suffix,
1744
+ showCopyIcon && rawValue && rawValue.trim() !== "" && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1745
+ BaseImage,
1746
+ {
1747
+ src: "assets/images/copyIcon.svg",
1748
+ alt: "copy",
1749
+ width: 16,
1750
+ height: 16,
1751
+ customClass: "cursor-pointer hover:opacity-70 transition-opacity",
1752
+ onClick: copyHandler
1753
+ }
1754
+ )
1755
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "animate-spin h-4 w-4 border-2 border-current border-t-transparent rounded-full" })
1756
+ ]
1757
+ }
1758
+ ),
1759
+ (hint || localHint) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-body-3xs text-light-copy", children: localHint || hint }),
1760
+ (validationError || localError) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-body-3xs text-red-500", children: localError || validationError })
1761
+ ] });
1762
+ };
1272
1763
 
1273
- // src/buzapay-checkout/checkout-button.tsx
1764
+ // src/buzapay-checkout/checkout-iframe.tsx
1765
+ var import_react6 = require("react");
1274
1766
  var import_jsx_runtime25 = require("react/jsx-runtime");
1275
- function BzpCheckoutButton({
1767
+ function BzpCheckoutIframe({
1768
+ style = {
1769
+ width: "100%",
1770
+ height: "100vh",
1771
+ border: "0",
1772
+ borderRadius: "6px",
1773
+ overflow: "hidden"
1774
+ },
1775
+ url,
1276
1776
  secretKey,
1277
1777
  environment = "sandbox",
1278
1778
  paymentObject = {
1779
+ merchantName: "",
1279
1780
  amount: 0,
1280
1781
  currency: "",
1281
1782
  email: "",
1282
1783
  phoneNumber: "",
1283
1784
  narration: "",
1284
- redirectUrl: ""
1785
+ redirectUrl: "https://sandbox-merchant.buzapay.com/account/three-ds-status"
1285
1786
  },
1286
- mode = "redirect"
1787
+ onError
1287
1788
  }) {
1789
+ const containerRef = (0, import_react6.useRef)(null);
1790
+ const iframeRef = (0, import_react6.useRef)(null);
1288
1791
  const [message, setMessage] = (0, import_react6.useState)("");
1289
1792
  const [launchUrl, setLaunchUrl] = (0, import_react6.useState)("");
1290
1793
  const [loading, setLoading] = (0, import_react6.useState)(false);
1794
+ const launchIframe = (0, import_react6.useCallback)(
1795
+ (url2) => {
1796
+ if (iframeRef.current && iframeRef.current.parentNode) {
1797
+ iframeRef.current.parentNode.removeChild(iframeRef.current);
1798
+ }
1799
+ const iframe = document.createElement("iframe");
1800
+ iframe.src = url2;
1801
+ if (style.width) iframe.style.width = style.width;
1802
+ if (style.height) iframe.style.height = style.height;
1803
+ if (style.border) iframe.style.border = style.border;
1804
+ if (style.borderRadius) iframe.style.borderRadius = style.borderRadius;
1805
+ if (style.overflow) iframe.style.overflow = style.overflow;
1806
+ containerRef.current?.appendChild(iframe);
1807
+ iframeRef.current = iframe;
1808
+ },
1809
+ [style]
1810
+ );
1811
+ const generatePaymentLinkHandler = async () => {
1812
+ if (url) {
1813
+ launchIframe(url);
1814
+ return;
1815
+ }
1816
+ if (!secretKey) {
1817
+ return setMessage("Secret key is required.");
1818
+ }
1819
+ if (!checkObjectTruthy(paymentObject)) {
1820
+ return setMessage("Secret key is required.");
1821
+ }
1822
+ setLoading(true);
1823
+ try {
1824
+ const response = await createPaymentLink(
1825
+ paymentObject,
1826
+ environment,
1827
+ secretKey
1828
+ );
1829
+ if (response?.isSuccessful) {
1830
+ setLaunchUrl(response.launchUrl ?? "");
1831
+ setMessage("Payment link created successfully");
1832
+ if (response.launchUrl) {
1833
+ setLoading(false);
1834
+ launchIframe(`${response.launchUrl}&merchantId=${btoa(secretKey)}`);
1835
+ }
1836
+ } else {
1837
+ setLoading(false);
1838
+ setMessage("Failed to create payment link");
1839
+ }
1840
+ } catch (e) {
1841
+ setMessage(e?.message || "Failed to create payment link");
1842
+ onError?.({
1843
+ errorMessage: message
1844
+ });
1845
+ } finally {
1846
+ setLoading(false);
1847
+ }
1848
+ };
1849
+ (0, import_react6.useEffect)(() => {
1850
+ if (!containerRef.current) return;
1851
+ generatePaymentLinkHandler();
1852
+ }, []);
1853
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "relative", style, children: [
1854
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref: containerRef, className: "w-full h-full" }),
1855
+ loading && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "absolute inset-0 grid place-items-center bg-white/60", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(IconLoader, {}) })
1856
+ ] });
1857
+ }
1858
+
1859
+ // src/buzapay-checkout/checkout-button.tsx
1860
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1861
+ function BzpCheckoutButton({
1862
+ secretKey,
1863
+ environment = "sandbox",
1864
+ paymentObject = {
1865
+ merchantName: "",
1866
+ amount: 0,
1867
+ currency: "",
1868
+ email: "",
1869
+ phoneNumber: "",
1870
+ narration: "",
1871
+ redirectUrl: "https://sandbox-merchant.buzapay.com/account/three-ds-status"
1872
+ },
1873
+ mode = "redirect",
1874
+ onError
1875
+ }) {
1876
+ const [message, setMessage] = (0, import_react7.useState)("");
1877
+ const [launchUrl, setLaunchUrl] = (0, import_react7.useState)("");
1878
+ const [urlLaunchUrl, setUrlLaunchUrl] = (0, import_react7.useState)("");
1879
+ const [loading, setLoading] = (0, import_react7.useState)(false);
1291
1880
  const generatePaymentLinkHandler = async () => {
1292
1881
  if (!secretKey) {
1293
1882
  setMessage("Secret key is required.");
@@ -1307,6 +1896,7 @@ function BzpCheckoutButton({
1307
1896
  );
1308
1897
  if (response?.isSuccessful && response.launchUrl) {
1309
1898
  setLaunchUrl(response.launchUrl);
1899
+ setUrlLaunchUrl(`${response.launchUrl}&merchantId=${btoa(secretKey)}`);
1310
1900
  setMessage("Payment link created successfully");
1311
1901
  if (mode === "redirect") {
1312
1902
  window.open(response.launchUrl, "_blank", "noopener,noreferrer");
@@ -1316,19 +1906,22 @@ function BzpCheckoutButton({
1316
1906
  }
1317
1907
  } catch (e) {
1318
1908
  setMessage(e?.message || "Failed to create payment link");
1909
+ onError?.({
1910
+ errorMessage: message
1911
+ });
1319
1912
  } finally {
1320
1913
  setLoading(false);
1321
1914
  }
1322
1915
  };
1323
- return launchUrl && mode === "iframe" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1916
+ return urlLaunchUrl && mode === "iframe" ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1324
1917
  BzpCheckoutIframe,
1325
1918
  {
1326
- url: launchUrl,
1919
+ url: urlLaunchUrl,
1327
1920
  secretKey,
1328
1921
  environment
1329
1922
  }
1330
- ) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { children: [
1331
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1923
+ ) : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { children: [
1924
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1332
1925
  BaseButton,
1333
1926
  {
1334
1927
  label: "Pay",
@@ -1338,7 +1931,7 @@ function BzpCheckoutButton({
1338
1931
  onClick: generatePaymentLinkHandler
1339
1932
  }
1340
1933
  ),
1341
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(BaseInputError, { errorMessage: message })
1934
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(BaseInputError, { errorMessage: message })
1342
1935
  ] });
1343
1936
  }
1344
1937
 
@@ -1347,137 +1940,367 @@ var import_react11 = require("react");
1347
1940
 
1348
1941
  // src/components/pay-by-card.tsx
1349
1942
  var import_react8 = require("react");
1350
-
1351
- // src/components/base/input.tsx
1352
- var import_react7 = __toESM(require("react"), 1);
1353
- var import_jsx_runtime26 = require("react/jsx-runtime");
1354
- var BaseInput = ({
1355
- label = "",
1356
- type = "text",
1357
- placeholder,
1358
- validationError = "",
1359
- hint = "",
1360
- rules = [],
1361
- isAmountInput = false,
1362
- required = false,
1363
- disabled = false,
1364
- loading = false,
1365
- showCopyIcon = false,
1366
- value,
1367
- defaultValue,
1368
- onChange,
1369
- onBlur,
1370
- prefix,
1371
- suffix,
1372
- className = ""
1373
- }) => {
1374
- const isControlled = value !== void 0;
1375
- const [rawValue, setRawValue] = import_react7.default.useState(
1376
- defaultValue ?? value ?? ""
1943
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1944
+ function PayByCard({
1945
+ secretKey,
1946
+ paymentObject,
1947
+ environment = "sandbox",
1948
+ onPaymentAuthorized,
1949
+ onError
1950
+ }) {
1951
+ const [formIndex, setFormIndex] = (0, import_react8.useState)(0);
1952
+ const [message, setMessage] = (0, import_react8.useState)("");
1953
+ const [isMakingPayment, setIsMakingPayment] = (0, import_react8.useState)(false);
1954
+ const [loading, setLoading] = (0, import_react8.useState)(false);
1955
+ const [cardType, setCardType] = (0, import_react8.useState)("");
1956
+ const [transactionReference, setTransactionReference] = (0, import_react8.useState)("");
1957
+ const [loadingCountries, setLoadingCountries] = (0, import_react8.useState)(false);
1958
+ const [loadingStates, setLoadingStates] = (0, import_react8.useState)(false);
1959
+ const [rawCountries, setRawCountries] = (0, import_react8.useState)([]);
1960
+ const [countries, setCountries] = (0, import_react8.useState)([]);
1961
+ const [countryStates, setCountryStates] = (0, import_react8.useState)([]);
1962
+ const [billingForm, setBillingForm] = (0, import_react8.useState)({
1963
+ address1: "",
1964
+ address2: "",
1965
+ postalCode: "",
1966
+ state: "",
1967
+ city: "",
1968
+ country: "",
1969
+ emailAddress: "",
1970
+ phoneNumber: ""
1971
+ });
1972
+ const [payForm, setPayForm] = (0, import_react8.useState)({
1973
+ customerName: "",
1974
+ cardNo: "",
1975
+ expireDate: "",
1976
+ cvv: "",
1977
+ cardPin: ""
1978
+ // Only required for Verve cards
1979
+ });
1980
+ const [billingErrors, setBillingErrors] = (0, import_react8.useState)(
1981
+ {}
1377
1982
  );
1378
- const [localHint, setLocalHint] = import_react7.default.useState("");
1379
- const [localError, setLocalError] = import_react7.default.useState("");
1380
- import_react7.default.useEffect(() => {
1381
- if (isControlled) setRawValue(value ?? "");
1382
- }, [isControlled, value]);
1383
- const formattedValue = isAmountInput ? formatAmount(rawValue.replace(/,/g, "")) : rawValue;
1384
- const handleChange = (e) => {
1385
- const incoming = e.target.value.replace(/,/g, "");
1386
- if (!isControlled) setRawValue(incoming);
1387
- onChange?.(incoming);
1388
- setTimeout(() => {
1389
- const el = e.target;
1390
- el.selectionStart = el.selectionEnd = el.value.length;
1391
- });
1983
+ const [payErrors, setPayErrors] = (0, import_react8.useState)({});
1984
+ const billingRules = {
1985
+ address1: "required",
1986
+ address2: "",
1987
+ // optional
1988
+ country: "required",
1989
+ state: "required",
1990
+ city: "required",
1991
+ postalCode: "required|no_special|char_length:6",
1992
+ emailAddress: "required|email",
1993
+ phoneNumber: "required"
1392
1994
  };
1393
- const handleBlur = () => {
1394
- onBlur?.(rawValue);
1995
+ const payRules = {
1996
+ customerName: "required",
1997
+ cardNo: "required|num_spaces",
1998
+ expireDate: "required",
1999
+ cvv: "required|numeric",
2000
+ cardPin: ""
2001
+ // optional unless Verve
1395
2002
  };
1396
- const handleKeyDown = (e) => {
1397
- if (rules.includes("numeric")) restrictToNumericKeys(e);
2003
+ const formatAmountHandler = formatAmount(
2004
+ paymentObject.amount,
2005
+ paymentObject.currency
2006
+ );
2007
+ const billingLabels = {
2008
+ address1: "Address Line 1",
2009
+ address2: "Address Line 2",
2010
+ postalCode: "Postal Code",
2011
+ state: "State",
2012
+ city: "City",
2013
+ country: "Country",
2014
+ emailAddress: "Email",
2015
+ phoneNumber: "Phone Number"
1398
2016
  };
1399
- const containerBg = disabled ? "bg-grey-50 cursor-not-allowed" : "bg-white";
1400
- const containerBorder = validationError || localError ? "border-red-300 bg-red-50" : "border-grey-100";
1401
- const copyToClipboard = (text) => {
1402
- return navigator.clipboard.writeText(text);
2017
+ const payLabels = {
2018
+ customerName: "Card Name",
2019
+ cardNo: "Card Number",
2020
+ expireDate: "Expiry Date",
2021
+ cvv: "CVV",
2022
+ cardPin: "Card PIN"
1403
2023
  };
1404
- const copyHandler = () => {
1405
- copyToClipboard(rawValue).then(() => {
1406
- setLocalHint("Text copied to clipboard");
1407
- }).catch((err) => {
1408
- setLocalError("Failed to copy text to clipboard");
1409
- });
2024
+ const proceedHandler = async () => {
2025
+ if (formIndex === 0) {
2026
+ const errs = validateGroup(billingForm, billingRules, billingLabels);
2027
+ setBillingErrors(errs);
2028
+ if (Object.keys(errs).length === 0) {
2029
+ setFormIndex(1);
2030
+ }
2031
+ return;
2032
+ }
2033
+ if (formIndex === 1) {
2034
+ const errs = validateGroup(payForm, payRules, payLabels);
2035
+ setPayErrors(errs);
2036
+ if (Object.keys(errs).length > 0) {
2037
+ return;
2038
+ }
2039
+ try {
2040
+ setIsMakingPayment(true);
2041
+ setMessage("");
2042
+ const cardDetails = {
2043
+ pan: payForm.cardNo ?? "",
2044
+ expiryDate: payForm.expireDate ?? "",
2045
+ cvv: payForm.cvv ?? "",
2046
+ cardScheme: cardType,
2047
+ nameOnCard: payForm.customerName ?? "",
2048
+ ...cardType === "Verve" && { pin: payForm.cardPin ?? "" }
2049
+ };
2050
+ const billingDetails = {
2051
+ address1: billingForm.address1 ?? "",
2052
+ address2: billingForm.address2 ?? "",
2053
+ postalCode: billingForm.postalCode ?? "",
2054
+ state: billingForm.state ?? "",
2055
+ city: billingForm.city ?? "",
2056
+ country: billingForm.country ?? "",
2057
+ emailAddress: billingForm.emailAddress ?? "",
2058
+ phoneNumber: billingForm.phoneNumber ?? ""
2059
+ };
2060
+ const encryptedCardDetails = encryptPayload(secretKey, cardDetails);
2061
+ const payload = {
2062
+ customerId: paymentObject?.email || payForm.customerName || "",
2063
+ amount: String(paymentObject?.amount ?? ""),
2064
+ currency: paymentObject?.currency || "USD",
2065
+ narration: paymentObject?.narration || "Test transaction",
2066
+ encryptedCardDetails: encryptedCardDetails.requestParam,
2067
+ billingDetails,
2068
+ redirectUrl: paymentObject?.redirectUrl || "",
2069
+ paymentReference: transactionReference,
2070
+ isCheckout: true
2071
+ };
2072
+ const request = { ...payload, merchantId: secretKey };
2073
+ let response = await authorizeCardPayment(environment, request);
2074
+ if (response?.responseParam) {
2075
+ response = decryptPayload(environment, response.responseParam);
2076
+ }
2077
+ if (response?.isSuccessful) {
2078
+ if (response.threeDsInteractionRequired === true) {
2079
+ const threeDsData = {
2080
+ transactionReference: response.transactionReference,
2081
+ threeDsHtml: response.threeDsHtml,
2082
+ amount: response.amount,
2083
+ responseMessage: response.responseMessage,
2084
+ paReq: response.threeDsHtml?.paReq,
2085
+ termUrl: response.threeDsHtml?.termUrl,
2086
+ action: response.threeDsHtml?.action,
2087
+ acsUrl: response.threeDsHtml?.acsUrl,
2088
+ md: response.threeDsHtml?.md
2089
+ };
2090
+ const stringifiedThreeDsData = btoa(JSON.stringify(threeDsData));
2091
+ const threeDsUrl = `https://sandbox-merchant.buzapay.com/account/three-ds-confirm?threeDsData=${encodeURIComponent(
2092
+ stringifiedThreeDsData
2093
+ )}&paymentReference=${response.transactionReference}`;
2094
+ window.open(threeDsUrl, "_self", "noopener,noreferrer");
2095
+ setMessage(
2096
+ "3D Secure authentication opened in new tab. Please complete the verification"
2097
+ );
2098
+ setIsMakingPayment(false);
2099
+ return;
2100
+ }
2101
+ if (response.responseMessage === "Payer Interaction Required" && response.threeDsHtml) {
2102
+ const threeDsData = {
2103
+ transactionReference: response.transactionReference,
2104
+ threeDsHtml: response.threeDsHtml,
2105
+ amount: response.amount,
2106
+ responseMessage: response.responseMessage,
2107
+ paReq: response.threeDsHtml?.paReq,
2108
+ termUrl: response.threeDsHtml?.termUrl,
2109
+ action: response.threeDsHtml?.action,
2110
+ acsUrl: response.threeDsHtml?.acsUrl,
2111
+ md: response.threeDsHtml?.md
2112
+ };
2113
+ const stringifiedThreeDsData = btoa(JSON.stringify(threeDsData));
2114
+ const threeDsUrl = `https://sandbox-merchant.buzapay.com/account/three-ds-confirm?threeDsData=${encodeURIComponent(
2115
+ stringifiedThreeDsData
2116
+ )}&paymentReference=${response.transactionReference}`;
2117
+ window.open(threeDsUrl, "_self", "noopener,noreferrer");
2118
+ setMessage(
2119
+ "3D Secure authentication opened in new tab. Please complete the verification"
2120
+ );
2121
+ setIsMakingPayment(false);
2122
+ return;
2123
+ }
2124
+ if (response.transactionReference?.trim()) {
2125
+ onPaymentAuthorized?.({
2126
+ paymentId: response.transactionReference,
2127
+ paymentDate: response?.data?.updatedAt,
2128
+ // optional if present
2129
+ paymentStatus: "authorized",
2130
+ message
2131
+ });
2132
+ }
2133
+ setMessage("Card payment authorized successfully");
2134
+ setIsMakingPayment(false);
2135
+ return;
2136
+ }
2137
+ setMessage(response?.responseMessage || "Payment failed");
2138
+ onPaymentAuthorized?.({
2139
+ paymentId: response?.transactionReference,
2140
+ paymentDate: response?.data?.updatedAt,
2141
+ paymentStatus: "payment failed",
2142
+ message
2143
+ });
2144
+ setIsMakingPayment(false);
2145
+ } catch (err) {
2146
+ let friendly = "Payment failed";
2147
+ if (err?.error?.responseParam) {
2148
+ try {
2149
+ const decryptedError = decryptPayload(
2150
+ environment,
2151
+ err.error.responseParam
2152
+ );
2153
+ friendly = decryptedError?.responseMessage || friendly;
2154
+ } catch {
2155
+ friendly = err?.error?.responseMessage || err?.error?.message || friendly;
2156
+ }
2157
+ } else {
2158
+ friendly = err?.error?.responseMessage || err?.error?.message || "Payment failed";
2159
+ }
2160
+ setMessage(friendly);
2161
+ setIsMakingPayment(false);
2162
+ onError?.({
2163
+ errorMessage: message
2164
+ });
2165
+ }
2166
+ }
1410
2167
  };
1411
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: `flex flex-col gap-2 ${className}`, children: [
1412
- label ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("p", { className: "mb-0 text-body-2xs font-normal text-heading-text", children: [
1413
- label,
1414
- required && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-orange-required", children: " *" })
1415
- ] }) : null,
1416
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1417
- "div",
1418
- {
1419
- className: `border-c px-3 py-2 flex items-center justify-between rounded-md h-12 ${containerBg} ${containerBorder}`,
1420
- children: [
1421
- prefix,
1422
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1423
- "input",
1424
- {
1425
- type,
1426
- value: formattedValue,
1427
- onChange: handleChange,
1428
- onBlur: handleBlur,
1429
- onKeyDown: handleKeyDown,
1430
- disabled,
1431
- placeholder: placeholder ?? (label ? `Enter ${label.toLowerCase()}` : void 0),
1432
- inputMode: isAmountInput ? "decimal" : void 0,
1433
- className: "search-input bg-transparent outline-none border-none focus:outline-none focus:ring-0 text-body-2xs text-light-copy font-normal w-full"
1434
- }
1435
- ),
1436
- !loading ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
1437
- suffix,
1438
- showCopyIcon && rawValue && rawValue.trim() !== "" && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1439
- BaseImage,
1440
- {
1441
- src: "assets/images/copyIcon.svg",
1442
- alt: "copy",
1443
- width: 16,
1444
- height: 16,
1445
- customClass: "cursor-pointer hover:opacity-70 transition-opacity",
1446
- onClick: copyHandler
1447
- }
1448
- )
1449
- ] }) : (
1450
- // Simple loader placeholder; swap for your icon component if desired
1451
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "animate-spin h-4 w-4 border-2 border-current border-t-transparent rounded-full" })
1452
- )
1453
- ]
2168
+ const generatePaymentLinkHandler = async () => {
2169
+ if (!secretKey) {
2170
+ setMessage("Secret key is required.");
2171
+ return;
2172
+ }
2173
+ if (!checkObjectTruthy(paymentObject)) {
2174
+ setMessage("Kindly ensure you are passing all the required data.");
2175
+ return;
2176
+ }
2177
+ setLoading(true);
2178
+ setMessage("");
2179
+ try {
2180
+ const response = await createPaymentLink(
2181
+ paymentObject,
2182
+ environment,
2183
+ secretKey
2184
+ );
2185
+ if (response?.isSuccessful && response.launchUrl) {
2186
+ const queryParams = getQueryParams(response.launchUrl);
2187
+ setTransactionReference(queryParams["paymentReference"]);
2188
+ setMessage("Payment link created successfully");
2189
+ } else {
2190
+ setMessage("Failed to create payment link");
1454
2191
  }
1455
- ),
1456
- (hint || localHint) && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-body-3xs text-light-copy", children: localHint || hint }),
1457
- (validationError || localError) && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-body-3xs text-red-500", children: localError || validationError })
1458
- ] });
1459
- };
1460
-
1461
- // src/components/pay-by-card.tsx
1462
- var import_jsx_runtime27 = require("react/jsx-runtime");
1463
- function PayByCard({}) {
1464
- const [formIndex, setFormIndex] = (0, import_react8.useState)(0);
2192
+ } catch (e) {
2193
+ setMessage(e?.message || "Failed to create payment link");
2194
+ onError?.({
2195
+ errorMessage: message
2196
+ });
2197
+ } finally {
2198
+ setLoading(false);
2199
+ }
2200
+ };
2201
+ const getAllCountries = async () => {
2202
+ setLoadingCountries(true);
2203
+ try {
2204
+ const response = await getCountries(
2205
+ environment,
2206
+ secretKey
2207
+ );
2208
+ if (response?.isSuccessful) {
2209
+ setRawCountries(response.data);
2210
+ setCountries(
2211
+ (response.data ?? []).map((c) => {
2212
+ return { label: c.countryName, value: c.iso2 };
2213
+ })
2214
+ );
2215
+ }
2216
+ } catch (e) {
2217
+ setMessage(e?.message || "Failed to get countries");
2218
+ onError?.({
2219
+ errorMessage: message
2220
+ });
2221
+ } finally {
2222
+ setLoadingCountries(false);
2223
+ }
2224
+ };
2225
+ const getStates = async (countryIso2) => {
2226
+ const country = rawCountries.find((c) => c.iso2 === countryIso2);
2227
+ if (!country) return;
2228
+ setLoadingStates(true);
2229
+ try {
2230
+ const response = await getCountryStates(
2231
+ country.iso3,
2232
+ environment,
2233
+ secretKey
2234
+ );
2235
+ if (response?.isSuccessful) {
2236
+ setCountryStates(
2237
+ (response.data ?? []).map((s) => ({
2238
+ label: s.name,
2239
+ value: s.name
2240
+ }))
2241
+ );
2242
+ }
2243
+ } catch (e) {
2244
+ setMessage(e?.message || "Failed to get country states");
2245
+ onError?.({
2246
+ errorMessage: message
2247
+ });
2248
+ } finally {
2249
+ setLoadingStates(false);
2250
+ }
2251
+ };
2252
+ const cardNumberInputHandler = (event) => {
2253
+ setCardType(cardTypeHandler(event));
2254
+ payRules.cardPin = cardType === "Verve" ? "required|numeric" : "";
2255
+ };
2256
+ (0, import_react8.useEffect)(() => {
2257
+ (async () => {
2258
+ await generatePaymentLinkHandler();
2259
+ await getAllCountries();
2260
+ })();
2261
+ }, []);
1465
2262
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-6", children: [
1466
2263
  formIndex === 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-2 gap-6 overflow-y-auto", children: [
1467
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "First Name", required: true }),
1468
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "Last Name", required: true }),
1469
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "Email", required: true }),
1470
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "Phone Number", required: true }),
2264
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2265
+ BaseInput,
2266
+ {
2267
+ label: "Address Line 1",
2268
+ required: true,
2269
+ value: billingForm.address1,
2270
+ onChange: (e) => {
2271
+ setBillingForm({ ...billingForm, address1: e });
2272
+ if (billingErrors.address1) {
2273
+ setBillingErrors((er) => ({ ...er, address1: "" }));
2274
+ }
2275
+ },
2276
+ validationError: billingErrors.address1 ?? ""
2277
+ }
2278
+ ) }),
2279
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2280
+ BaseInput,
2281
+ {
2282
+ label: "Address Line 2",
2283
+ value: billingForm.address2,
2284
+ onChange: (e) => setBillingForm({ ...billingForm, address2: e }),
2285
+ validationError: billingErrors.address2 ?? ""
2286
+ }
2287
+ ) }),
1471
2288
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1472
2289
  BaseSelect,
1473
2290
  {
1474
2291
  label: "Select Country",
1475
2292
  required: true,
1476
- options: [
1477
- { label: "United States", value: "US" },
1478
- { label: "Canada", value: "CA" },
1479
- { label: "United Kingdom", value: "UK" }
1480
- ]
2293
+ options: countries,
2294
+ loading: loadingCountries,
2295
+ value: billingForm.country,
2296
+ onChange: (e) => {
2297
+ setBillingForm({ ...billingForm, country: e, state: "" });
2298
+ getStates(e);
2299
+ if (billingErrors.country) {
2300
+ setBillingErrors((er) => ({ ...er, country: "" }));
2301
+ }
2302
+ },
2303
+ validationError: billingErrors.country ?? ""
1481
2304
  }
1482
2305
  ),
1483
2306
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
@@ -1485,37 +2308,180 @@ function PayByCard({}) {
1485
2308
  {
1486
2309
  label: "Select State",
1487
2310
  required: true,
1488
- options: [
1489
- { label: "California", value: "CA" },
1490
- { label: "Texas", value: "TX" },
1491
- { label: "New York", value: "NY" }
1492
- ]
2311
+ options: countryStates,
2312
+ loading: loadingStates,
2313
+ value: billingForm.state,
2314
+ onChange: (e) => {
2315
+ setBillingForm({ ...billingForm, state: e });
2316
+ if (billingErrors.state) {
2317
+ setBillingErrors((er) => ({ ...er, state: "" }));
2318
+ }
2319
+ },
2320
+ validationError: billingErrors.state ?? ""
2321
+ }
2322
+ ),
2323
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2324
+ BaseInput,
2325
+ {
2326
+ label: "City",
2327
+ required: true,
2328
+ value: billingForm.city,
2329
+ onChange: (e) => {
2330
+ setBillingForm({ ...billingForm, city: e });
2331
+ if (billingErrors.city) {
2332
+ setBillingErrors((er) => ({ ...er, city: "" }));
2333
+ }
2334
+ },
2335
+ validationError: billingErrors.city ?? ""
2336
+ }
2337
+ ),
2338
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2339
+ BaseInput,
2340
+ {
2341
+ label: "Postal Code",
2342
+ required: true,
2343
+ value: billingForm.postalCode,
2344
+ onChange: (e) => {
2345
+ setBillingForm({ ...billingForm, postalCode: e });
2346
+ if (billingErrors.postalCode) {
2347
+ setBillingErrors((er) => ({ ...er, postalCode: "" }));
2348
+ }
2349
+ },
2350
+ validationError: billingErrors.postalCode ?? ""
1493
2351
  }
1494
2352
  ),
1495
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "City", required: true }),
1496
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "Postal Code", required: true }),
1497
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "Street Address", required: true }) })
2353
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2354
+ BaseInput,
2355
+ {
2356
+ label: "Email",
2357
+ required: true,
2358
+ value: billingForm.emailAddress,
2359
+ onChange: (e) => {
2360
+ setBillingForm({ ...billingForm, emailAddress: e });
2361
+ if (billingErrors.emailAddress) {
2362
+ setBillingErrors((er) => ({ ...er, emailAddress: "" }));
2363
+ }
2364
+ },
2365
+ validationError: billingErrors.emailAddress ?? ""
2366
+ }
2367
+ ),
2368
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2369
+ BaseInput,
2370
+ {
2371
+ label: "Phone Number",
2372
+ required: true,
2373
+ value: billingForm.phoneNumber,
2374
+ onChange: (e) => {
2375
+ setBillingForm({ ...billingForm, phoneNumber: e });
2376
+ if (billingErrors.phoneNumber) {
2377
+ setBillingErrors((er) => ({ ...er, phoneNumber: "" }));
2378
+ }
2379
+ },
2380
+ validationError: billingErrors.phoneNumber ?? ""
2381
+ }
2382
+ )
1498
2383
  ] }),
1499
2384
  formIndex === 1 && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1500
2385
  "div",
1501
2386
  {
1502
2387
  className: "grid grid-cols-2 gap-6 overflow-y-auto",
1503
- style: { maxHeight: "320px" },
2388
+ style: { maxHeight: 320 },
1504
2389
  children: [
1505
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "Card Name", required: true }) }),
1506
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "Card Number", required: true }) }),
1507
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "Expiry Date", required: true }),
1508
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BaseInput, { label: "CVV", required: true })
2390
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2391
+ BaseInput,
2392
+ {
2393
+ label: "Card Name",
2394
+ required: true,
2395
+ value: payForm.customerName,
2396
+ onChange: (e) => {
2397
+ setPayForm({ ...payForm, customerName: e });
2398
+ if (payErrors.customerName) {
2399
+ setPayErrors((er) => ({ ...er, customerName: "" }));
2400
+ }
2401
+ },
2402
+ validationError: payErrors.customerName ?? ""
2403
+ }
2404
+ ) }),
2405
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2406
+ BaseInput,
2407
+ {
2408
+ label: "Card Number",
2409
+ required: true,
2410
+ rules: ["numeric"],
2411
+ mask: "0000 0000 0000 0000",
2412
+ placeholder: "0000 0000 0000 0000",
2413
+ value: payForm.cardNo,
2414
+ preventPaste: true,
2415
+ onChange: (e) => {
2416
+ setPayForm({ ...payForm, cardNo: e });
2417
+ if (payErrors.cardNo)
2418
+ setPayErrors((er) => ({ ...er, cardNo: "" }));
2419
+ cardNumberInputHandler(e);
2420
+ },
2421
+ validationError: payErrors.cardNo ?? ""
2422
+ }
2423
+ ) }),
2424
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2425
+ BaseInput,
2426
+ {
2427
+ label: "Expiry Date",
2428
+ required: true,
2429
+ value: payForm.expireDate,
2430
+ mask: "00/00",
2431
+ placeholder: "00/00",
2432
+ onChange: (e) => {
2433
+ setPayForm({ ...payForm, expireDate: e });
2434
+ if (payErrors.expireDate)
2435
+ setPayErrors((er) => ({ ...er, expireDate: "" }));
2436
+ },
2437
+ validationError: payErrors.expireDate ?? ""
2438
+ }
2439
+ ),
2440
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2441
+ BaseInput,
2442
+ {
2443
+ label: "CVV",
2444
+ required: true,
2445
+ rules: ["numeric"],
2446
+ value: payForm.cvv,
2447
+ mask: "000",
2448
+ placeholder: "000",
2449
+ onChange: (e) => {
2450
+ setPayForm({ ...payForm, cvv: e });
2451
+ if (payErrors.cvv) setPayErrors((er) => ({ ...er, cvv: "" }));
2452
+ },
2453
+ validationError: payErrors.cvv ?? ""
2454
+ }
2455
+ ),
2456
+ cardType === "Verve" && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2457
+ BaseInput,
2458
+ {
2459
+ label: "Card Pin",
2460
+ required: true,
2461
+ rules: ["numeric"],
2462
+ value: payForm.cardPin,
2463
+ mask: "0000",
2464
+ placeholder: "0000",
2465
+ onChange: (e) => {
2466
+ setPayForm({ ...payForm, cardPin: e });
2467
+ if (payErrors.cardPin)
2468
+ setPayErrors((er) => ({ ...er, cardPin: "" }));
2469
+ },
2470
+ validationError: payErrors.cardPin ?? ""
2471
+ }
2472
+ )
1509
2473
  ]
1510
2474
  }
1511
2475
  ),
1512
2476
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1513
2477
  BaseButton,
1514
2478
  {
1515
- label: formIndex === 0 ? "Proceed" : "Pay",
2479
+ label: formIndex === 0 ? "Proceed" : `Pay ${formatAmountHandler}`,
1516
2480
  type: "primary",
1517
2481
  customClass: "w-full",
1518
- onClick: formIndex === 0 ? () => setFormIndex(1) : void 0
2482
+ loading: isMakingPayment,
2483
+ onClick: proceedHandler,
2484
+ disabled: isMakingPayment
1519
2485
  }
1520
2486
  )
1521
2487
  ] });
@@ -1525,92 +2491,302 @@ function PayByCard({}) {
1525
2491
  var import_react9 = require("react");
1526
2492
  var import_jsx_runtime28 = require("react/jsx-runtime");
1527
2493
  var PayByTransfer = ({
1528
- amountDisplay = "NGN 200,500.00",
1529
- bankName = "Teerus MFB",
1530
- accountNumber = "0001928940",
1531
- initialSeconds = 30 * 60,
1532
- onConfirmPaid,
1533
- onCancel
2494
+ secretKey,
2495
+ paymentObject,
2496
+ environment = "sandbox",
2497
+ onPaymentAuthorized,
2498
+ onCancel,
2499
+ onError
1534
2500
  }) => {
1535
- const [remaining, setRemaining] = (0, import_react9.useState)(initialSeconds);
2501
+ const [formIndex, setFormIndex] = (0, import_react9.useState)(0);
2502
+ const [isMakingPayment, setIsMakingPayment] = (0, import_react9.useState)(false);
2503
+ const [isConfirmCall, setIsConfirmCall] = (0, import_react9.useState)(false);
2504
+ const [isPaymentConfirmed, setIsPaymentConfirmed] = (0, import_react9.useState)(false);
2505
+ const [transactionReference, setTransactionReference] = (0, import_react9.useState)("");
2506
+ const [paymentReferenceStatus, setPaymentReferenceStatus] = (0, import_react9.useState)("");
2507
+ const [isFetchingPaymentDetails, setIsFetchingPaymentDetails] = (0, import_react9.useState)(false);
2508
+ const [paymentReferenceDetails, setPaymentReferenceDetails] = (0, import_react9.useState)(null);
2509
+ const [paymentAccountDetails, setPaymentAccountDetails] = (0, import_react9.useState)(null);
2510
+ const [paymentMade, setPaymentMade] = (0, import_react9.useState)(false);
2511
+ const [loading, setLoading] = (0, import_react9.useState)(false);
2512
+ const [message, setMessage] = (0, import_react9.useState)("");
2513
+ const [remainingSeconds, setRemainingSeconds] = (0, import_react9.useState)(60);
2514
+ const [countDownTime, setCountDownTime] = (0, import_react9.useState)("00:00");
1536
2515
  const intervalRef = (0, import_react9.useRef)(null);
1537
- const countDownTime = (0, import_react9.useMemo)(() => {
1538
- const clamped = Math.max(0, remaining);
1539
- const m = Math.floor(clamped / 60);
1540
- const s = clamped % 60;
1541
- return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
1542
- }, [remaining]);
1543
- (0, import_react9.useEffect)(() => {
1544
- intervalRef.current = window.setInterval(() => {
1545
- setRemaining((prev) => prev - 1);
1546
- }, 1e3);
1547
- return () => {
1548
- if (intervalRef.current) {
1549
- clearInterval(intervalRef.current);
1550
- intervalRef.current = null;
2516
+ const [transferForm, setTransferForm] = (0, import_react9.useState)({
2517
+ customerName: ""
2518
+ });
2519
+ const [transferErrors, setTransferErrors] = (0, import_react9.useState)(
2520
+ {}
2521
+ );
2522
+ const transferRules = {
2523
+ customerName: "required"
2524
+ };
2525
+ const transferLabels = {
2526
+ customerName: "Customer Name"
2527
+ };
2528
+ const formatAmountHandler = formatAmount(
2529
+ paymentObject.amount,
2530
+ paymentObject.currency
2531
+ );
2532
+ const payHandler = async () => {
2533
+ if (formIndex === 0) {
2534
+ const errs = validateGroup(transferForm, transferRules, transferLabels);
2535
+ setTransferErrors(errs);
2536
+ if (Object.keys(errs).length === 0) {
2537
+ const payload = {
2538
+ paymentReference: transactionReference,
2539
+ channel: "virtual_account",
2540
+ customerName: transferForm.customerName ?? "",
2541
+ merchantId: secretKey
2542
+ };
2543
+ setIsMakingPayment(true);
2544
+ try {
2545
+ const response = await generatePaymentAccount(environment, payload);
2546
+ if (response?.isSuccessful) {
2547
+ setPaymentAccountDetails(response.data);
2548
+ startTimer();
2549
+ setMessage("Virtual account generated successfully for payment.");
2550
+ setIsMakingPayment(true);
2551
+ setFormIndex(1);
2552
+ }
2553
+ } catch (err) {
2554
+ setIsMakingPayment(false);
2555
+ setMessage(err.error.responseMessage || err.error.message);
2556
+ onError?.({
2557
+ errorMessage: message
2558
+ });
2559
+ }
1551
2560
  }
1552
- };
2561
+ return;
2562
+ }
2563
+ };
2564
+ const getReferenceDetails = async () => {
2565
+ setIsFetchingPaymentDetails(true);
2566
+ try {
2567
+ const response = await getPaymentReferenceDetails(
2568
+ environment,
2569
+ transactionReference
2570
+ );
2571
+ if (response?.isSuccessful) {
2572
+ setPaymentReferenceDetails(response.data);
2573
+ const made = response.data?.paymentStatus === "Payment Received";
2574
+ setPaymentMade(made);
2575
+ const noServerStatus = response.data?.finalTransactionStatus == null || response.data?.paymentStatus == null;
2576
+ if (noServerStatus) {
2577
+ if (isConfirmCall) {
2578
+ setMessage("Transaction not confirmed !!");
2579
+ }
2580
+ if (!isPaymentConfirmed && !made) {
2581
+ setPaymentReferenceStatus("pending");
2582
+ onPaymentAuthorized?.({
2583
+ paymentId: transactionReference,
2584
+ paymentDate: response.data?.updatedAt,
2585
+ paymentStatus: "pending",
2586
+ message
2587
+ });
2588
+ } else {
2589
+ setPaymentReferenceStatus("confirmed");
2590
+ onPaymentAuthorized?.({
2591
+ paymentId: transactionReference,
2592
+ paymentDate: response.data?.updatedAt,
2593
+ paymentStatus: "confirmed",
2594
+ message
2595
+ });
2596
+ }
2597
+ } else if (response.data?.finalTransactionStatus === "Success" || response.data?.paymentStatus === "Received" || response.data?.paymentStatus === "Payment Received") {
2598
+ setPaymentReferenceStatus("confirmed");
2599
+ setIsPaymentConfirmed(true);
2600
+ setMessage("Transaction confirmed !!");
2601
+ onPaymentAuthorized?.({
2602
+ paymentId: transactionReference,
2603
+ paymentDate: response.data?.updatedAt,
2604
+ paymentStatus: "confirmed",
2605
+ message
2606
+ });
2607
+ }
2608
+ } else if (!response?.isSuccessful && response?.responseCode === "119") {
2609
+ setPaymentReferenceStatus("used");
2610
+ setMessage(response.responseMessage || "");
2611
+ onPaymentAuthorized?.({
2612
+ paymentId: transactionReference,
2613
+ paymentDate: null,
2614
+ paymentStatus: "used",
2615
+ message
2616
+ });
2617
+ }
2618
+ } catch (err) {
2619
+ setPaymentReferenceStatus("");
2620
+ setMessage(
2621
+ err?.error?.responseMessage || err?.error?.message || "Something went wrong"
2622
+ );
2623
+ onError?.({
2624
+ errorMessage: message
2625
+ });
2626
+ } finally {
2627
+ setIsFetchingPaymentDetails(false);
2628
+ }
2629
+ };
2630
+ const generatePaymentLinkHandler = async () => {
2631
+ if (!secretKey) {
2632
+ setMessage("Secret key is required.");
2633
+ return;
2634
+ }
2635
+ if (!checkObjectTruthy(paymentObject)) {
2636
+ setMessage("Kindly ensure you are passing all the required data.");
2637
+ return;
2638
+ }
2639
+ setLoading(true);
2640
+ setMessage("");
2641
+ try {
2642
+ const response = await createPaymentLink(
2643
+ paymentObject,
2644
+ environment,
2645
+ secretKey
2646
+ );
2647
+ if (response?.isSuccessful && response.launchUrl) {
2648
+ const queryParams = getQueryParams(response.launchUrl);
2649
+ setTransactionReference(queryParams["paymentReference"]);
2650
+ setMessage("Payment link created successfully");
2651
+ } else {
2652
+ setMessage("Failed to create payment link");
2653
+ }
2654
+ } catch (e) {
2655
+ setMessage(e?.message || "Failed to create payment link");
2656
+ onError?.({
2657
+ errorMessage: message
2658
+ });
2659
+ } finally {
2660
+ setLoading(false);
2661
+ }
2662
+ };
2663
+ const updateDisplay = (0, import_react9.useCallback)((secs) => {
2664
+ const minutes = String(Math.floor(secs / 60)).padStart(2, "0");
2665
+ const seconds = String(secs % 60).padStart(2, "0");
2666
+ setCountDownTime(`${minutes}:${seconds}`);
1553
2667
  }, []);
1554
- (0, import_react9.useEffect)(() => {
1555
- if (remaining < 0 && intervalRef.current) {
2668
+ const startTimer = (0, import_react9.useCallback)(() => {
2669
+ if (intervalRef.current) {
2670
+ clearInterval(intervalRef.current);
2671
+ intervalRef.current = null;
2672
+ }
2673
+ updateDisplay(remainingSeconds);
2674
+ intervalRef.current = setInterval(() => {
2675
+ setRemainingSeconds((prev) => {
2676
+ const next = prev - 1;
2677
+ if (next < 0) {
2678
+ if (intervalRef.current) {
2679
+ clearInterval(intervalRef.current);
2680
+ intervalRef.current = null;
2681
+ }
2682
+ setCountDownTime("00:00");
2683
+ return 0;
2684
+ }
2685
+ updateDisplay(next);
2686
+ return next;
2687
+ });
2688
+ }, 1e3);
2689
+ }, [remainingSeconds, updateDisplay]);
2690
+ const stopTimer = () => {
2691
+ if (intervalRef.current) {
1556
2692
  clearInterval(intervalRef.current);
1557
2693
  intervalRef.current = null;
1558
2694
  }
1559
- }, [remaining]);
2695
+ };
2696
+ (0, import_react9.useEffect)(() => {
2697
+ (async () => {
2698
+ await generatePaymentLinkHandler();
2699
+ })();
2700
+ }, []);
2701
+ (0, import_react9.useEffect)(() => {
2702
+ if (remainingSeconds < 0 && intervalRef.current) {
2703
+ stopTimer();
2704
+ }
2705
+ }, [remainingSeconds]);
1560
2706
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-10", children: [
1561
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { className: "text-sub-copy text-sm font-semibold text-center", children: [
1562
- "Amount to Pay ",
1563
- amountDisplay
2707
+ formIndex === 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-10", children: [
2708
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2709
+ BaseInput,
2710
+ {
2711
+ label: "Customer Name",
2712
+ required: true,
2713
+ value: transferForm.customerName,
2714
+ onChange: (e) => {
2715
+ setTransferForm({ ...transferForm, customerName: e });
2716
+ if (transferForm.customerName) {
2717
+ setTransferErrors((er) => ({ ...er, customerName: "" }));
2718
+ }
2719
+ },
2720
+ validationError: transferErrors.customerName ?? ""
2721
+ }
2722
+ ),
2723
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2724
+ BaseButton,
2725
+ {
2726
+ label: `Pay ${formatAmountHandler}`,
2727
+ type: "primary",
2728
+ customClass: "w-full",
2729
+ loading: isMakingPayment,
2730
+ onClick: payHandler
2731
+ }
2732
+ )
1564
2733
  ] }),
1565
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "bg-[#EFF7FF] p-4 rounded-lg flex flex-col gap-6", children: [
1566
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(BaseLabelInfo, { label: "Bank Name", value: bankName, type: "horizontal" }),
1567
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between", children: [
2734
+ formIndex === 1 && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-10", children: [
2735
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "bg-[#EFF7FF] p-4 rounded-lg flex flex-col gap-6", children: [
1568
2736
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1569
2737
  BaseLabelInfo,
1570
2738
  {
1571
- label: "Account Number",
1572
- value: accountNumber,
1573
- type: "horizontal"
2739
+ label: "Bank Name",
2740
+ value: `${paymentAccountDetails?.bank} ${paymentAccountDetails?.accountName}`
1574
2741
  }
1575
2742
  ),
1576
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(BaseCopy, { color: "#9DBFDE", copyText: accountNumber })
2743
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between", children: [
2744
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2745
+ BaseLabelInfo,
2746
+ {
2747
+ label: "Account Number",
2748
+ value: paymentAccountDetails?.accountNumber
2749
+ }
2750
+ ),
2751
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2752
+ BaseCopy,
2753
+ {
2754
+ color: "#9DBFDE",
2755
+ copyText: paymentAccountDetails?.accountNumber ?? ""
2756
+ }
2757
+ )
2758
+ ] }),
2759
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between", children: [
2760
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(BaseLabelInfo, { label: "Amount", value: formatAmountHandler }),
2761
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(BaseCopy, { color: "#9DBFDE", copyText: formatAmountHandler })
2762
+ ] })
2763
+ ] }),
2764
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { className: "w-2/3 mx-auto text-center text-body-2xs font-medium text-sub-copy", children: [
2765
+ "This account is for this transaction only and expires in",
2766
+ " ",
2767
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-orange-500", children: remainingSeconds >= 0 ? countDownTime : "00:00" })
1577
2768
  ] }),
1578
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between", children: [
2769
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-4", children: [
1579
2770
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1580
- BaseLabelInfo,
2771
+ BaseButton,
1581
2772
  {
1582
- label: "Amount",
1583
- value: amountDisplay,
1584
- type: "horizontal"
2773
+ label: "I have paid the money",
2774
+ type: "primary",
2775
+ customClass: "w-full",
2776
+ loading: isFetchingPaymentDetails,
2777
+ onClick: getReferenceDetails
1585
2778
  }
1586
2779
  ),
1587
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(BaseCopy, { color: "#9DBFDE", copyText: amountDisplay })
2780
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2781
+ "button",
2782
+ {
2783
+ type: "button",
2784
+ onClick: onCancel,
2785
+ className: "text-heading-text text-body-2xs font-medium text-center py-2 cursor-pointer",
2786
+ children: "Cancel Payment"
2787
+ }
2788
+ )
1588
2789
  ] })
1589
- ] }),
1590
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { className: "w-2/3 mx-auto text-center text-body-2xs font-medium text-sub-copy", children: [
1591
- "This account is for this transaction only and expires in",
1592
- " ",
1593
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-orange-500", children: remaining >= 0 ? countDownTime : "00:00" })
1594
- ] }),
1595
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-4", children: [
1596
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1597
- BaseButton,
1598
- {
1599
- label: "I have paid the money",
1600
- type: "primary",
1601
- customClass: "w-full",
1602
- onClick: onConfirmPaid
1603
- }
1604
- ),
1605
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1606
- "button",
1607
- {
1608
- type: "button",
1609
- onClick: onCancel,
1610
- className: "text-heading-text text-body-2xs font-medium text-center py-2 cursor-pointer",
1611
- children: "Cancel Payment"
1612
- }
1613
- )
1614
2790
  ] })
1615
2791
  ] });
1616
2792
  };
@@ -1618,12 +2794,219 @@ var PayByTransfer = ({
1618
2794
  // src/components/pay-by-stable-coin.tsx
1619
2795
  var import_react10 = require("react");
1620
2796
  var import_jsx_runtime29 = require("react/jsx-runtime");
1621
- var PayByStableCoin = ({ onProceedToPay }) => {
2797
+ var PayByStableCoin = ({
2798
+ secretKey,
2799
+ paymentObject,
2800
+ environment = "sandbox",
2801
+ onPaymentAuthorized,
2802
+ onError
2803
+ }) => {
1622
2804
  const [formIndex, setFormIndex] = (0, import_react10.useState)(0);
1623
- const payHandler = () => {
1624
- setFormIndex(1);
1625
- onProceedToPay?.();
2805
+ const [message, setMessage] = (0, import_react10.useState)("");
2806
+ const [loading, setLoading] = (0, import_react10.useState)(false);
2807
+ const [generatingAddress, setGeneratingAddress] = (0, import_react10.useState)(false);
2808
+ const [loadingStableCoins, setLoadingStableCoins] = (0, import_react10.useState)(false);
2809
+ const [loadingStableCoinNetworks, setLoadingStableCoinNetworks] = (0, import_react10.useState)(false);
2810
+ const [transactionReference, setTransactionReference] = (0, import_react10.useState)("");
2811
+ const [stableCoins, setStableCoins] = (0, import_react10.useState)([]);
2812
+ const [networkList, setNetworkList] = (0, import_react10.useState)([]);
2813
+ const [addressDetails, setAddressDetails] = (0, import_react10.useState)(null);
2814
+ const [generateAddressPayload, setGenerateAddressPayload] = (0, import_react10.useState)(null);
2815
+ const [isConfirmingPayment, setIsConfirmingPayment] = (0, import_react10.useState)(false);
2816
+ const [paymentReferenceDetails, setPaymentReferenceDetails] = (0, import_react10.useState)(null);
2817
+ const [paymentReferenceStatus, setPaymentReferenceStatus] = (0, import_react10.useState)("");
2818
+ const [stableCoinForm, setStableCoinForm] = (0, import_react10.useState)({
2819
+ stableCoin: "",
2820
+ network: ""
2821
+ });
2822
+ const [stableCoinErrors, setStableCoinErrors] = (0, import_react10.useState)({});
2823
+ const stableCoinRules = {
2824
+ stableCoin: "required",
2825
+ network: "required"
2826
+ };
2827
+ const stableCoinLabels = {
2828
+ stableCoin: "Stable Coin",
2829
+ network: "Network"
2830
+ };
2831
+ const generatePaymentLinkHandler = async () => {
2832
+ if (!secretKey) {
2833
+ setMessage("Secret key is required.");
2834
+ return;
2835
+ }
2836
+ if (!checkObjectTruthy(paymentObject)) {
2837
+ setMessage("Kindly ensure you are passing all the required data.");
2838
+ return;
2839
+ }
2840
+ setLoading(true);
2841
+ setMessage("");
2842
+ try {
2843
+ const response = await createPaymentLink(
2844
+ paymentObject,
2845
+ environment,
2846
+ secretKey
2847
+ );
2848
+ if (response?.isSuccessful && response.launchUrl) {
2849
+ const queryParams = getQueryParams(response.launchUrl);
2850
+ setTransactionReference(queryParams["paymentReference"]);
2851
+ setMessage("Payment link created successfully");
2852
+ } else {
2853
+ setMessage("Failed to create payment link");
2854
+ }
2855
+ } catch (e) {
2856
+ setMessage(e?.message || "Failed to create payment link");
2857
+ onError?.({
2858
+ errorMessage: message
2859
+ });
2860
+ } finally {
2861
+ setLoading(false);
2862
+ }
2863
+ };
2864
+ const generateAddress = async (payload) => {
2865
+ if (!payload) return;
2866
+ setGeneratingAddress(true);
2867
+ try {
2868
+ const response = await generateStableCoinAddress(
2869
+ environment,
2870
+ payload
2871
+ );
2872
+ if (response?.isSuccessful) {
2873
+ setAddressDetails(response.data);
2874
+ setFormIndex(1);
2875
+ }
2876
+ } catch (e) {
2877
+ setMessage(e?.message || "Failed to generate address");
2878
+ onError?.({
2879
+ errorMessage: message
2880
+ });
2881
+ } finally {
2882
+ setGeneratingAddress(false);
2883
+ }
2884
+ };
2885
+ const getAllStableCoins = async () => {
2886
+ setLoadingStableCoins(true);
2887
+ try {
2888
+ const response = await getStableCoins(environment);
2889
+ if (response?.isSuccessful) {
2890
+ setStableCoins(
2891
+ response.data?.map((c) => ({
2892
+ label: c.name,
2893
+ value: c.name
2894
+ })) ?? []
2895
+ );
2896
+ }
2897
+ } catch (e) {
2898
+ setMessage(e?.message || "Failed to get stable coins");
2899
+ onError?.({
2900
+ errorMessage: message
2901
+ });
2902
+ } finally {
2903
+ setLoadingStableCoins(false);
2904
+ }
2905
+ };
2906
+ const getAllStableCoinNetworks = async (stableCoin) => {
2907
+ setLoadingStableCoinNetworks(true);
2908
+ try {
2909
+ const response = await getStableCoinNetworks(
2910
+ environment,
2911
+ stableCoin
2912
+ );
2913
+ if (response?.isSuccessful) {
2914
+ setNetworkList(
2915
+ response.networks?.map((n) => ({
2916
+ label: n,
2917
+ value: n
2918
+ })) ?? []
2919
+ );
2920
+ }
2921
+ } catch (e) {
2922
+ setMessage(e?.message || "Failed to get stable coin networks");
2923
+ onError?.({
2924
+ errorMessage: message
2925
+ });
2926
+ } finally {
2927
+ setLoadingStableCoinNetworks(false);
2928
+ }
2929
+ };
2930
+ const confirmPaymentHandler = async () => {
2931
+ setIsConfirmingPayment(true);
2932
+ try {
2933
+ const response = await getPaymentReferenceDetails(environment, transactionReference);
2934
+ if (response?.isSuccessful) {
2935
+ setPaymentReferenceDetails(response.data);
2936
+ const needsConfirm = response.data?.finalTransactionStatus == null || response.data?.paymentStatus == null;
2937
+ if (needsConfirm) {
2938
+ setMessage("Transaction not confirmed !!");
2939
+ setPaymentReferenceStatus("pending");
2940
+ onPaymentAuthorized?.({
2941
+ paymentId: transactionReference,
2942
+ paymentDate: response.data?.updatedAt ?? "",
2943
+ paymentStatus: "pending",
2944
+ message
2945
+ });
2946
+ } else if (response.data?.finalTransactionStatus === "Success" || response.data?.paymentStatus === "Payment Received") {
2947
+ setMessage("Transaction confirmed !!");
2948
+ setPaymentReferenceStatus("confirmed");
2949
+ onPaymentAuthorized?.({
2950
+ paymentId: transactionReference,
2951
+ paymentDate: response.data?.updatedAt,
2952
+ paymentStatus: "confirmed",
2953
+ message
2954
+ });
2955
+ }
2956
+ } else if (!response?.isSuccessful && response?.responseCode === "119") {
2957
+ setPaymentReferenceStatus("used");
2958
+ setMessage(response.responseMessage || "");
2959
+ onPaymentAuthorized?.({
2960
+ paymentId: transactionReference,
2961
+ paymentDate: null,
2962
+ paymentStatus: "used",
2963
+ message
2964
+ });
2965
+ }
2966
+ } catch (err) {
2967
+ setPaymentReferenceStatus("");
2968
+ setMessage(
2969
+ err?.error?.responseMessage || err?.error?.message || "Something went wrong"
2970
+ );
2971
+ onError?.({
2972
+ errorMessage: message
2973
+ });
2974
+ } finally {
2975
+ setIsConfirmingPayment(false);
2976
+ }
2977
+ };
2978
+ const formatAmountHandler = formatAmount(
2979
+ paymentObject.amount,
2980
+ paymentObject.currency
2981
+ );
2982
+ const payHandler = async () => {
2983
+ const errs = validateGroup(
2984
+ stableCoinForm,
2985
+ stableCoinRules,
2986
+ stableCoinLabels
2987
+ );
2988
+ setStableCoinErrors(errs);
2989
+ if (Object.keys(errs).length === 0) {
2990
+ const payload = {
2991
+ paymentReference: transactionReference,
2992
+ currency: stableCoinForm.stableCoin,
2993
+ chain: stableCoinForm.network,
2994
+ transactionAmount: paymentObject.amount,
2995
+ merchantId: secretKey
2996
+ };
2997
+ setGenerateAddressPayload(payload);
2998
+ await generateAddress(payload);
2999
+ } else {
3000
+ return;
3001
+ }
1626
3002
  };
3003
+ const amountPlusNetworkFee = addressDetails ? Number(addressDetails.transactionAmount) + addressDetails.networkFee : 0;
3004
+ (0, import_react10.useEffect)(() => {
3005
+ (async () => {
3006
+ await generatePaymentLinkHandler();
3007
+ await getAllStableCoins();
3008
+ })();
3009
+ }, []);
1627
3010
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-6", children: [
1628
3011
  formIndex === 0 && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1629
3012
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "grid grid-cols-1 gap-6", children: [
@@ -1632,11 +3015,17 @@ var PayByStableCoin = ({ onProceedToPay }) => {
1632
3015
  {
1633
3016
  label: "Select Crypto",
1634
3017
  required: true,
1635
- options: [
1636
- { label: "USDT", value: "USDT" },
1637
- { label: "USDC", value: "USDC" },
1638
- { label: "BUSD", value: "BUSD" }
1639
- ]
3018
+ options: stableCoins,
3019
+ loading: loadingStableCoins,
3020
+ value: stableCoinForm.stableCoin,
3021
+ onChange: (e) => {
3022
+ setStableCoinForm({ ...stableCoinForm, stableCoin: e });
3023
+ getAllStableCoinNetworks(e);
3024
+ if (stableCoinErrors.stableCoin) {
3025
+ setStableCoinErrors((er) => ({ ...er, stableCoin: "" }));
3026
+ }
3027
+ },
3028
+ validationError: stableCoinErrors.stableCoin ?? ""
1640
3029
  }
1641
3030
  ),
1642
3031
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
@@ -1644,52 +3033,46 @@ var PayByStableCoin = ({ onProceedToPay }) => {
1644
3033
  {
1645
3034
  label: "Select Network",
1646
3035
  required: true,
1647
- options: [
1648
- { label: "Ethereum", value: "ETH" },
1649
- { label: "Binance Smart Chain", value: "BSC" },
1650
- { label: "Polygon", value: "MATIC" }
1651
- ]
3036
+ options: networkList,
3037
+ loading: loadingStableCoinNetworks,
3038
+ value: stableCoinForm.network,
3039
+ onChange: (e) => {
3040
+ setStableCoinForm({ ...stableCoinForm, network: e });
3041
+ if (stableCoinErrors.network) {
3042
+ setStableCoinErrors((er) => ({ ...er, network: "" }));
3043
+ }
3044
+ },
3045
+ validationError: stableCoinErrors.network ?? ""
1652
3046
  }
1653
3047
  )
1654
3048
  ] }),
1655
3049
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1656
3050
  BaseButton,
1657
3051
  {
1658
- label: "Pay",
3052
+ label: `Pay ${formatAmountHandler}`,
1659
3053
  type: "primary",
1660
3054
  customClass: "w-full",
3055
+ loading: generatingAddress,
1661
3056
  onClick: payHandler
1662
3057
  }
1663
3058
  )
1664
3059
  ] }),
1665
3060
  formIndex === 1 && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-6", children: [
1666
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "mx-auto", children: [
1667
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1668
- BaseImage,
1669
- {
1670
- src: "../../../assets/images/stable-coin-qr-code.png",
1671
- alt: "QR Code",
1672
- width: 122,
1673
- height: 122,
1674
- customClass: "mb-1"
1675
- }
1676
- ),
1677
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-4xs text-light-copy font-normal text-center", children: "USDC" })
1678
- ] }),
3061
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "mx-auto", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-4xs text-light-copy font-normal text-center", children: generateAddressPayload?.currency }) }),
1679
3062
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-6 border-c border-grey-100 p-4 rounded-2xl bg-light-white-50", children: [
1680
3063
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "border-b border-grey-border pb-4 flex flex-col gap-2", children: [
1681
3064
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-3xs text-light-copy font-normal", children: "Network" }),
1682
3065
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex justify-between", children: [
1683
3066
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-1", children: [
1684
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-2xs font-medium text-sub-copy", children: "BNB Smart Chain (BEP20)" }),
3067
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-2xs font-medium text-sub-copy", children: addressDetails?.chain }),
1685
3068
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
1686
3069
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-3xs text-light-copy font-normal", children: "*Est. arrival = 3 mins" }),
1687
3070
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-3xs text-light-copy font-normal", children: "|" }),
1688
3071
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1689
3072
  BaseCurrencyAmount,
1690
3073
  {
1691
- currency: "USDC",
1692
- amount: 10,
3074
+ currency: generateAddressPayload?.currency ?? "",
3075
+ amount: addressDetails?.networkFee ?? 0,
1693
3076
  textClass: "mb-0 text-body-3xs text-light-copy font-normal",
1694
3077
  iconColorClass: "#557591",
1695
3078
  iconWidth: 12,
@@ -1704,8 +3087,8 @@ var PayByStableCoin = ({ onProceedToPay }) => {
1704
3087
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "pb-4 flex flex-col gap-2", children: [
1705
3088
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-3xs text-light-copy font-normal", children: "Deposit Address >" }),
1706
3089
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex justify-between", children: [
1707
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-2xs font-medium text-sub-copy w-2/3 break-words", children: "0j8938ysheeee8333c162883a4d4f5g6t111nhk8uey37777yt6" }),
1708
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(BaseCopy, { copyText: "0j8938ysheeee8333c162883a4d4f5g6t111nhk8uey37777yt6" })
3090
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mb-0 text-body-2xs font-medium text-sub-copy w-2/3 break-words", children: addressDetails?.walletAddress }),
3091
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(BaseCopy, { copyText: addressDetails?.walletAddress ?? "" })
1709
3092
  ] })
1710
3093
  ] })
1711
3094
  ] }),
@@ -1715,8 +3098,8 @@ var PayByStableCoin = ({ onProceedToPay }) => {
1715
3098
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1716
3099
  BaseCurrencyAmount,
1717
3100
  {
1718
- currency: "USDC",
1719
- amount: 12,
3101
+ currency: generateAddressPayload?.currency ?? "",
3102
+ amount: addressDetails?.networkFee ?? 0,
1720
3103
  textClass: "mb-0 text-body-2xs font-extrabold text-primary-black",
1721
3104
  iconColorClass: "#231F20"
1722
3105
  }
@@ -1727,8 +3110,8 @@ var PayByStableCoin = ({ onProceedToPay }) => {
1727
3110
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1728
3111
  BaseCurrencyAmount,
1729
3112
  {
1730
- currency: "USDC",
1731
- amount: 15,
3113
+ currency: generateAddressPayload?.currency ?? "",
3114
+ amount: amountPlusNetworkFee,
1732
3115
  textClass: "mb-0 text-body-lg font-extrabold text-primary-black",
1733
3116
  iconColorClass: "#231F20",
1734
3117
  iconWidth: 20,
@@ -1742,7 +3125,9 @@ var PayByStableCoin = ({ onProceedToPay }) => {
1742
3125
  {
1743
3126
  label: "Confirm Payment",
1744
3127
  type: "primary",
1745
- customClass: "w-full"
3128
+ customClass: "w-full",
3129
+ loading: isConfirmingPayment,
3130
+ onClick: confirmPaymentHandler
1746
3131
  }
1747
3132
  ) })
1748
3133
  ] })
@@ -1752,95 +3137,157 @@ var PayByStableCoin = ({ onProceedToPay }) => {
1752
3137
  // src/buzapay-checkout/checkout-card.tsx
1753
3138
  var import_jsx_runtime30 = require("react/jsx-runtime");
1754
3139
  function BzpCheckoutCard({
3140
+ secretKey,
1755
3141
  options,
1756
3142
  environment = "sandbox",
1757
3143
  paymentObject = {
3144
+ merchantName: "",
1758
3145
  amount: 0,
1759
3146
  currency: "",
1760
3147
  email: "",
1761
3148
  phoneNumber: "",
1762
3149
  narration: "",
1763
- redirectUrl: ""
1764
- }
3150
+ redirectUrl: "https://sandbox-merchant.buzapay.com/account/three-ds-status"
3151
+ },
3152
+ onPaymentAuthorized,
3153
+ onError
1765
3154
  }) {
1766
- const [checkoutState, setCheckoutState] = (0, import_react11.useState)("PAYMENT");
3155
+ const [checkoutState, setCheckoutState] = (0, import_react11.useState)("PENDING");
1767
3156
  const paymentTypeOptions = [
1768
3157
  { label: "Card", value: "CARD" },
1769
3158
  { label: "Bank Transfer", value: "BANK_TRANSFER" },
1770
3159
  { label: "Stable Coin", value: "STABLE_COIN" }
1771
3160
  ];
1772
3161
  const [filteredPaymentTypeOptions, setFilteredPaymentTypeOptions] = (0, import_react11.useState)([]);
1773
- const [paymentType, setPaymentType] = (0, import_react11.useState)(
1774
- filteredPaymentTypeOptions[0].value
1775
- );
3162
+ const [paymentType, setPaymentType] = (0, import_react11.useState)("");
3163
+ const [successObject, setSuccessObject] = (0, import_react11.useState)({
3164
+ paymentDate: "",
3165
+ paymentId: "",
3166
+ paymentStatus: ""
3167
+ });
1776
3168
  const paymentTypeHandler = (event) => {
1777
3169
  setPaymentType(event);
1778
3170
  };
3171
+ const setSuccess = (event) => {
3172
+ setSuccessObject({ ...event });
3173
+ if (event.paymentStatus === "Authorized") {
3174
+ setCheckoutState("SUCCESS");
3175
+ } else if (event.paymentStatus === "Payment failed") {
3176
+ setCheckoutState("PENDING");
3177
+ } else if (event.paymentStatus === "used") {
3178
+ setCheckoutState("USED");
3179
+ } else {
3180
+ setCheckoutState("PENDING");
3181
+ }
3182
+ onPaymentAuthorized?.(event);
3183
+ };
1779
3184
  (0, import_react11.useEffect)(() => {
1780
3185
  let options2 = [];
1781
3186
  if (paymentObject.currency === "USD") {
1782
- options2 = paymentTypeOptions.filter(
1783
- (option) => option.value !== "BANK_TRANSFER"
1784
- );
3187
+ if (paymentObject.amount < 50) {
3188
+ options2 = paymentTypeOptions.filter(
3189
+ (option) => option.value !== "BANK_TRANSFER" && option.value !== "STABLE_COIN"
3190
+ );
3191
+ } else {
3192
+ options2 = paymentTypeOptions.filter(
3193
+ (option) => option.value !== "BANK_TRANSFER"
3194
+ );
3195
+ }
1785
3196
  } else {
1786
3197
  options2 = paymentTypeOptions.filter(
1787
3198
  (option) => option.value !== "STABLE_COIN"
1788
3199
  );
1789
3200
  }
1790
3201
  setFilteredPaymentTypeOptions(options2);
1791
- }, [paymentObject.currency]);
1792
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(BaseCard, { showBackButton: checkoutState === "STABLE_COIN_PAYMENT", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "grid grid-cols-3", children: [
1793
- checkoutState === "PAYMENT" && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "bg-[#EFF7FF] px-6 py-8 flex flex-col gap-5 col-span-1 rounded-l-xl", children: [
3202
+ }, [paymentObject.currency, paymentObject.amount]);
3203
+ (0, import_react11.useEffect)(() => {
3204
+ if (filteredPaymentTypeOptions.length) {
3205
+ setPaymentType(filteredPaymentTypeOptions[0].value);
3206
+ }
3207
+ }, [filteredPaymentTypeOptions]);
3208
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(BaseCard, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "grid grid-cols-3", children: [
3209
+ checkoutState === "PENDING" && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "bg-[#EFF7FF] px-6 py-8 flex flex-col gap-5 col-span-1 rounded-l-xl", children: [
1794
3210
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-heading-text text-body-xs font-semibold", children: "Pay with" }),
1795
3211
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1796
3212
  BaseRadioGroup,
1797
3213
  {
1798
- options: paymentTypeOptions,
3214
+ options: filteredPaymentTypeOptions,
1799
3215
  selectedChange: (e) => paymentTypeHandler(e)
1800
3216
  }
1801
3217
  )
1802
3218
  ] }),
1803
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "col-span-2", children: [
1804
- (checkoutState === "PAYMENT" || checkoutState === "STABLE_COIN_PAYMENT") && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
1805
- checkoutState === "PAYMENT" && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center justify-between px-12 py-8", children: [
1806
- options?.imageUrl ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1807
- BaseImage,
3219
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3220
+ "div",
3221
+ {
3222
+ className: checkoutState === "SUCCESS" ? "col-span-3" : "col-span-2",
3223
+ children: [
3224
+ checkoutState === "PENDING" && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center justify-between px-12 py-8", children: [
3225
+ options?.imageUrl ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3226
+ BaseImage,
3227
+ {
3228
+ src: options?.imageUrl ?? "",
3229
+ alt: "Merchant Logo",
3230
+ width: 52,
3231
+ height: 52,
3232
+ customClass: "rounded-lg"
3233
+ }
3234
+ ) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3235
+ "div",
3236
+ {
3237
+ className: "bg-heading-text rounded flex flex-col justify-center",
3238
+ style: { width: "52px", height: "52px" },
3239
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-white text-center text-body-2xs font-medium", children: "Logo" })
3240
+ }
3241
+ ),
3242
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
3243
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-body-2xs font-regular text-sub-copy text-right", children: paymentObject.merchantName }),
3244
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: "text-body-2xs font-regular text-sub-copy text-right", children: [
3245
+ "Pay:",
3246
+ " ",
3247
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-orange-500 font-extrabold", children: formatAmount(paymentObject.amount, paymentObject.currency) })
3248
+ ] })
3249
+ ] })
3250
+ ] }),
3251
+ checkoutState === "PENDING" && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "overflow-y-scroll px-10 pb-10 pt-2", children: paymentType === "CARD" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3252
+ PayByCard,
3253
+ {
3254
+ secretKey,
3255
+ environment,
3256
+ paymentObject,
3257
+ onPaymentAuthorized: setSuccess,
3258
+ onError
3259
+ }
3260
+ ) : paymentType === "BANK_TRANSFER" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3261
+ PayByTransfer,
1808
3262
  {
1809
- src: options?.imageUrl ?? "",
1810
- alt: "Merchant Logo",
1811
- width: 52,
1812
- height: 52,
1813
- customClass: "rounded-lg"
3263
+ secretKey,
3264
+ environment,
3265
+ paymentObject,
3266
+ onPaymentAuthorized: setSuccess,
3267
+ onError
1814
3268
  }
1815
3269
  ) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1816
- "div",
3270
+ PayByStableCoin,
1817
3271
  {
1818
- className: "bg-heading-text rounded flex flex-col justify-center",
1819
- style: { width: "52px", height: "52px" },
1820
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-white text-center text-body-2xs font-medium", children: "Logo" })
3272
+ secretKey,
3273
+ environment,
3274
+ paymentObject,
3275
+ onPaymentAuthorized: setSuccess,
3276
+ onError
1821
3277
  }
1822
- ),
1823
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
1824
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-body-2xs font-regular text-sub-copy text-right", children: "Raymahni Merchant LLC" }),
1825
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: "text-body-2xs font-regular text-sub-copy text-right", children: [
1826
- "Pay:",
1827
- " ",
1828
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "text-orange-500 font-extrabold", children: [
1829
- currencySign(paymentObject.currency),
1830
- " 100,051.00"
1831
- ] })
1832
- ] })
1833
- ] })
1834
- ] }),
1835
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "overflow-y-scroll px-10 pb-10 pt-2", children: paymentType === "CARD" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(PayByCard, {}) : paymentType === "BANK_TRANSFER" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(PayByTransfer, {}) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1836
- PayByStableCoin,
1837
- {
1838
- onProceedToPay: () => setCheckoutState("STABLE_COIN_PAYMENT")
1839
- }
1840
- ) })
1841
- ] }),
1842
- checkoutState === "SUCCESS" && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(BaseSuccess, {})
1843
- ] })
3278
+ ) }),
3279
+ checkoutState === "SUCCESS" && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3280
+ BaseSuccess,
3281
+ {
3282
+ amount: paymentObject.amount,
3283
+ currency: paymentObject.currency,
3284
+ redirectUrl: paymentObject.redirectUrl ?? "",
3285
+ successObject
3286
+ }
3287
+ )
3288
+ ]
3289
+ }
3290
+ )
1844
3291
  ] }) });
1845
3292
  }
1846
3293
  // Annotate the CommonJS export names for ESM import in node: