@cimplify/sdk 0.8.6 → 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.d.mts CHANGED
@@ -77,6 +77,22 @@ interface CimplifyCheckoutProps {
77
77
  }
78
78
  declare function CimplifyCheckout({ client, businessId, cartId, locationId, linkUrl, orderTypes, enrollInLink, onComplete, onError, onStatusChange, appearance, demoMode, className, }: CimplifyCheckoutProps): React.ReactElement;
79
79
 
80
+ interface PriceProps {
81
+ /** The amount in base (business) currency. */
82
+ amount: number | string;
83
+ /** Optional CSS class name for the wrapping span. */
84
+ className?: string;
85
+ /** Optional prefix rendered before the formatted price (e.g. "+"). */
86
+ prefix?: string;
87
+ }
88
+ /**
89
+ * Price — renders a single price value in the user's chosen display currency.
90
+ *
91
+ * Reads `displayCurrency` and `convertPrice` from CimplifyProvider context
92
+ * so every price on the page stays consistent with the selected currency.
93
+ */
94
+ declare function Price({ amount, className, prefix }: PriceProps): React.ReactElement;
95
+
80
96
  interface CimplifyContextValue {
81
97
  client: CimplifyClient;
82
98
  business: Business | null;
@@ -87,6 +103,16 @@ interface CimplifyContextValue {
87
103
  setCurrentLocation: (location: Location) => void;
88
104
  isReady: boolean;
89
105
  isDemoMode: boolean;
106
+ /** The business's pricing currency (always the base, for API calls). */
107
+ baseCurrency: string;
108
+ /** The currency prices are displayed in. Equals baseCurrency when no FX override is set. */
109
+ displayCurrency: string;
110
+ /** Set a display currency override for FX conversion. Pass null to reset to base currency. */
111
+ setDisplayCurrency: (currency: string | null) => void;
112
+ /** Convert a base-currency amount to the display currency. No-op when currencies match. */
113
+ convertPrice: (amount: number | string) => number;
114
+ /** Current FX rate (baseCurrency → displayCurrency). Null when no conversion is active. */
115
+ fxRate: number | null;
90
116
  }
91
117
  interface CimplifyProviderProps {
92
118
  client?: CimplifyClient;
@@ -376,4 +402,4 @@ declare function useCheckout(): {
376
402
  isLoading: boolean;
377
403
  };
378
404
 
379
- export { Ad, AdPosition, type AdProps, AdProvider, AdSlot, type AddToCartOptions, AddressElement, AuthElement, CimplifyCheckout, type CimplifyCheckoutProps, type CimplifyContextValue, CimplifyProvider, type CimplifyProviderProps, ElementsProvider, PaymentElement, type UseBundleOptions, type UseBundleResult, type UseCartItem, type UseCartOptions, type UseCartResult, type UseCategoriesOptions, type UseCategoriesResult, type UseCollectionOptions, type UseCollectionResult, type UseCollectionsOptions, type UseCollectionsResult, type UseCompositeOptions, type UseCompositeResult, type UseLocationsOptions, type UseLocationsResult, type UseOrderOptions, type UseOrderResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseQuoteInput, type UseQuoteOptions, type UseQuoteResult, type UseSearchOptions, type UseSearchResult, useAds, useBundle, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useComposite, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
405
+ export { Ad, AdPosition, type AdProps, AdProvider, AdSlot, type AddToCartOptions, AddressElement, AuthElement, CimplifyCheckout, type CimplifyCheckoutProps, type CimplifyContextValue, CimplifyProvider, type CimplifyProviderProps, ElementsProvider, PaymentElement, Price, type PriceProps, type UseBundleOptions, type UseBundleResult, type UseCartItem, type UseCartOptions, type UseCartResult, type UseCategoriesOptions, type UseCategoriesResult, type UseCollectionOptions, type UseCollectionResult, type UseCollectionsOptions, type UseCollectionsResult, type UseCompositeOptions, type UseCompositeResult, type UseLocationsOptions, type UseLocationsResult, type UseOrderOptions, type UseOrderResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseQuoteInput, type UseQuoteOptions, type UseQuoteResult, type UseSearchOptions, type UseSearchResult, useAds, useBundle, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useComposite, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
package/dist/react.d.ts CHANGED
@@ -77,6 +77,22 @@ interface CimplifyCheckoutProps {
77
77
  }
78
78
  declare function CimplifyCheckout({ client, businessId, cartId, locationId, linkUrl, orderTypes, enrollInLink, onComplete, onError, onStatusChange, appearance, demoMode, className, }: CimplifyCheckoutProps): React.ReactElement;
79
79
 
80
+ interface PriceProps {
81
+ /** The amount in base (business) currency. */
82
+ amount: number | string;
83
+ /** Optional CSS class name for the wrapping span. */
84
+ className?: string;
85
+ /** Optional prefix rendered before the formatted price (e.g. "+"). */
86
+ prefix?: string;
87
+ }
88
+ /**
89
+ * Price — renders a single price value in the user's chosen display currency.
90
+ *
91
+ * Reads `displayCurrency` and `convertPrice` from CimplifyProvider context
92
+ * so every price on the page stays consistent with the selected currency.
93
+ */
94
+ declare function Price({ amount, className, prefix }: PriceProps): React.ReactElement;
95
+
80
96
  interface CimplifyContextValue {
81
97
  client: CimplifyClient;
82
98
  business: Business | null;
@@ -87,6 +103,16 @@ interface CimplifyContextValue {
87
103
  setCurrentLocation: (location: Location) => void;
88
104
  isReady: boolean;
89
105
  isDemoMode: boolean;
106
+ /** The business's pricing currency (always the base, for API calls). */
107
+ baseCurrency: string;
108
+ /** The currency prices are displayed in. Equals baseCurrency when no FX override is set. */
109
+ displayCurrency: string;
110
+ /** Set a display currency override for FX conversion. Pass null to reset to base currency. */
111
+ setDisplayCurrency: (currency: string | null) => void;
112
+ /** Convert a base-currency amount to the display currency. No-op when currencies match. */
113
+ convertPrice: (amount: number | string) => number;
114
+ /** Current FX rate (baseCurrency → displayCurrency). Null when no conversion is active. */
115
+ fxRate: number | null;
90
116
  }
91
117
  interface CimplifyProviderProps {
92
118
  client?: CimplifyClient;
@@ -376,4 +402,4 @@ declare function useCheckout(): {
376
402
  isLoading: boolean;
377
403
  };
378
404
 
379
- export { Ad, AdPosition, type AdProps, AdProvider, AdSlot, type AddToCartOptions, AddressElement, AuthElement, CimplifyCheckout, type CimplifyCheckoutProps, type CimplifyContextValue, CimplifyProvider, type CimplifyProviderProps, ElementsProvider, PaymentElement, type UseBundleOptions, type UseBundleResult, type UseCartItem, type UseCartOptions, type UseCartResult, type UseCategoriesOptions, type UseCategoriesResult, type UseCollectionOptions, type UseCollectionResult, type UseCollectionsOptions, type UseCollectionsResult, type UseCompositeOptions, type UseCompositeResult, type UseLocationsOptions, type UseLocationsResult, type UseOrderOptions, type UseOrderResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseQuoteInput, type UseQuoteOptions, type UseQuoteResult, type UseSearchOptions, type UseSearchResult, useAds, useBundle, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useComposite, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
405
+ export { Ad, AdPosition, type AdProps, AdProvider, AdSlot, type AddToCartOptions, AddressElement, AuthElement, CimplifyCheckout, type CimplifyCheckoutProps, type CimplifyContextValue, CimplifyProvider, type CimplifyProviderProps, ElementsProvider, PaymentElement, Price, type PriceProps, type UseBundleOptions, type UseBundleResult, type UseCartItem, type UseCartOptions, type UseCartResult, type UseCategoriesOptions, type UseCategoriesResult, type UseCollectionOptions, type UseCollectionResult, type UseCollectionsOptions, type UseCollectionsResult, type UseCompositeOptions, type UseCompositeResult, type UseLocationsOptions, type UseLocationsResult, type UseOrderOptions, type UseOrderResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseQuoteInput, type UseQuoteOptions, type UseQuoteResult, type UseSearchOptions, type UseSearchResult, useAds, useBundle, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useComposite, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
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",
@@ -4320,6 +4411,8 @@ function createCimplifyClient(config = {}) {
4320
4411
  return new CimplifyClient(config);
4321
4412
  }
4322
4413
  var LOCATION_STORAGE_KEY = "cimplify_location_id";
4414
+ var DISPLAY_CURRENCY_STORAGE_KEY = "cimplify_display_currency";
4415
+ var FX_REFRESH_INTERVAL = 12e4;
4323
4416
  var DEFAULT_CURRENCY = "USD";
4324
4417
  var DEFAULT_COUNTRY = "US";
4325
4418
  function createDefaultClient() {
@@ -4348,6 +4441,27 @@ function setStoredLocationId(locationId) {
4348
4441
  }
4349
4442
  window.localStorage.setItem(LOCATION_STORAGE_KEY, locationId);
4350
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
+ }
4351
4465
  function resolveInitialLocation(locations) {
4352
4466
  if (locations.length === 0) {
4353
4467
  return null;
@@ -4377,6 +4491,54 @@ function CimplifyProvider({
4377
4491
  onLocationChangeRef.current = onLocationChange;
4378
4492
  }, [onLocationChange]);
4379
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
+ );
4380
4542
  const setCurrentLocation = React2.useCallback(
4381
4543
  (location) => {
4382
4544
  setCurrentLocationState(location);
@@ -4446,22 +4608,32 @@ function CimplifyProvider({
4446
4608
  () => ({
4447
4609
  client: resolvedClient,
4448
4610
  business,
4449
- currency: business?.default_currency || DEFAULT_CURRENCY,
4611
+ currency: baseCurrency,
4450
4612
  country: business?.country_code || DEFAULT_COUNTRY,
4451
4613
  locations,
4452
4614
  currentLocation,
4453
4615
  setCurrentLocation,
4454
4616
  isReady,
4455
- isDemoMode
4617
+ isDemoMode,
4618
+ baseCurrency,
4619
+ displayCurrency,
4620
+ setDisplayCurrency,
4621
+ convertPrice,
4622
+ fxRate
4456
4623
  }),
4457
4624
  [
4458
4625
  resolvedClient,
4459
4626
  business,
4627
+ baseCurrency,
4460
4628
  locations,
4461
4629
  currentLocation,
4462
4630
  setCurrentLocation,
4463
4631
  isReady,
4464
- isDemoMode
4632
+ isDemoMode,
4633
+ displayCurrency,
4634
+ setDisplayCurrency,
4635
+ convertPrice,
4636
+ fxRate
4465
4637
  ]
4466
4638
  );
4467
4639
  return /* @__PURE__ */ jsxRuntime.jsx(CimplifyContext.Provider, { value: contextValue, children });
@@ -4476,6 +4648,13 @@ function useCimplify() {
4476
4648
  function useOptionalCimplify() {
4477
4649
  return React2.useContext(CimplifyContext);
4478
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
+ }
4479
4658
  var productsCache = /* @__PURE__ */ new Map();
4480
4659
  var productsInflight = /* @__PURE__ */ new Map();
4481
4660
  function buildProductsCacheKey(client, locationId, options) {
@@ -6513,6 +6692,7 @@ exports.CimplifyCheckout = CimplifyCheckout;
6513
6692
  exports.CimplifyProvider = CimplifyProvider;
6514
6693
  exports.ElementsProvider = ElementsProvider;
6515
6694
  exports.PaymentElement = PaymentElement;
6695
+ exports.Price = Price;
6516
6696
  exports.useAds = useAds;
6517
6697
  exports.useBundle = useBundle;
6518
6698
  exports.useCart = useCart;
package/dist/react.mjs CHANGED
@@ -742,6 +742,110 @@ function CimplifyCheckout({
742
742
  ] });
743
743
  }
744
744
 
745
+ // src/utils/price.ts
746
+ var CURRENCY_SYMBOLS = {
747
+ // Major world currencies
748
+ USD: "$",
749
+ EUR: "\u20AC",
750
+ GBP: "\xA3",
751
+ JPY: "\xA5",
752
+ CNY: "\xA5",
753
+ CHF: "CHF",
754
+ CAD: "C$",
755
+ AUD: "A$",
756
+ NZD: "NZ$",
757
+ HKD: "HK$",
758
+ SGD: "S$",
759
+ INR: "\u20B9",
760
+ BRL: "R$",
761
+ MXN: "MX$",
762
+ KRW: "\u20A9",
763
+ RUB: "\u20BD",
764
+ TRY: "\u20BA",
765
+ THB: "\u0E3F",
766
+ PLN: "z\u0142",
767
+ SEK: "kr",
768
+ NOK: "kr",
769
+ DKK: "kr",
770
+ CZK: "K\u010D",
771
+ HUF: "Ft",
772
+ ILS: "\u20AA",
773
+ AED: "\u062F.\u0625",
774
+ SAR: "\uFDFC",
775
+ MYR: "RM",
776
+ PHP: "\u20B1",
777
+ IDR: "Rp",
778
+ VND: "\u20AB",
779
+ TWD: "NT$",
780
+ // African currencies
781
+ GHS: "GH\u20B5",
782
+ NGN: "\u20A6",
783
+ KES: "KSh",
784
+ ZAR: "R",
785
+ XOF: "CFA",
786
+ XAF: "FCFA",
787
+ EGP: "E\xA3",
788
+ MAD: "MAD",
789
+ TZS: "TSh",
790
+ UGX: "USh",
791
+ RWF: "FRw",
792
+ ETB: "Br",
793
+ ZMW: "ZK",
794
+ BWP: "P",
795
+ MUR: "\u20A8",
796
+ SCR: "\u20A8",
797
+ NAD: "N$",
798
+ SZL: "E",
799
+ LSL: "L",
800
+ MWK: "MK",
801
+ AOA: "Kz",
802
+ CDF: "FC",
803
+ GMD: "D",
804
+ GNF: "FG",
805
+ LRD: "L$",
806
+ SLL: "Le",
807
+ MZN: "MT",
808
+ SDG: "SDG",
809
+ SSP: "SSP",
810
+ SOS: "Sh.So.",
811
+ DJF: "Fdj",
812
+ ERN: "Nfk",
813
+ CVE: "$",
814
+ STN: "Db",
815
+ KMF: "CF",
816
+ BIF: "FBu"
817
+ };
818
+ function getCurrencySymbol(currencyCode2) {
819
+ return CURRENCY_SYMBOLS[currencyCode2.toUpperCase()] || currencyCode2;
820
+ }
821
+ function formatPrice(amount, currency = "GHS", locale = "en-US") {
822
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
823
+ if (isNaN(numAmount)) {
824
+ return `${getCurrencySymbol(currency)}0.00`;
825
+ }
826
+ try {
827
+ return new Intl.NumberFormat(locale, {
828
+ style: "currency",
829
+ currency: currency.toUpperCase(),
830
+ minimumFractionDigits: 2,
831
+ maximumFractionDigits: 2
832
+ }).format(numAmount);
833
+ } catch {
834
+ return `${getCurrencySymbol(currency)}${numAmount.toFixed(2)}`;
835
+ }
836
+ }
837
+ function parsePrice(value) {
838
+ if (value === void 0 || value === null) {
839
+ return 0;
840
+ }
841
+ if (typeof value === "number") {
842
+ return isNaN(value) ? 0 : value;
843
+ }
844
+ const cleaned = value.replace(/[^\d.-]/g, "");
845
+ const parsed = parseFloat(cleaned);
846
+ return isNaN(parsed) ? 0 : parsed;
847
+ }
848
+
745
849
  // src/types/common.ts
746
850
  function money(value) {
747
851
  return value;
@@ -1353,19 +1457,6 @@ var MOBILE_MONEY_PROVIDER = {
1353
1457
  AIRTEL: "airtel"
1354
1458
  };
1355
1459
 
1356
- // src/utils/price.ts
1357
- function parsePrice(value) {
1358
- if (value === void 0 || value === null) {
1359
- return 0;
1360
- }
1361
- if (typeof value === "number") {
1362
- return isNaN(value) ? 0 : value;
1363
- }
1364
- const cleaned = value.replace(/[^\d.-]/g, "");
1365
- const parsed = parseFloat(cleaned);
1366
- return isNaN(parsed) ? 0 : parsed;
1367
- }
1368
-
1369
1460
  // src/utils/payment.ts
1370
1461
  var PAYMENT_SUCCESS_STATUSES = /* @__PURE__ */ new Set([
1371
1462
  "success",
@@ -4314,6 +4405,8 @@ function createCimplifyClient(config = {}) {
4314
4405
  return new CimplifyClient(config);
4315
4406
  }
4316
4407
  var LOCATION_STORAGE_KEY = "cimplify_location_id";
4408
+ var DISPLAY_CURRENCY_STORAGE_KEY = "cimplify_display_currency";
4409
+ var FX_REFRESH_INTERVAL = 12e4;
4317
4410
  var DEFAULT_CURRENCY = "USD";
4318
4411
  var DEFAULT_COUNTRY = "US";
4319
4412
  function createDefaultClient() {
@@ -4342,6 +4435,27 @@ function setStoredLocationId(locationId) {
4342
4435
  }
4343
4436
  window.localStorage.setItem(LOCATION_STORAGE_KEY, locationId);
4344
4437
  }
4438
+ function getStoredDisplayCurrency() {
4439
+ if (typeof window === "undefined" || !window.localStorage) {
4440
+ return null;
4441
+ }
4442
+ const value = window.localStorage.getItem(DISPLAY_CURRENCY_STORAGE_KEY);
4443
+ if (!value) {
4444
+ return null;
4445
+ }
4446
+ const normalized = value.trim().toUpperCase();
4447
+ return normalized.length > 0 ? normalized : null;
4448
+ }
4449
+ function setStoredDisplayCurrency(currency) {
4450
+ if (typeof window === "undefined" || !window.localStorage) {
4451
+ return;
4452
+ }
4453
+ if (!currency) {
4454
+ window.localStorage.removeItem(DISPLAY_CURRENCY_STORAGE_KEY);
4455
+ return;
4456
+ }
4457
+ window.localStorage.setItem(DISPLAY_CURRENCY_STORAGE_KEY, currency.toUpperCase());
4458
+ }
4345
4459
  function resolveInitialLocation(locations) {
4346
4460
  if (locations.length === 0) {
4347
4461
  return null;
@@ -4371,6 +4485,54 @@ function CimplifyProvider({
4371
4485
  onLocationChangeRef.current = onLocationChange;
4372
4486
  }, [onLocationChange]);
4373
4487
  const isDemoMode = resolvedClient.getPublicKey().trim().length === 0;
4488
+ const baseCurrency = business?.default_currency || DEFAULT_CURRENCY;
4489
+ const [displayCurrencyOverride, setDisplayCurrencyOverride] = useState(
4490
+ () => getStoredDisplayCurrency()
4491
+ );
4492
+ const [fxRate, setFxRate] = useState(null);
4493
+ const displayCurrency = displayCurrencyOverride && displayCurrencyOverride !== baseCurrency ? displayCurrencyOverride : baseCurrency;
4494
+ const setDisplayCurrency = useCallback(
4495
+ (currency) => {
4496
+ const normalized = currency?.trim().toUpperCase() || null;
4497
+ setDisplayCurrencyOverride(normalized);
4498
+ setStoredDisplayCurrency(normalized);
4499
+ if (!normalized || normalized === baseCurrency) {
4500
+ setFxRate(null);
4501
+ }
4502
+ },
4503
+ [baseCurrency]
4504
+ );
4505
+ useEffect(() => {
4506
+ if (displayCurrency === baseCurrency || isDemoMode) {
4507
+ setFxRate(null);
4508
+ return;
4509
+ }
4510
+ let cancelled = false;
4511
+ async function fetchRate() {
4512
+ const result = await resolvedClient.fx.getRate(
4513
+ baseCurrency,
4514
+ displayCurrency
4515
+ );
4516
+ if (!cancelled && result.ok) {
4517
+ setFxRate(result.value.rate);
4518
+ }
4519
+ }
4520
+ void fetchRate();
4521
+ const intervalId = setInterval(() => void fetchRate(), FX_REFRESH_INTERVAL);
4522
+ return () => {
4523
+ cancelled = true;
4524
+ clearInterval(intervalId);
4525
+ };
4526
+ }, [resolvedClient, baseCurrency, displayCurrency, isDemoMode]);
4527
+ const convertPrice = useCallback(
4528
+ (amount) => {
4529
+ const num = typeof amount === "string" ? parseFloat(amount) : amount;
4530
+ if (isNaN(num)) return 0;
4531
+ if (!fxRate || displayCurrency === baseCurrency) return num;
4532
+ return Math.round(num * fxRate * 100) / 100;
4533
+ },
4534
+ [fxRate, displayCurrency, baseCurrency]
4535
+ );
4374
4536
  const setCurrentLocation = useCallback(
4375
4537
  (location) => {
4376
4538
  setCurrentLocationState(location);
@@ -4440,22 +4602,32 @@ function CimplifyProvider({
4440
4602
  () => ({
4441
4603
  client: resolvedClient,
4442
4604
  business,
4443
- currency: business?.default_currency || DEFAULT_CURRENCY,
4605
+ currency: baseCurrency,
4444
4606
  country: business?.country_code || DEFAULT_COUNTRY,
4445
4607
  locations,
4446
4608
  currentLocation,
4447
4609
  setCurrentLocation,
4448
4610
  isReady,
4449
- isDemoMode
4611
+ isDemoMode,
4612
+ baseCurrency,
4613
+ displayCurrency,
4614
+ setDisplayCurrency,
4615
+ convertPrice,
4616
+ fxRate
4450
4617
  }),
4451
4618
  [
4452
4619
  resolvedClient,
4453
4620
  business,
4621
+ baseCurrency,
4454
4622
  locations,
4455
4623
  currentLocation,
4456
4624
  setCurrentLocation,
4457
4625
  isReady,
4458
- isDemoMode
4626
+ isDemoMode,
4627
+ displayCurrency,
4628
+ setDisplayCurrency,
4629
+ convertPrice,
4630
+ fxRate
4459
4631
  ]
4460
4632
  );
4461
4633
  return /* @__PURE__ */ jsx(CimplifyContext.Provider, { value: contextValue, children });
@@ -4470,6 +4642,13 @@ function useCimplify() {
4470
4642
  function useOptionalCimplify() {
4471
4643
  return useContext(CimplifyContext);
4472
4644
  }
4645
+ function Price({ amount, className, prefix }) {
4646
+ const { displayCurrency, convertPrice } = useCimplify();
4647
+ return /* @__PURE__ */ jsxs("span", { className, children: [
4648
+ prefix,
4649
+ formatPrice(convertPrice(amount), displayCurrency)
4650
+ ] });
4651
+ }
4473
4652
  var productsCache = /* @__PURE__ */ new Map();
4474
4653
  var productsInflight = /* @__PURE__ */ new Map();
4475
4654
  function buildProductsCacheKey(client, locationId, options) {
@@ -6499,4 +6678,4 @@ function useCheckout() {
6499
6678
  return { submit, process, isLoading };
6500
6679
  }
6501
6680
 
6502
- export { Ad, AdProvider, AddressElement, AuthElement, CimplifyCheckout, CimplifyProvider, ElementsProvider, PaymentElement, useAds, useBundle, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useComposite, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
6681
+ export { Ad, AdProvider, AddressElement, AuthElement, CimplifyCheckout, CimplifyProvider, ElementsProvider, PaymentElement, Price, useAds, useBundle, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useComposite, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/sdk",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Cimplify Commerce SDK for storefronts",
5
5
  "keywords": [
6
6
  "cimplify",