@reevit/react 0.6.0 → 0.8.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.mjs CHANGED
@@ -509,8 +509,9 @@ function mapToPaymentIntent(response, config) {
509
509
  status: response.status,
510
510
  recommendedPsp: mapProviderToPsp(response.provider),
511
511
  availableMethods: config.paymentMethods || ["card", "mobile_money"],
512
- reference: response.reference || response.id,
513
- // Use backend reference or fallback to ID
512
+ providerRefId: response.provider_ref_id,
513
+ reference: response.reference || response.provider_ref_id || response.id,
514
+ // Use backend reference or fallback to provider ref then ID
514
515
  orgId: response.org_id,
515
516
  connectionId: response.connection_id,
516
517
  provider: response.provider,
@@ -811,30 +812,47 @@ function detectCountryFromCurrency(currency) {
811
812
  };
812
813
  return currencyToCountry[currency.toUpperCase()] || "GH";
813
814
  }
815
+ var MethodIcons = {
816
+ card: () => /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
817
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "4", width: "22", height: "16", rx: "3" }),
818
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "10", x2: "23", y2: "10" }),
819
+ /* @__PURE__ */ jsx("line", { x1: "5", y1: "15", x2: "9", y2: "15" })
820
+ ] }),
821
+ mobile_money: () => /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
822
+ /* @__PURE__ */ jsx("rect", { x: "5", y: "2", width: "14", height: "20", rx: "3" }),
823
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "18", x2: "12", y2: "18.01", strokeWidth: "2", strokeLinecap: "round" })
824
+ ] }),
825
+ bank_transfer: () => /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
826
+ /* @__PURE__ */ jsx("path", { d: "M3 21h18" }),
827
+ /* @__PURE__ */ jsx("path", { d: "M3 10h18" }),
828
+ /* @__PURE__ */ jsx("path", { d: "M12 3l9 7H3l9-7z" }),
829
+ /* @__PURE__ */ jsx("path", { d: "M6 10v8" }),
830
+ /* @__PURE__ */ jsx("path", { d: "M10 10v8" }),
831
+ /* @__PURE__ */ jsx("path", { d: "M14 10v8" }),
832
+ /* @__PURE__ */ jsx("path", { d: "M18 10v8" })
833
+ ] }),
834
+ apple_pay: () => /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.53 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" }) }),
835
+ google_pay: () => /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z", fill: "currentColor" }) })
836
+ };
814
837
  var methodConfig = {
815
838
  card: {
816
839
  label: "Card",
817
- icon: "\u{1F4B3}",
818
840
  description: "Pay with Visa, Mastercard, or other cards"
819
841
  },
820
842
  mobile_money: {
821
843
  label: "Mobile Money",
822
- icon: "\u{1F4F1}",
823
844
  description: "MTN, Telecel, AirtelTigo Money"
824
845
  },
825
846
  bank_transfer: {
826
847
  label: "Bank Transfer",
827
- icon: "\u{1F3E6}",
828
848
  description: "Pay directly from your bank account"
829
849
  },
830
850
  apple_pay: {
831
851
  label: "Apple Pay",
832
- icon: "\u{1F34E}",
833
852
  description: "Pay with Apple Pay"
834
853
  },
835
854
  google_pay: {
836
855
  label: "Google Pay",
837
- icon: "\u{1F916}",
838
856
  description: "Pay with Google Pay"
839
857
  }
840
858
  };
