@cimplify/sdk 0.8.5 → 0.8.7

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/react.js CHANGED
@@ -748,6 +748,110 @@ function CimplifyCheckout({
748
748
  ] });
749
749
  }
750
750
 
751
+ // src/utils/price.ts
752
+ var CURRENCY_SYMBOLS = {
753
+ // Major world currencies
754
+ USD: "$",
755
+ EUR: "\u20AC",
756
+ GBP: "\xA3",
757
+ JPY: "\xA5",
758
+ CNY: "\xA5",
759
+ CHF: "CHF",
760
+ CAD: "C$",
761
+ AUD: "A$",
762
+ NZD: "NZ$",
763
+ HKD: "HK$",
764
+ SGD: "S$",
765
+ INR: "\u20B9",
766
+ BRL: "R$",
767
+ MXN: "MX$",
768
+ KRW: "\u20A9",
769
+ RUB: "\u20BD",
770
+ TRY: "\u20BA",
771
+ THB: "\u0E3F",
772
+ PLN: "z\u0142",
773
+ SEK: "kr",
774
+ NOK: "kr",
775
+ DKK: "kr",
776
+ CZK: "K\u010D",
777
+ HUF: "Ft",
778
+ ILS: "\u20AA",
779
+ AED: "\u062F.\u0625",
780
+ SAR: "\uFDFC",
781
+ MYR: "RM",
782
+ PHP: "\u20B1",
783
+ IDR: "Rp",
784
+ VND: "\u20AB",
785
+ TWD: "NT$",
786
+ // African currencies
787
+ GHS: "GH\u20B5",
788
+ NGN: "\u20A6",
789
+ KES: "KSh",
790
+ ZAR: "R",
791
+ XOF: "CFA",
792
+ XAF: "FCFA",
793
+ EGP: "E\xA3",
794
+ MAD: "MAD",
795
+ TZS: "TSh",
796
+ UGX: "USh",
797
+ RWF: "FRw",
798
+ ETB: "Br",
799
+ ZMW: "ZK",
800
+ BWP: "P",
801
+ MUR: "\u20A8",
802
+ SCR: "\u20A8",
803
+ NAD: "N$",
804
+ SZL: "E",
805
+ LSL: "L",
806
+ MWK: "MK",
807
+ AOA: "Kz",
808
+ CDF: "FC",
809
+ GMD: "D",
810
+ GNF: "FG",
811
+ LRD: "L$",
812
+ SLL: "Le",
813
+ MZN: "MT",
814
+ SDG: "SDG",
815
+ SSP: "SSP",
816
+ SOS: "Sh.So.",
817
+ DJF: "Fdj",
818
+ ERN: "Nfk",
819
+ CVE: "$",
820
+ STN: "Db",
821
+ KMF: "CF",
822
+ BIF: "FBu"
823
+ };
824
+ function getCurrencySymbol(currencyCode2) {
825
+ return CURRENCY_SYMBOLS[currencyCode2.toUpperCase()] || currencyCode2;
826
+ }
827
+ function formatPrice(amount, currency = "GHS", locale = "en-US") {
828
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
829
+ if (isNaN(numAmount)) {
830
+ return `${getCurrencySymbol(currency)}0.00`;
831
+ }
832
+ try {
833
+ return new Intl.NumberFormat(locale, {
834
+ style: "currency",
835
+ currency: currency.toUpperCase(),
836
+ minimumFractionDigits: 2,
837
+ maximumFractionDigits: 2
838
+ }).format(numAmount);
839
+ } catch {
840
+ return `${getCurrencySymbol(currency)}${numAmount.toFixed(2)}`;
841
+ }
842
+ }
843
+ function parsePrice(value) {
844
+ if (value === void 0 || value === null) {
845
+ return 0;
846
+ }
847
+ if (typeof value === "number") {
848
+ return isNaN(value) ? 0 : value;
849
+ }
850
+ const cleaned = value.replace(/[^\d.-]/g, "");
851
+ const parsed = parseFloat(cleaned);
852
+ return isNaN(parsed) ? 0 : parsed;
853
+ }
854
+
751
855
  // src/types/common.ts