@@ -884,7 +902,6 @@ function PaymentMethodSelector({
884
902
  ),
885
903
  style: selectedTheme?.backgroundColor ? { backgroundColor: selectedTheme.backgroundColor } : void 0,
886
904
  children: methods.map((method, index) => {
887
- const config = methodConfig[method];
888
905
  const isSelected = selectedMethod === method;
889
906
  const methodLabel = getMethodLabel(method);
890
907
  const methodDescription = getMethodDescription(method);
@@ -915,7 +932,7 @@ function PaymentMethodSelector({
915
932
  className: "reevit-method-option__logo-img"
916
933
  },
917
934
  i
918
- )) }) : /* @__PURE__ */ jsx("span", { className: "reevit-method-option__icon", children: config.icon }) }),
935
+ )) }) : /* @__PURE__ */ jsx("span", { className: "reevit-method-option__icon", children: MethodIcons[method]() }) }),
919
936
  /* @__PURE__ */ jsxs("div", { className: "reevit-method-option__content", children: [
920
937
  /* @__PURE__ */ jsx("span", { className: "reevit-method-option__label", style: selectedTheme?.textColor ? { color: selectedTheme.textColor } : void 0, children: methodLabel }),
921
938
  !isGrid && /* @__PURE__ */ jsx("span", { className: "reevit-method-option__description", style: selectedTheme?.descriptionColor ? { color: selectedTheme.descriptionColor } : void 0, children: methodDescription })
@@ -1315,6 +1332,38 @@ function PaystackBridge({
1315
1332
  /* @__PURE__ */ jsx("p", { children: "Connecting to Paystack..." })
1316
1333
  ] }) });
1317
1334
  }
1335
+ var DEFAULT_REEVIT_API_BASE_URL = "https://api.reevit.io";
1336
+ function getHubtelCallbackURL(apiBaseUrl) {
1337
+ return `${apiBaseUrl || DEFAULT_REEVIT_API_BASE_URL}/v1/webhooks/incoming/hubtel`;
1338
+ }
1339
+ function parseHubtelCallbackPayload(input) {
1340
+ if (!input || typeof input !== "object") {
1341
+ return {};
1342
+ }
1343
+ const raw = input;
1344
+ const nested = raw.data;
1345
+ if (typeof nested === "string") {
1346
+ try {
1347
+ const parsed = JSON.parse(nested);
1348
+ if (parsed && typeof parsed === "object") {
1349
+ return { ...raw, ...parsed };
1350
+ }
1351
+ } catch {
1352
+ }
1353
+ } else if (nested && typeof nested === "object") {
1354
+ return { ...raw, ...nested };
1355
+ }
1356
+ return raw;
1357
+ }
1358
+ function readHubtelField(payload, keys) {
1359
+ for (const key of keys) {
1360
+ const value = payload[key];
1361
+ if (typeof value === "string" && value.trim().length > 0) {
1362
+ return value.trim();
1363
+ }
1364
+ }
1365
+ return "";
1366
+ }
1318
1367
  function HubtelBridge({
1319
1368
  paymentId,
1320
1369
  publicKey,
@@ -1391,12 +1440,12 @@ function HubtelBridge({
1391
1440
  purchaseDescription: description,
1392
1441
  customerPhoneNumber: phone || "",
1393
1442
  ...email ? { customerEmail: email } : {},
1394
- clientReference: reference || `hubtel_${Date.now()}`,
1443
+ clientReference: reference || paymentId || `hubtel_${Date.now()}`,
1395
1444
  ...methodPreference ? { paymentMethod: methodPreference } : {}
1396
1445
  };
1397
1446
  const config = {
1398
1447
  branding: "enabled",
1399
- callbackUrl: callbackUrl || window.location.href,
1448
+ callbackUrl: callbackUrl || getHubtelCallbackURL(apiBaseUrl),
1400
1449
  merchantAccount: typeof resolvedMerchantAccount === "string" ? parseInt(resolvedMerchantAccount, 10) : resolvedMerchantAccount,
1401
1450
  basicAuth: authValue || "",
1402
1451
  ...methodPreference ? { paymentMethod: methodPreference } : {}
@@ -1407,24 +1456,41 @@ function HubtelBridge({
1407
1456
  callBacks: {
1408
1457
  onInit: () => console.log("Hubtel checkout initialized"),
1409
1458
  onPaymentSuccess: (data) => {
1459
+ const payload = parseHubtelCallbackPayload(data);
1460
+ const transactionReference = readHubtelField(payload, [
1461
+ "transactionId",
1462
+ "transaction_id",
1463
+ "transactionReference",
1464
+ "paymentReference",
1465
+ "checkoutId"
1466
+ ]);
1467
+ const clientReference = readHubtelField(payload, ["clientReference", "client_reference"]);
1410
1468
  const result = {
1411
- paymentId: data.transactionId || reference || "",
1412
- reference: data.clientReference || reference || "",
1469
+ paymentId,
1470
+ reference: clientReference || reference || paymentId,
1413
1471
  amount,
1414
1472
  currency: currency || "GHS",
1415
1473
  paymentMethod: preferredMethod || "mobile_money",
1416
1474
  psp: "hubtel",
1417
- pspReference: data.transactionId || "",
1418
- status: "success"
1475
+ pspReference: transactionReference || paymentId,
1476
+ status: "success",
1477
+ metadata: {
1478
+ hubtel_payload: payload,
1479
+ hubtel_raw: data
1480
+ }
1419
1481
  };
1420
1482
  onSuccess(result);
1421
1483
  checkout.closePopUp();
1422
1484
  },
1423
1485
  onPaymentFailure: (data) => {
1486
+ const payload = parseHubtelCallbackPayload(data);
1424
1487
  const error = {
1425
1488
  code: "PAYMENT_FAILED",
1426
- message: data.message || "Payment failed",
1427
- recoverable: true
1489
+ message: readHubtelField(payload, ["message", "error", "reason"]) || "Payment failed",
1490
+ recoverable: true,
1491
+ details: {
1492
+ hubtel_payload: payload
1493
+ }
1428
1494
  };
1429
1495
  onError(error);
1430
1496
  },
@@ -1449,6 +1515,8 @@ function HubtelBridge({
1449
1515
  phone,
1450
1516
  description,
1451
1517
  callbackUrl,
1518
+ paymentId,
1519
+ apiBaseUrl,
1452
1520
  authValue,
1453
1521
  isLoading,
1454
1522
  preferredMethod,
@@ -1481,7 +1549,7 @@ function openHubtelPopup(config) {
1481
1549
  };
1482
1550
  const checkoutConfig = {
1483
1551
  branding: "enabled",
1484
- callbackUrl: config.callbackUrl || window.location.href,
1552
+ callbackUrl: config.callbackUrl || getHubtelCallbackURL(config.apiBaseUrl),
1485
1553
  merchantAccount: typeof config.merchantAccount === "string" ? parseInt(config.merchantAccount, 10) : config.merchantAccount,
1486
1554
  basicAuth: config.basicAuth || "",
1487
1555
  ...methodPreference ? { paymentMethod: methodPreference } : {}
@@ -1491,11 +1559,11 @@ function openHubtelPopup(config) {
1491
1559
  config: checkoutConfig,
1492
1560
  callBacks: {
1493
1561
  onPaymentSuccess: (data) => {
1494
- config.onSuccess?.(data);
1562
+ config.onSuccess?.(parseHubtelCallbackPayload(data));
1495
1563
  checkout.closePopUp();
1496
1564
  },
1497
1565
  onPaymentFailure: (data) => {
1498
- config.onError?.(data);
1566
+ config.onError?.(parseHubtelCallbackPayload(data));
1499
1567
  },
1500
1568
  onClose: () => {
1501
1569
  config.onClose?.();
@@ -2475,7 +2543,11 @@ function ReevitCheckout({
2475
2543
  const renderContent = () => {
2476
2544
  if (status === "loading" || status === "processing") {
2477
2545
  return /* @__PURE__ */ jsxs("div", { className: "reevit-loading reevit-animate-fade-in", children: [
2478
- /* @__PURE__ */ jsx("div", { className: "reevit-spinner" }),
2546
+ /* @__PURE__ */ jsxs("div", { className: "reevit-dot-pulse", children: [
2547
+ /* @__PURE__ */ jsx("span", { className: "reevit-dot-pulse__dot" }),
2548
+ /* @__PURE__ */ jsx("span", { className: "reevit-dot-pulse__dot" }),
2549
+ /* @__PURE__ */ jsx("span", { className: "reevit-dot-pulse__dot" })
2550
+ ] }),
2479
2551
  /* @__PURE__ */ jsx("p", { children: status === "loading" ? "Preparing checkout..." : "Processing payment..." })
2480
2552
  ] });
2481
2553
  }
@@ -2491,12 +2563,22 @@ function ReevitCheckout({
2491
2563
  "Reference: ",
2492
2564
  result.reference
2493
2565
  ] }),
2494
- /* @__PURE__ */ jsx("p", { className: "reevit-success__redirect", children: "Redirecting in a moment..." })
2566
+ /* @__PURE__ */ jsx("p", { className: "reevit-success__redirect", children: "Redirecting in a moment..." }),
2567
+ /* @__PURE__ */ jsx(
2568
+ "div",
2569
+ {
2570
+ className: "reevit-success__countdown",
2571
+ style: { animationDuration: `${successDelayMs}ms` }
2572
+ }
2573
+ )
2495
2574
  ] });
2496
2575
  }
2497
2576
  if (status === "failed" && error && !error.recoverable) {
2498
2577
  return /* @__PURE__ */ jsxs("div", { className: "reevit-error reevit-animate-fade-in", children: [
2499
- /* @__PURE__ */ jsx("div", { className: "reevit-error__icon", children: "\u2715" }),
2578
+ /* @__PURE__ */ jsx("div", { className: "reevit-error__icon", children: /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2579
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2580
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2581
+ ] }) }),
2500
2582
  /* @__PURE__ */ jsx("h3", { children: "Payment Failed" }),
2501
2583
  /* @__PURE__ */ jsx("p", { children: error.message }),
2502
2584
  /* @__PURE__ */ jsx("button", { className: "reevit-btn reevit-btn--primary", onClick: handleBack, children: "Try Again" })
@@ -2541,10 +2623,11 @@ function ReevitCheckout({
2541
2623
  merchantAccount: paymentIntent?.pspCredentials?.merchantAccount || "",
2542
2624
  amount: paymentIntent?.amount ?? amount,
2543
2625
  currency: paymentIntent?.currency ?? currency,
2544
- reference: paymentIntent?.reference || reference,
2626
+ reference: paymentIntent?.providerRefId || paymentIntent?.reference || reference,
2545
2627
  email,
2546
2628
  phone: momoData?.phone || phone,
2547
2629
  description: `Payment ${paymentIntent?.reference || reference || ""}`,
2630
+ callbackUrl: `${apiBaseUrl || "https://api.reevit.io"}/v1/webhooks/incoming/hubtel`,
2548
2631
  hubtelSessionToken: paymentIntent?.id ? paymentIntent.id : void 0,
2549
2632
  clientSecret: paymentIntent?.clientSecret,
2550
2633
  apiBaseUrl,
@@ -2618,7 +2701,11 @@ function ReevitCheckout({
2618
2701
  );
2619
2702
  default:
2620
2703
  return /* @__PURE__ */ jsxs("div", { className: "reevit-error", children: [
2621
- /* @__PURE__ */ jsx("div", { className: "reevit-error__icon", children: "\u26A0\uFE0F" }),
2704
+ /* @__PURE__ */ jsx("div", { className: "reevit-error__icon", children: /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2705
+ /* @__PURE__ */ jsx("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
2706
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
2707
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
2708
+ ] }) }),
2622
2709
  /* @__PURE__ */ jsx("h3", { children: "Provider Not Supported" }),
2623
2710
  /* @__PURE__ */ jsxs("p", { children: [
2624
2711
  "Provider (",