752
856
  function money(value) {
753
857
  return value;
@@ -1359,19 +1463,6 @@ var MOBILE_MONEY_PROVIDER = {
1359
1463
  AIRTEL: "airtel"
1360
1464
  };
1361
1465
 
1362
- // src/utils/price.ts
1363
- function parsePrice(value) {
1364
- if (value === void 0 || value === null) {
1365
- return 0;
1366
- }
1367
- if (typeof value === "number") {
1368
- return isNaN(value) ? 0 : value;
1369
- }
1370
- const cleaned = value.replace(/[^\d.-]/g, "");
1371
- const parsed = parseFloat(cleaned);
1372
- return isNaN(parsed) ? 0 : parsed;
1373
- }
1374
-
1375
1466
  // src/utils/payment.ts
1376
1467
  var PAYMENT_SUCCESS_STATUSES = /* @__PURE__ */ new Set([
1377
1468
  "success",
@@ -1563,6 +1654,10 @@ var DEFAULT_POLL_INTERVAL_MS = 3e3;
1563
1654
  var DEFAULT_MAX_POLL_ATTEMPTS = 60;
1564
1655
  var MAX_CONSECUTIVE_NETWORK_ERRORS = 5;
1565
1656
  var CARD_PROVIDER_PAYSTACK = "paystack";
1657
+ var NEXT_ACTION_NONE = "none";
1658
+ var NEXT_ACTION_CARD_POPUP = "card_popup";
1659
+ var NEXT_ACTION_REDIRECT = "redirect";
1660
+ var NEXT_ACTION_AUTHORIZATION = "authorization";
1566
1661
  function normalizeCardPopupError(error) {
1567
1662
  if (!error || error === "PAYMENT_CANCELLED" || error === "CANCELLED") {
1568
1663
  return "PAYMENT_CANCELLED";
@@ -1602,7 +1697,8 @@ function isValidMobileMoneyProvider(value) {
1602
1697
  return VALID_MOBILE_MONEY_PROVIDERS.has(value);
1603
1698
  }
1604
1699
  function normalizeAuthorizationType(value) {
1605
- return value === "otp" || value === "pin" ? value : void 0;
1700
+ const lower = value?.toLowerCase();
1701
+ return lower === "otp" || lower === "pin" ? lower : void 0;
1606
1702
  }
1607
1703
  function formatMoney2(value) {
1608
1704
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -1649,6 +1745,9 @@ var CheckoutResolver = class {
1649
1745
  if (!checkoutResult.order_id) {
1650
1746
  return this.fail("CHECKOUT_FAILED", "Checkout did not return an order ID.", false);
1651
1747
  }
1748
+ if (checkoutResult.next_action) {
1749
+ return this.resolveNextAction(checkoutResult);
1750
+ }
1652
1751
  let latestCheckoutResult = checkoutResult;
1653
1752
  let authorizationType = normalizeAuthorizationType(checkoutResult.authorization_type);
1654
1753
  let paymentReference = checkoutResult.payment_reference;
@@ -1764,6 +1863,110 @@ var CheckoutResolver = class {
1764
1863
  return this.fail("CHECKOUT_FAILED", message, true);
1765
1864
  }
1766
1865
  }
1866
+ async resolveNextAction(checkoutResult) {
1867
+ const action = checkoutResult.next_action;
1868
+ if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
1869
+ return this.finalizeSuccess(checkoutResult);
1870
+ }
1871
+ let latestCheckoutResult = checkoutResult;
1872
+ let paymentReference = checkoutResult.payment_reference;
1873
+ switch (action.type) {
1874
+ case NEXT_ACTION_NONE:
1875
+ return this.finalizeSuccess(checkoutResult);
1876
+ case NEXT_ACTION_CARD_POPUP: {
1877
+ const provider = normalizeCardProvider(action.provider);
1878
+ this.emit("awaiting_authorization", {
1879
+ display_text: "Complete payment in the card authorization popup.",
1880
+ order_id: checkoutResult.order_id,
1881
+ order_number: checkoutResult.order_number
1882
+ });
1883
+ if (!this.allowPopups) {
1884
+ return this.fail(
1885
+ "PROVIDER_UNAVAILABLE",
1886
+ "Card payment popup is unavailable in this environment.",
1887
+ false
1888
+ );
1889
+ }
1890
+ const popupResult = await openCardPopup(
1891
+ provider,
1892
+ checkoutResult,
1893
+ this.checkoutData.customer.email || "customer@cimplify.io",
1894
+ this.getOrderCurrency(checkoutResult),
1895
+ this.signal
1896
+ );
1897
+ if (!popupResult.success) {
1898
+ const popupError = normalizeCardPopupError(popupResult.error);
1899
+ if (popupError === "POPUP_BLOCKED") {
1900
+ return this.fail(
1901
+ "POPUP_BLOCKED",
1902
+ "Unable to open card payment popup. Please allow popups and try again.",
1903
+ true
1904
+ );
1905
+ }
1906
+ if (popupError === "PROVIDER_UNAVAILABLE") {
1907
+ return this.fail("PROVIDER_UNAVAILABLE", "Card payment provider is unavailable.", false);
1908
+ }
1909
+ return this.fail("PAYMENT_CANCELLED", "Card payment was cancelled before completion.", true);
1910
+ }
1911
+ paymentReference = popupResult.reference || paymentReference;
1912
+ break;
1913
+ }
1914
+ case NEXT_ACTION_REDIRECT: {
1915
+ if (typeof window !== "undefined" && this.returnUrl) {
1916
+ window.location.assign(action.authorization_url);
1917
+ return this.fail("REDIRECT_REQUIRED", "Redirecting to complete payment authorization.", true);
1918
+ }
1919
+ if (typeof window !== "undefined") {
1920
+ const popup = window.open(
1921
+ action.authorization_url,
1922
+ "cimplify-auth",
1923
+ "width=520,height=760,noopener,noreferrer"
1924
+ );
1925
+ if (!popup) {
1926
+ return this.fail(
1927
+ "POPUP_BLOCKED",
1928
+ "Authorization popup was blocked. Please allow popups and retry.",
1929
+ true
1930
+ );
1931
+ }
1932
+ }
1933
+ break;
1934
+ }
1935
+ case NEXT_ACTION_AUTHORIZATION: {
1936
+ const authType = normalizeAuthorizationType(action.authorization_type);
1937
+ const authorization = await this.handleAuthorization({
1938
+ authorizationType: authType,
1939
+ paymentReference,
1940
+ displayText: action.display_text,
1941
+ provider: checkoutResult.provider
1942
+ });
1943
+ if (!authorization.ok) {
1944
+ return authorization.result;
1945
+ }
1946
+ if (authorization.value) {
1947
+ latestCheckoutResult = authorization.value;
1948
+ paymentReference = authorization.value.payment_reference || paymentReference;
1949
+ if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1950
+ return this.finalizeSuccess(authorization.value);
1951
+ }
1952
+ if (this.isFailureStatus(authorization.value.payment_status)) {
1953
+ return this.fail(
1954
+ "AUTHORIZATION_FAILED",
1955
+ authorization.value.display_text || "Payment authorization failed.",
1956
+ false
1957
+ );
1958
+ }
1959
+ }
1960
+ break;
1961
+ }
1962
+ }
1963
+ return this.pollUntilTerminal({
1964
+ orderId: checkoutResult.order_id,
1965
+ latestCheckoutResult,
1966
+ paymentReference,
1967
+ authorizationType: normalizeAuthorizationType(checkoutResult.authorization_type)
1968
+ });
1969
+ }
1767
1970
  async pollUntilTerminal(input) {
1768
1971
  let consecutiveErrors = 0;
1769
1972
  let latestCheckoutResult = input.latestCheckoutResult;
@@ -4208,6 +4411,8 @@ function createCimplifyClient(config = {}) {
4208
4411
  return new CimplifyClient(config);
4209
4412
  }
4210
4413
  var LOCATION_STORAGE_KEY = "cimplify_location_id";
4414
+ var DISPLAY_CURRENCY_STORAGE_KEY = "cimplify_display_currency";
4415
+ var FX_REFRESH_INTERVAL = 12e4;
4211
4416
  var DEFAULT_CURRENCY = "USD";
4212
4417
  var DEFAULT_COUNTRY = "US";
4213
4418
  function createDefaultClient() {
@@ -4236,6 +4441,27 @@ function setStoredLocationId(locationId) {
4236
4441
  }
4237
4442
  window.localStorage.setItem(LOCATION_STORAGE_KEY, locationId);
4238
4443
  }
4444
+ function getStoredDisplayCurrency() {
4445
+ if (typeof window === "undefined" || !window.localStorage) {
4446
+ return null;
4447
+ }
4448
+ const value = window.localStorage.getItem(DISPLAY_CURRENCY_STORAGE_KEY);
4449
+ if (!value) {
4450
+ return null;
4451
+ }
4452
+ const normalized = value.trim().toUpperCase();
4453
+ return normalized.length > 0 ? normalized : null;
4454
+ }
4455
+ function setStoredDisplayCurrency(currency) {
4456
+ if (typeof window === "undefined" || !window.localStorage) {
4457
+ return;
4458
+ }
4459
+ if (!currency) {
4460
+ window.localStorage.removeItem(DISPLAY_CURRENCY_STORAGE_KEY);
4461
+ return;
4462
+ }
4463
+ window.localStorage.setItem(DISPLAY_CURRENCY_STORAGE_KEY, currency.toUpperCase());
4464
+ }
4239
4465
  function resolveInitialLocation(locations) {
4240
4466
  if (locations.length === 0) {
4241
4467
  return null;
@@ -4265,6 +4491,54 @@ function CimplifyProvider({
4265
4491
  onLocationChangeRef.current = onLocationChange;
4266
4492
  }, [onLocationChange]);
4267
4493
  const isDemoMode = resolvedClient.getPublicKey().trim().length === 0;
4494
+ const baseCurrency = business?.default_currency || DEFAULT_CURRENCY;
4495
+ const [displayCurrencyOverride, setDisplayCurrencyOverride] = React2.useState(
4496
+ () => getStoredDisplayCurrency()
4497
+ );
4498
+ const [fxRate, setFxRate] = React2.useState(null);
4499
+ const displayCurrency = displayCurrencyOverride && displayCurrencyOverride !== baseCurrency ? displayCurrencyOverride : baseCurrency;
4500
+ const setDisplayCurrency = React2.useCallback(
4501
+ (currency) => {
4502
+ const normalized = currency?.trim().toUpperCase() || null;
4503
+ setDisplayCurrencyOverride(normalized);
4504
+ setStoredDisplayCurrency(normalized);
4505
+ if (!normalized || normalized === baseCurrency) {
4506
+ setFxRate(null);
4507
+ }
4508
+ },
4509
+ [baseCurrency]
4510
+ );
4511
+ React2.useEffect(() => {
4512
+ if (displayCurrency === baseCurrency || isDemoMode) {
4513
+ setFxRate(null);
4514
+ return;
4515
+ }
4516
+ let cancelled = false;
4517
+ async function fetchRate() {
4518
+ const result = await resolvedClient.fx.getRate(
4519
+ baseCurrency,
4520
+ displayCurrency
4521
+ );
4522
+ if (!cancelled && result.ok) {
4523
+ setFxRate(result.value.rate);
4524
+ }
4525
+ }
4526
+ void fetchRate();
4527
+ const intervalId = setInterval(() => void fetchRate(), FX_REFRESH_INTERVAL);
4528
+ return () => {
4529
+ cancelled = true;
4530
+ clearInterval(intervalId);
4531
+ };
4532
+ }, [resolvedClient, baseCurrency, displayCurrency, isDemoMode]);
4533
+ const convertPrice = React2.useCallback(
4534
+ (amount) => {
4535
+ const num = typeof amount === "string" ? parseFloat(amount) : amount;
4536
+ if (isNaN(num)) return 0;
4537
+ if (!fxRate || displayCurrency === baseCurrency) return num;
4538
+ return Math.round(num * fxRate * 100) / 100;
4539
+ },
4540
+ [fxRate, displayCurrency, baseCurrency]
4541
+ );
4268
4542
  const setCurrentLocation = React2.useCallback(
4269
4543
  (location) => {
4270
4544
  setCurrentLocationState(location);
@@ -4334,22 +4608,32 @@ function CimplifyProvider({
4334
4608
  () => ({
4335
4609
  client: resolvedClient,
4336
4610
  business,
4337
- currency: business?.default_currency || DEFAULT_CURRENCY,
4611
+ currency: baseCurrency,
4338
4612
  country: business?.country_code || DEFAULT_COUNTRY,
4339
4613
  locations,
4340
4614
  currentLocation,
4341
4615
  setCurrentLocation,
4342
4616
  isReady,
4343
- isDemoMode
4617
+ isDemoMode,
4618
+ baseCurrency,
4619
+ displayCurrency,
4620
+ setDisplayCurrency,
4621
+ convertPrice,
4622
+ fxRate
4344
4623
  }),
4345
4624
  [
4346
4625
  resolvedClient,
4347
4626
  business,
4627
+ baseCurrency,
4348
4628
  locations,
4349
4629
  currentLocation,
4350
4630
  setCurrentLocation,
4351
4631
  isReady,
4352
- isDemoMode
4632
+ isDemoMode,
4633
+ displayCurrency,
4634
+ setDisplayCurrency,
4635
+ convertPrice,
4636
+ fxRate
4353
4637
  ]
4354
4638
  );
4355
4639
  return /* @__PURE__ */ jsxRuntime.jsx(CimplifyContext.Provider, { value: contextValue, children });
@@ -4364,6 +4648,13 @@ function useCimplify() {
4364
4648
  function useOptionalCimplify() {
4365
4649
  return React2.useContext(CimplifyContext);
4366
4650
  }
4651
+ function Price({ amount, className, prefix }) {
4652
+ const { displayCurrency, convertPrice } = useCimplify();
4653
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className, children: [
4654
+ prefix,
4655
+ formatPrice(convertPrice(amount), displayCurrency)
4656
+ ] });
4657
+ }
4367
4658
  var productsCache = /* @__PURE__ */ new Map();
4368
4659
  var productsInflight = /* @__PURE__ */ new Map();
4369
4660
  function buildProductsCacheKey(client, locationId, options) {
@@ -6401,6 +6692,7 @@ exports.CimplifyCheckout = CimplifyCheckout;
6401
6692
  exports.CimplifyProvider = CimplifyProvider;
6402
6693
  exports.ElementsProvider = ElementsProvider;
6403
6694
  exports.PaymentElement = PaymentElement;
6695
+ exports.Price = Price;
6404
6696
  exports.useAds = useAds;
6405
6697
  exports.useBundle = useBundle;
6406
6698
  exports.useCart = useCart;