@cimplify/sdk 0.6.2 → 0.6.3

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
@@ -662,1994 +662,2837 @@ var ORDER_MUTATION = {
662
662
  var DEFAULT_CURRENCY = "GHS";
663
663
  var DEFAULT_COUNTRY = "GHA";
664
664
 
665
- // src/checkout.ts
666
- function toCimplifyError3(error) {
667
- if (error instanceof CimplifyError) return error;
668
- if (error instanceof Error) {
669
- return new CimplifyError("UNKNOWN_ERROR", error.message, false);
665
+ // src/utils/price.ts
666
+ var CURRENCY_SYMBOLS = {
667
+ // Major world currencies
668
+ USD: "$",
669
+ EUR: "\u20AC",
670
+ GBP: "\xA3",
671
+ JPY: "\xA5",
672
+ CNY: "\xA5",
673
+ CHF: "CHF",
674
+ CAD: "C$",
675
+ AUD: "A$",
676
+ NZD: "NZ$",
677
+ HKD: "HK$",
678
+ SGD: "S$",
679
+ INR: "\u20B9",
680
+ BRL: "R$",
681
+ MXN: "MX$",
682
+ KRW: "\u20A9",
683
+ RUB: "\u20BD",
684
+ TRY: "\u20BA",
685
+ THB: "\u0E3F",
686
+ PLN: "z\u0142",
687
+ SEK: "kr",
688
+ NOK: "kr",
689
+ DKK: "kr",
690
+ CZK: "K\u010D",
691
+ HUF: "Ft",
692
+ ILS: "\u20AA",
693
+ AED: "\u062F.\u0625",
694
+ SAR: "\uFDFC",
695
+ MYR: "RM",
696
+ PHP: "\u20B1",
697
+ IDR: "Rp",
698
+ VND: "\u20AB",
699
+ TWD: "NT$",
700
+ // African currencies
701
+ GHS: "GH\u20B5",
702
+ NGN: "\u20A6",
703
+ KES: "KSh",
704
+ ZAR: "R",
705
+ XOF: "CFA",
706
+ XAF: "FCFA",
707
+ EGP: "E\xA3",
708
+ MAD: "MAD",
709
+ TZS: "TSh",
710
+ UGX: "USh",
711
+ RWF: "FRw",
712
+ ETB: "Br",
713
+ ZMW: "ZK",
714
+ BWP: "P",
715
+ MUR: "\u20A8",
716
+ SCR: "\u20A8",
717
+ NAD: "N$",
718
+ SZL: "E",
719
+ LSL: "L",
720
+ MWK: "MK",
721
+ AOA: "Kz",
722
+ CDF: "FC",
723
+ GMD: "D",
724
+ GNF: "FG",
725
+ LRD: "L$",
726
+ SLL: "Le",
727
+ MZN: "MT",
728
+ SDG: "SDG",
729
+ SSP: "SSP",
730
+ SOS: "Sh.So.",
731
+ DJF: "Fdj",
732
+ ERN: "Nfk",
733
+ CVE: "$",
734
+ STN: "Db",
735
+ KMF: "CF",
736
+ BIF: "FBu"
737
+ };
738
+ function getCurrencySymbol(currencyCode) {
739
+ return CURRENCY_SYMBOLS[currencyCode.toUpperCase()] || currencyCode;
740
+ }
741
+ function formatNumberCompact(value, decimals = 1) {
742
+ const absValue = Math.abs(value);
743
+ const sign = value < 0 ? "-" : "";
744
+ if (absValue >= 1e9) {
745
+ return `${sign}${(absValue / 1e9).toFixed(decimals)}B`;
670
746
  }
671
- return new CimplifyError("UNKNOWN_ERROR", String(error), false);
747
+ if (absValue >= 1e6) {
748
+ return `${sign}${(absValue / 1e6).toFixed(decimals)}M`;
749
+ }
750
+ if (absValue >= 1e3) {
751
+ return `${sign}${(absValue / 1e3).toFixed(decimals)}K`;
752
+ }
753
+ return `${sign}${absValue.toFixed(decimals)}`;
672
754
  }
673
- async function safe3(promise) {
755
+ function formatPrice(amount, currency = "GHS", locale = "en-US") {
756
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
757
+ if (isNaN(numAmount)) {
758
+ return `${getCurrencySymbol(currency)}0.00`;
759
+ }
674
760
  try {
675
- return ok(await promise);
676
- } catch (error) {
677
- return err(toCimplifyError3(error));
761
+ return new Intl.NumberFormat(locale, {
762
+ style: "currency",
763
+ currency: currency.toUpperCase(),
764
+ minimumFractionDigits: 2,
765
+ maximumFractionDigits: 2
766
+ }).format(numAmount);
767
+ } catch {
768
+ return `${getCurrencySymbol(currency)}${numAmount.toFixed(2)}`;
678
769
  }
679
770
  }
680
- function generateIdempotencyKey() {
681
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
682
- return `idem_${crypto.randomUUID()}`;
771
+ function formatPriceAdjustment(amount, currency = "GHS", locale = "en-US") {
772
+ const formatted = formatPrice(Math.abs(amount), currency, locale);
773
+ if (amount > 0) {
774
+ return `+${formatted}`;
775
+ } else if (amount < 0) {
776
+ return `-${formatted}`;
683
777
  }
684
- const timestamp = Date.now().toString(36);
685
- const random = Math.random().toString(36).substring(2, 15);
686
- return `idem_${timestamp}_${random}`;
778
+ return formatted;
687
779
  }
688
- var CheckoutService = class {
689
- constructor(client) {
690
- this.client = client;
691
- }
692
- async process(data) {
693
- const checkoutData = {
694
- ...data,
695
- idempotency_key: data.idempotency_key || generateIdempotencyKey()
696
- };
697
- return safe3(
698
- this.client.call(CHECKOUT_MUTATION.PROCESS, {
699
- checkout_data: checkoutData
700
- })
701
- );
780
+ function formatPriceCompact(amount, currency = "GHS", decimals = 1) {
781
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
782
+ if (isNaN(numAmount)) {
783
+ return `${getCurrencySymbol(currency)}0`;
702
784
  }
703
- async initializePayment(orderId, method) {
704
- return safe3(
705
- this.client.call("order.initializePayment", {
706
- order_id: orderId,
707
- payment_method: method
708
- })
709
- );
785
+ const symbol = getCurrencySymbol(currency);
786
+ if (Math.abs(numAmount) < 1e3) {
787
+ return `${symbol}${numAmount.toFixed(2)}`;
710
788
  }
711
- async submitAuthorization(input) {
712
- return safe3(
713
- this.client.call(PAYMENT_MUTATION.SUBMIT_AUTHORIZATION, input)
714
- );
789
+ return `${symbol}${formatNumberCompact(numAmount, decimals)}`;
790
+ }
791
+ function formatMoney(amount, currency = "GHS") {
792
+ const symbol = getCurrencySymbol(currency);
793
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
794
+ if (isNaN(numAmount)) {
795
+ return `${symbol}0.00`;
715
796
  }
716
- async pollPaymentStatus(orderId) {
717
- return safe3(
718
- this.client.call(PAYMENT_MUTATION.CHECK_STATUS, orderId)
719
- );
797
+ return `${symbol}${numAmount.toFixed(2)}`;
798
+ }
799
+ function parsePrice(value) {
800
+ if (value === void 0 || value === null) {
801
+ return 0;
720
802
  }
721
- async updateOrderCustomer(orderId, customer) {
722
- return safe3(
723
- this.client.call(ORDER_MUTATION.UPDATE_CUSTOMER, {
724
- order_id: orderId,
725
- ...customer
726
- })
727
- );
803
+ if (typeof value === "number") {
804
+ return isNaN(value) ? 0 : value;
728
805
  }
729
- async verifyPayment(orderId) {
730
- return safe3(
731
- this.client.call("order.verifyPayment", {
732
- order_id: orderId
733
- })
734
- );
806
+ const cleaned = value.replace(/[^\d.-]/g, "");
807
+ const parsed = parseFloat(cleaned);
808
+ return isNaN(parsed) ? 0 : parsed;
809
+ }
810
+ function getDisplayPrice(product) {
811
+ if (product.price_info) {
812
+ return parsePrice(product.price_info.final_price);
735
813
  }
736
- };
737
-
738
- // src/orders.ts
739
- function toCimplifyError4(error) {
740
- if (error instanceof CimplifyError) return error;
741
- if (error instanceof Error) {
742
- return new CimplifyError("UNKNOWN_ERROR", error.message, false);
814
+ if (product.final_price !== void 0 && product.final_price !== null) {
815
+ return parsePrice(product.final_price);
743
816
  }
744
- return new CimplifyError("UNKNOWN_ERROR", String(error), false);
745
- }
746
- async function safe4(promise) {
747
- try {
748
- return ok(await promise);
749
- } catch (error) {
750
- return err(toCimplifyError4(error));
817
+ if (product.default_price !== void 0 && product.default_price !== null) {
818
+ return parsePrice(product.default_price);
751
819
  }
820
+ return 0;
752
821
  }
753
- var OrderQueries = class {
754
- constructor(client) {
755
- this.client = client;
756
- }
757
- async list(options) {
758
- let query2 = "orders";
759
- if (options?.status) {
760
- query2 += `[?(@.status=='${options.status}')]`;
761
- }
762
- query2 += "#sort(created_at,desc)";
763
- if (options?.limit) {
764
- query2 += `#limit(${options.limit})`;
765
- }
766
- if (options?.offset) {
767
- query2 += `#offset(${options.offset})`;
768
- }
769
- return safe4(this.client.query(query2));
822
+ function getBasePrice(product) {
823
+ if (product.price_info) {
824
+ return parsePrice(product.price_info.base_price);
770
825
  }
771
- async get(orderId) {
772
- return safe4(this.client.query(`orders.${orderId}`));
826
+ if (product.base_price !== void 0 && product.base_price !== null) {
827
+ return parsePrice(product.base_price);
773
828
  }
774
- async getRecent(limit = 5) {
775
- return safe4(this.client.query(`orders#sort(created_at,desc)#limit(${limit})`));
829
+ if (product.default_price !== void 0 && product.default_price !== null) {
830
+ return parsePrice(product.default_price);
776
831
  }
777
- async getByStatus(status) {
778
- return safe4(this.client.query(`orders[?(@.status=='${status}')]`));
832
+ return 0;
833
+ }
834
+ function isOnSale(product) {
835
+ const basePrice = getBasePrice(product);
836
+ const finalPrice = getDisplayPrice(product);
837
+ return basePrice > finalPrice && basePrice > 0;
838
+ }
839
+ function getDiscountPercentage(product) {
840
+ const basePrice = getBasePrice(product);
841
+ const finalPrice = getDisplayPrice(product);
842
+ if (basePrice > finalPrice && basePrice > 0) {
843
+ return Math.round((basePrice - finalPrice) / basePrice * 100);
779
844
  }
780
- async cancel(orderId, reason) {
781
- return safe4(
782
- this.client.call("order.cancelOrder", {
783
- order_id: orderId,
784
- reason
785
- })
786
- );
787
- }
788
- };
789
-
790
- // src/link.ts
791
- function toCimplifyError5(error) {
792
- if (error instanceof CimplifyError) return error;
793
- if (error instanceof Error) {
794
- return new CimplifyError("UNKNOWN_ERROR", error.message, false);
795
- }
796
- return new CimplifyError("UNKNOWN_ERROR", String(error), false);
845
+ return 0;
797
846
  }
798
- async function safe5(promise) {
799
- try {
800
- return ok(await promise);
801
- } catch (error) {
802
- return err(toCimplifyError5(error));
847
+ function getMarkupPercentage(product) {
848
+ const basePrice = getBasePrice(product);
849
+ const finalPrice = getDisplayPrice(product);
850
+ if (finalPrice > basePrice && basePrice > 0) {
851
+ return Math.round((finalPrice - basePrice) / basePrice * 100);
803
852
  }
853
+ return 0;
804
854
  }
805
- var LinkService = class {
806
- constructor(client) {
807
- this.client = client;
808
- }
809
- async requestOtp(input) {
810
- return safe5(this.client.linkPost("/v1/link/auth/request-otp", input));
811
- }
812
- async verifyOtp(input) {
813
- const result = await safe5(
814
- this.client.linkPost("/v1/link/auth/verify-otp", input)
815
- );
816
- if (result.ok && result.value.session_token) {
817
- this.client.setSessionToken(result.value.session_token);
818
- }
819
- return result;
820
- }
821
- async logout() {
822
- const result = await safe5(this.client.linkPost("/v1/link/auth/logout"));
823
- if (result.ok) {
824
- this.client.clearSession();
825
- }
826
- return result;
827
- }
828
- async checkStatus(contact) {
829
- return safe5(
830
- this.client.call(LINK_MUTATION.CHECK_STATUS, {
831
- contact
832
- })
833
- );
834
- }
835
- async getLinkData() {
836
- return safe5(this.client.query(LINK_QUERY.DATA));
837
- }
838
- async getAddresses() {
839
- return safe5(this.client.query(LINK_QUERY.ADDRESSES));
840
- }
841
- async getMobileMoney() {
842
- return safe5(this.client.query(LINK_QUERY.MOBILE_MONEY));
843
- }
844
- async getPreferences() {
845
- return safe5(this.client.query(LINK_QUERY.PREFERENCES));
846
- }
847
- async enroll(data) {
848
- return safe5(this.client.call(LINK_MUTATION.ENROLL, data));
849
- }
850
- async enrollAndLinkOrder(data) {
851
- return safe5(
852
- this.client.call(LINK_MUTATION.ENROLL_AND_LINK_ORDER, data)
853
- );
854
- }
855
- async updatePreferences(preferences) {
856
- return safe5(this.client.call(LINK_MUTATION.UPDATE_PREFERENCES, preferences));
857
- }
858
- async createAddress(input) {
859
- return safe5(this.client.call(LINK_MUTATION.CREATE_ADDRESS, input));
860
- }
861
- async updateAddress(input) {
862
- return safe5(this.client.call(LINK_MUTATION.UPDATE_ADDRESS, input));
863
- }
864
- async deleteAddress(addressId) {
865
- return safe5(this.client.call(LINK_MUTATION.DELETE_ADDRESS, addressId));
866
- }
867
- async setDefaultAddress(addressId) {
868
- return safe5(this.client.call(LINK_MUTATION.SET_DEFAULT_ADDRESS, addressId));
869
- }
870
- async trackAddressUsage(addressId) {
871
- return safe5(
872
- this.client.call(LINK_MUTATION.TRACK_ADDRESS_USAGE, {
873
- address_id: addressId
874
- })
875
- );
876
- }
877
- async createMobileMoney(input) {
878
- return safe5(this.client.call(LINK_MUTATION.CREATE_MOBILE_MONEY, input));
879
- }
880
- async deleteMobileMoney(mobileMoneyId) {
881
- return safe5(this.client.call(LINK_MUTATION.DELETE_MOBILE_MONEY, mobileMoneyId));
882
- }
883
- async setDefaultMobileMoney(mobileMoneyId) {
884
- return safe5(
885
- this.client.call(LINK_MUTATION.SET_DEFAULT_MOBILE_MONEY, mobileMoneyId)
886
- );
887
- }
888
- async trackMobileMoneyUsage(mobileMoneyId) {
889
- return safe5(
890
- this.client.call(LINK_MUTATION.TRACK_MOBILE_MONEY_USAGE, {
891
- mobile_money_id: mobileMoneyId
892
- })
893
- );
894
- }
895
- async verifyMobileMoney(mobileMoneyId) {
896
- return safe5(
897
- this.client.call(LINK_MUTATION.VERIFY_MOBILE_MONEY, mobileMoneyId)
898
- );
899
- }
900
- async getSessions() {
901
- return safe5(this.client.linkGet("/v1/link/sessions"));
902
- }
903
- async revokeSession(sessionId) {
904
- return safe5(this.client.linkDelete(`/v1/link/sessions/${sessionId}`));
905
- }
906
- async revokeAllSessions() {
907
- return safe5(this.client.linkDelete("/v1/link/sessions"));
908
- }
909
- async getAddressesRest() {
910
- return safe5(this.client.linkGet("/v1/link/addresses"));
911
- }
912
- async createAddressRest(input) {
913
- return safe5(this.client.linkPost("/v1/link/addresses", input));
914
- }
915
- async deleteAddressRest(addressId) {
916
- return safe5(this.client.linkDelete(`/v1/link/addresses/${addressId}`));
917
- }
918
- async setDefaultAddressRest(addressId) {
919
- return safe5(this.client.linkPost(`/v1/link/addresses/${addressId}/default`));
920
- }
921
- async getMobileMoneyRest() {
922
- return safe5(this.client.linkGet("/v1/link/mobile-money"));
923
- }
924
- async createMobileMoneyRest(input) {
925
- return safe5(this.client.linkPost("/v1/link/mobile-money", input));
926
- }
927
- async deleteMobileMoneyRest(mobileMoneyId) {
928
- return safe5(this.client.linkDelete(`/v1/link/mobile-money/${mobileMoneyId}`));
855
+ function getProductCurrency(product) {
856
+ if (product.price_info?.currency) {
857
+ return product.price_info.currency;
929
858
  }
930
- async setDefaultMobileMoneyRest(mobileMoneyId) {
931
- return safe5(
932
- this.client.linkPost(`/v1/link/mobile-money/${mobileMoneyId}/default`)
933
- );
859
+ if (product.currency) {
860
+ return product.currency;
934
861
  }
935
- };
862
+ return "GHS";
863
+ }
864
+ function formatProductPrice(product, locale = "en-US") {
865
+ const price = getDisplayPrice(product);
866
+ const currency = getProductCurrency(product);
867
+ return formatPrice(price, currency, locale);
868
+ }
936
869
 
937
- // src/auth.ts
938
- function toCimplifyError6(error) {
939
- if (error instanceof CimplifyError) return error;
940
- if (error instanceof Error) {
941
- return new CimplifyError("UNKNOWN_ERROR", error.message, false);
870
+ // src/utils/payment.ts
871
+ function categorizePaymentError(error, errorCode) {
872
+ let message = "An unexpected error occurred during payment processing. Please try again or contact support.";
873
+ let recoverable = true;
874
+ let code = errorCode || "PAYMENT_ERROR";
875
+ const technical = error.stack;
876
+ const errorMessage = error.message?.toLowerCase() || "";
877
+ if (errorCode === "INSUFFICIENT_FUNDS" || errorMessage.includes("insufficient") || errorMessage.includes("funds")) {
878
+ code = "INSUFFICIENT_FUNDS";
879
+ message = "Your payment method has insufficient funds. Please try another payment method.";
880
+ } else if (errorCode === "CARD_DECLINED" || errorMessage.includes("declined")) {
881
+ code = "CARD_DECLINED";
882
+ message = "Your card was declined. Please try another card or payment method.";
883
+ } else if (errorMessage.includes("cancelled") || errorMessage.includes("canceled") || errorCode === "PAYMENT_CANCELLED") {
884
+ code = "PAYMENT_CANCELLED";
885
+ message = "Payment was cancelled. You can try again when ready.";
886
+ } else if (errorMessage.includes("network") || errorMessage.includes("connection") || errorCode === "NETWORK_ERROR") {
887
+ code = "NETWORK_ERROR";
888
+ message = "Network connection issue. Please check your internet connection and try again.";
889
+ } else if (errorMessage.includes("timeout") || errorCode === "TIMEOUT") {
890
+ code = "TIMEOUT";
891
+ message = "Payment processing timed out. Please try again.";
892
+ } else if (errorCode === "PAYMENT_ACTION_NOT_COMPLETED") {
893
+ code = "PAYMENT_ACTION_NOT_COMPLETED";
894
+ message = "Payment action was not completed. Please try again.";
895
+ } else if (errorCode === "AUTHORIZATION_FAILED") {
896
+ code = "AUTHORIZATION_FAILED";
897
+ message = "Authorization failed. Please check your code and try again.";
898
+ } else if (errorCode === "INVALID_OTP") {
899
+ code = "INVALID_OTP";
900
+ message = "Invalid verification code. Please check and try again.";
942
901
  }
943
- return new CimplifyError("UNKNOWN_ERROR", String(error), false);
902
+ return { code, message, recoverable, technical };
944
903
  }
945
- async function safe6(promise) {
946
- try {
947
- return ok(await promise);
948
- } catch (error) {
949
- return err(toCimplifyError6(error));
950
- }
904
+ function isQuickPaymentResponse(response) {
905
+ return response !== null && typeof response === "object" && "payment" in response && typeof response.payment === "object";
951
906
  }
952
- var AuthService = class {
953
- constructor(client) {
954
- this.client = client;
955
- }
956
- async getStatus() {
957
- return safe6(this.client.query("auth"));
958
- }
959
- async getCurrentUser() {
960
- const result = await this.getStatus();
961
- if (!result.ok) return result;
962
- return ok(result.value.customer || null);
907
+ function isWebPaymentResponse(response) {
908
+ return response !== null && typeof response === "object" && "transaction" in response && typeof response.transaction === "object";
909
+ }
910
+ function normalizePaymentResponse(response) {
911
+ if (!response) {
912
+ return {
913
+ method: "unknown",
914
+ provider: "unknown",
915
+ requires_action: false,
916
+ metadata: {}
917
+ };
963
918
  }
964
- async isAuthenticated() {
965
- const result = await this.getStatus();
966
- if (!result.ok) return result;
967
- return ok(result.value.is_authenticated);
919
+ if (isQuickPaymentResponse(response)) {
920
+ return {
921
+ method: response.payment.type?.toLowerCase() || "unknown",
922
+ provider: response.payment.provider?.toLowerCase() || "unknown",
923
+ requires_action: !!response.payment.redirect_url || !!response.payment.access_code,
924
+ public_key: response.payment.public_key,
925
+ client_secret: response.payment.access_code,
926
+ access_code: response.payment.access_code,
927
+ redirect_url: response.payment.redirect_url,
928
+ transaction_id: response.payment.reference,
929
+ order_id: response.order_id,
930
+ reference: response.payment.reference,
931
+ instructions: response.payment.instructions,
932
+ metadata: response.payment.metadata
933
+ };
968
934
  }
969
- async requestOtp(contact, contactType) {
970
- return safe6(
971
- this.client.call(AUTH_MUTATION.REQUEST_OTP, {
972
- contact,
973
- contact_type: contactType
974
- })
975
- );
976
- }
977
- async verifyOtp(code, contact) {
978
- return safe6(
979
- this.client.call(AUTH_MUTATION.VERIFY_OTP, {
980
- otp_code: code,
981
- contact
982
- })
983
- );
984
- }
985
- async logout() {
986
- return safe6(this.client.call("auth.logout"));
987
- }
988
- async updateProfile(input) {
989
- return safe6(this.client.call("auth.update_profile", input));
935
+ if (isWebPaymentResponse(response)) {
936
+ const authType = response.authorization_type?.toLowerCase();
937
+ const validAuthTypes = ["otp", "pin", "phone", "birthday", "address"];
938
+ const safeAuthType = authType && validAuthTypes.includes(authType) ? authType : void 0;
939
+ return {
940
+ provider: response.transaction.provider_type?.toLowerCase() || "unknown",
941
+ requires_action: response.requires_action || false,
942
+ public_key: response.public_key,
943
+ client_secret: response.client_secret,
944
+ redirect_url: response.authorization_url,
945
+ transaction_id: response.transaction.id,
946
+ order_id: response.transaction.order_id,
947
+ reference: response.transaction.provider_reference,
948
+ metadata: response.transaction.metadata,
949
+ method: response.transaction.payment_method?.toLowerCase() || "unknown",
950
+ instructions: response.display_text,
951
+ display_text: response.display_text,
952
+ requires_authorization: response.requires_authorization,
953
+ authorization_type: safeAuthType,
954
+ provider_payment_id: response.provider_payment_id || response.transaction.provider_reference
955
+ };
990
956
  }
991
- async changePassword(input) {
992
- return safe6(this.client.call("auth.change_password", input));
957
+ return {
958
+ method: "unknown",
959
+ provider: "unknown",
960
+ requires_action: false,
961
+ metadata: {}
962
+ };
963
+ }
964
+ var PAYMENT_SUCCESS_STATUSES = /* @__PURE__ */ new Set([
965
+ "success",
966
+ "succeeded",
967
+ "paid",
968
+ "captured",
969
+ "completed",
970
+ "authorized"
971
+ ]);
972
+ var PAYMENT_FAILURE_STATUSES = /* @__PURE__ */ new Set([
973
+ "failed",
974
+ "declined",
975
+ "cancelled",
976
+ "voided",
977
+ "error"
978
+ ]);
979
+ var PAYMENT_REQUIRES_ACTION_STATUSES = /* @__PURE__ */ new Set([
980
+ "requires_action",
981
+ "requires_payment_method",
982
+ "requires_capture"
983
+ ]);
984
+ var PAYMENT_STATUS_ALIAS_MAP = {
985
+ ok: "success",
986
+ done: "success",
987
+ paid: "paid",
988
+ paid_in_full: "paid",
989
+ paid_successfully: "paid",
990
+ succeeded: "success",
991
+ captured: "captured",
992
+ completed: "completed",
993
+ pending_confirmation: "pending_confirmation",
994
+ requires_authorization: "requires_action",
995
+ requires_action: "requires_action",
996
+ requires_payment_method: "requires_payment_method",
997
+ requires_capture: "requires_capture",
998
+ partially_paid: "partially_paid",
999
+ partially_refunded: "partially_refunded",
1000
+ card_declined: "declined",
1001
+ canceled: "cancelled",
1002
+ authorized: "authorized",
1003
+ cancelled: "cancelled",
1004
+ unresolved: "pending"
1005
+ };
1006
+ var KNOWN_PAYMENT_STATUSES = /* @__PURE__ */ new Set([
1007
+ "pending",
1008
+ "processing",
1009
+ "created",
1010
+ "pending_confirmation",
1011
+ "success",
1012
+ "succeeded",
1013
+ "failed",
1014
+ "declined",
1015
+ "authorized",
1016
+ "refunded",
1017
+ "partially_refunded",
1018
+ "partially_paid",
1019
+ "paid",
1020
+ "unpaid",
1021
+ "requires_action",
1022
+ "requires_payment_method",
1023
+ "requires_capture",
1024
+ "captured",
1025
+ "cancelled",
1026
+ "completed",
1027
+ "voided",
1028
+ "error",
1029
+ "unknown"
1030
+ ]);
1031
+ function normalizeStatusToken(status) {
1032
+ return status?.trim().toLowerCase().replace(/[\s-]+/g, "_") ?? "";
1033
+ }
1034
+ function normalizePaymentStatusValue(status) {
1035
+ const normalized = normalizeStatusToken(status);
1036
+ if (Object.prototype.hasOwnProperty.call(PAYMENT_STATUS_ALIAS_MAP, normalized)) {
1037
+ return PAYMENT_STATUS_ALIAS_MAP[normalized];
993
1038
  }
994
- async resetPassword(email) {
995
- return safe6(this.client.call("auth.reset_password", { email }));
1039
+ return KNOWN_PAYMENT_STATUSES.has(normalized) ? normalized : "unknown";
1040
+ }
1041
+ function isPaymentStatusSuccess(status) {
1042
+ const normalizedStatus = normalizePaymentStatusValue(status);
1043
+ return PAYMENT_SUCCESS_STATUSES.has(normalizedStatus);
1044
+ }
1045
+ function isPaymentStatusFailure(status) {
1046
+ const normalizedStatus = normalizePaymentStatusValue(status);
1047
+ return PAYMENT_FAILURE_STATUSES.has(normalizedStatus);
1048
+ }
1049
+ function isPaymentStatusRequiresAction(status) {
1050
+ const normalizedStatus = normalizePaymentStatusValue(status);
1051
+ return PAYMENT_REQUIRES_ACTION_STATUSES.has(normalizedStatus);
1052
+ }
1053
+ function normalizeStatusResponse(response) {
1054
+ if (!response || typeof response !== "object") {
1055
+ return {
1056
+ status: "pending",
1057
+ paid: false,
1058
+ message: "No status available"
1059
+ };
996
1060
  }
1061
+ const res = response;
1062
+ const normalizedStatus = normalizePaymentStatusValue(res.status ?? void 0);
1063
+ const paidValue = res.paid === true;
1064
+ const derivedPaid = paidValue || [
1065
+ "success",
1066
+ "succeeded",
1067
+ "paid",
1068
+ "captured",
1069
+ "authorized",
1070
+ "completed"
1071
+ ].includes(normalizedStatus);
1072
+ return {
1073
+ status: normalizedStatus,
1074
+ paid: derivedPaid,
1075
+ amount: res.amount,
1076
+ currency: res.currency,
1077
+ reference: res.reference,
1078
+ message: res.message || ""
1079
+ };
1080
+ }
1081
+ var MOBILE_MONEY_PROVIDERS = {
1082
+ mtn: { name: "MTN Mobile Money", prefix: ["024", "054", "055", "059"] },
1083
+ vodafone: { name: "Vodafone Cash", prefix: ["020", "050"] },
1084
+ airtel: { name: "AirtelTigo Money", prefix: ["027", "057", "026", "056"] }
997
1085
  };
998
-
999
- // src/business.ts
1000
- function toCimplifyError7(error) {
1001
- if (error instanceof CimplifyError) return error;
1002
- if (error instanceof Error) {
1003
- return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1086
+ function detectMobileMoneyProvider(phoneNumber) {
1087
+ const cleaned = phoneNumber.replace(/\D/g, "");
1088
+ const prefix = cleaned.slice(-9, -6);
1089
+ for (const [provider, info] of Object.entries(MOBILE_MONEY_PROVIDERS)) {
1090
+ if (info.prefix.some((p) => prefix.startsWith(p.slice(1)))) {
1091
+ return provider;
1092
+ }
1004
1093
  }
1005
- return new CimplifyError("UNKNOWN_ERROR", String(error), false);
1094
+ return null;
1006
1095
  }
1007
- async function safe7(promise) {
1096
+
1097
+ // src/utils/paystack.ts
1098
+ async function openPaystackPopup(options, signal) {
1099
+ if (typeof window === "undefined") {
1100
+ return { success: false, error: "PAYSTACK_UNAVAILABLE" };
1101
+ }
1102
+ let PaystackPop;
1008
1103
  try {
1009
- return ok(await promise);
1010
- } catch (error) {
1011
- return err(toCimplifyError7(error));
1104
+ const imported = await import('@paystack/inline-js');
1105
+ PaystackPop = imported.default;
1106
+ } catch {
1107
+ return { success: false, error: "PAYSTACK_UNAVAILABLE" };
1012
1108
  }
1109
+ return new Promise((resolve) => {
1110
+ let settled = false;
1111
+ const resolveOnce = (result) => {
1112
+ if (settled) {
1113
+ return;
1114
+ }
1115
+ settled = true;
1116
+ if (signal) {
1117
+ signal.removeEventListener("abort", onAbort);
1118
+ }
1119
+ resolve(result);
1120
+ };
1121
+ const onAbort = () => {
1122
+ resolveOnce({ success: false, error: "CANCELLED" });
1123
+ };
1124
+ if (signal?.aborted) {
1125
+ resolveOnce({ success: false, error: "CANCELLED" });
1126
+ return;
1127
+ }
1128
+ if (signal) {
1129
+ signal.addEventListener("abort", onAbort, { once: true });
1130
+ }
1131
+ try {
1132
+ const popup = new PaystackPop();
1133
+ popup.newTransaction({
1134
+ key: options.key,
1135
+ email: options.email,
1136
+ amount: options.amount,
1137
+ currency: options.currency,
1138
+ reference: options.reference,
1139
+ accessCode: options.accessCode,
1140
+ onSuccess: (transaction) => {
1141
+ resolveOnce({
1142
+ success: true,
1143
+ reference: transaction.reference ?? options.reference
1144
+ });
1145
+ },
1146
+ onCancel: () => {
1147
+ resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
1148
+ },
1149
+ onError: (error) => {
1150
+ resolveOnce({
1151
+ success: false,
1152
+ error: error?.message || "PAYMENT_FAILED"
1153
+ });
1154
+ }
1155
+ });
1156
+ } catch {
1157
+ resolveOnce({ success: false, error: "POPUP_BLOCKED" });
1158
+ }
1159
+ });
1013
1160
  }
1014
- var BusinessService = class {
1015
- constructor(client) {
1016
- this.client = client;
1161
+
1162
+ // src/utils/checkout-resolver.ts
1163
+ var DEFAULT_POLL_INTERVAL_MS = 3e3;
1164
+ var DEFAULT_MAX_POLL_ATTEMPTS = 60;
1165
+ var MAX_CONSECUTIVE_NETWORK_ERRORS = 5;
1166
+ function normalizeAuthorizationType(value) {
1167
+ return value === "otp" || value === "pin" ? value : void 0;
1168
+ }
1169
+ function formatMoney2(value) {
1170
+ if (typeof value === "number" && Number.isFinite(value)) {
1171
+ return value.toFixed(2);
1017
1172
  }
1018
- async getInfo() {
1019
- return safe7(this.client.query("business.info"));
1173
+ if (typeof value === "string" && value.trim().length > 0) {
1174
+ return value;
1020
1175
  }
1021
- async getByHandle(handle) {
1022
- return safe7(this.client.query(`business.handle.${handle}`));
1176
+ return "0.00";
1177
+ }
1178
+ function createAbortError() {
1179
+ const error = new Error("CANCELLED");
1180
+ error.name = "AbortError";
1181
+ return error;
1182
+ }
1183
+ function isAbortError(error) {
1184
+ if (error instanceof DOMException && error.name === "AbortError") {
1185
+ return true;
1023
1186
  }
1024
- async getByDomain(domain) {
1025
- return safe7(this.client.query("business.domain", { domain }));
1187
+ return error instanceof Error && (error.name === "AbortError" || error.message === "CANCELLED");
1188
+ }
1189
+ var CheckoutResolver = class {
1190
+ constructor(options) {
1191
+ this.client = options.client;
1192
+ this.checkoutData = options.checkoutData;
1193
+ this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
1194
+ this.maxPollAttempts = options.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;
1195
+ this.onStatusChange = options.onStatusChange;
1196
+ this.onAuthorizationRequired = options.onAuthorizationRequired;
1197
+ this.signal = options.signal;
1198
+ this.returnUrl = options.returnUrl;
1199
+ this.enrollInLink = options.enrollInLink !== false;
1200
+ this.businessId = options.businessId;
1201
+ this.addressData = options.addressData;
1202
+ this.paymentData = options.paymentData;
1203
+ this.orderType = options.orderType;
1204
+ this.cartTotal = options.cartTotal;
1205
+ this.cartCurrency = options.cartCurrency;
1206
+ this.allowCardPopup = options.allowCardPopup ?? false;
1207
+ }
1208
+ async resolve(checkoutResult) {
1209
+ try {
1210
+ this.ensureNotAborted();
1211
+ if (!checkoutResult.order_id) {
1212
+ return this.fail("CHECKOUT_FAILED", "Checkout did not return an order ID.", false);
1213
+ }
1214
+ let latestCheckoutResult = checkoutResult;
1215
+ let authorizationType = normalizeAuthorizationType(checkoutResult.authorization_type);
1216
+ let paymentReference = checkoutResult.payment_reference;
1217
+ if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
1218
+ return this.finalizeSuccess(checkoutResult);
1219
+ }
1220
+ if (checkoutResult.client_secret && checkoutResult.public_key) {
1221
+ if (!this.allowCardPopup) {
1222
+ return this.fail(
1223
+ "PAYSTACK_HANDOFF_REQUIRED",
1224
+ "Card authorization requires Elements. Use elements.processCheckout() for card flows.",
1225
+ true
1226
+ );
1227
+ }
1228
+ this.emit("awaiting_authorization", {
1229
+ authorization_type: authorizationType,
1230
+ display_text: "Complete payment in the card authorization popup.",
1231
+ order_id: checkoutResult.order_id,
1232
+ order_number: checkoutResult.order_number,
1233
+ provider: checkoutResult.provider ?? "paystack"
1234
+ });
1235
+ const popupResult = await openPaystackPopup(
1236
+ {
1237
+ key: checkoutResult.public_key,
1238
+ email: this.checkoutData.customer.email || "customer@cimplify.io",
1239
+ accessCode: checkoutResult.client_secret,
1240
+ reference: paymentReference || checkoutResult.client_secret,
1241
+ currency: this.getOrderCurrency(checkoutResult)
1242
+ },
1243
+ this.signal
1244
+ );
1245
+ if (!popupResult.success) {
1246
+ if (popupResult.error === "POPUP_BLOCKED" || popupResult.error === "PAYSTACK_UNAVAILABLE") {
1247
+ return this.fail(
1248
+ "POPUP_BLOCKED",
1249
+ "Unable to open card payment popup. Please allow popups and try again.",
1250
+ true
1251
+ );
1252
+ }
1253
+ return this.fail(
1254
+ popupResult.error || "PAYMENT_CANCELLED",
1255
+ "Card payment was cancelled before completion.",
1256
+ true
1257
+ );
1258
+ }
1259
+ }
1260
+ if (checkoutResult.authorization_url) {
1261
+ if (typeof window !== "undefined" && this.returnUrl) {
1262
+ window.location.assign(checkoutResult.authorization_url);
1263
+ return this.fail(
1264
+ "REDIRECT_REQUIRED",
1265
+ "Redirecting to complete payment authorization.",
1266
+ true
1267
+ );
1268
+ }
1269
+ if (typeof window !== "undefined") {
1270
+ const popup = window.open(
1271
+ checkoutResult.authorization_url,
1272
+ "cimplify-auth",
1273
+ "width=520,height=760,noopener,noreferrer"
1274
+ );
1275
+ if (!popup) {
1276
+ return this.fail(
1277
+ "POPUP_BLOCKED",
1278
+ "Authorization popup was blocked. Please allow popups and retry.",
1279
+ true
1280
+ );
1281
+ }
1282
+ }
1283
+ }
1284
+ if (checkoutResult.requires_authorization || isPaymentStatusRequiresAction(checkoutResult.payment_status)) {
1285
+ const authorization = await this.handleAuthorization({
1286
+ authorizationType,
1287
+ paymentReference,
1288
+ displayText: checkoutResult.display_text,
1289
+ provider: checkoutResult.provider
1290
+ });
1291
+ if (!authorization.ok) {
1292
+ return authorization.result;
1293
+ }
1294
+ if (authorization.value) {
1295
+ latestCheckoutResult = authorization.value;
1296
+ paymentReference = authorization.value.payment_reference || paymentReference;
1297
+ authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
1298
+ if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1299
+ return this.finalizeSuccess(authorization.value);
1300
+ }
1301
+ if (this.isFailureStatus(authorization.value.payment_status)) {
1302
+ return this.fail(
1303
+ "AUTHORIZATION_FAILED",
1304
+ authorization.value.display_text || "Payment authorization failed.",
1305
+ false
1306
+ );
1307
+ }
1308
+ }
1309
+ }
1310
+ return this.pollUntilTerminal({
1311
+ orderId: checkoutResult.order_id,
1312
+ latestCheckoutResult,
1313
+ paymentReference,
1314
+ authorizationType
1315
+ });
1316
+ } catch (error) {
1317
+ if (isAbortError(error)) {
1318
+ return this.fail("CANCELLED", "Checkout was cancelled.", true);
1319
+ }
1320
+ const message = error instanceof Error ? error.message : "Checkout failed unexpectedly.";
1321
+ return this.fail("CHECKOUT_FAILED", message, true);
1322
+ }
1323
+ }
1324
+ async pollUntilTerminal(input) {
1325
+ let consecutiveErrors = 0;
1326
+ let latestCheckoutResult = input.latestCheckoutResult;
1327
+ let paymentReference = input.paymentReference;
1328
+ let authorizationType = input.authorizationType;
1329
+ for (let attempt = 0; attempt < this.maxPollAttempts; attempt++) {
1330
+ this.ensureNotAborted();
1331
+ this.emit("polling", {
1332
+ poll_attempt: attempt,
1333
+ max_poll_attempts: this.maxPollAttempts,
1334
+ order_id: input.orderId,
1335
+ order_number: latestCheckoutResult.order_number
1336
+ });
1337
+ const statusResult = await this.client.checkout.pollPaymentStatus(input.orderId);
1338
+ if (!statusResult.ok) {
1339
+ consecutiveErrors += 1;
1340
+ if (consecutiveErrors >= MAX_CONSECUTIVE_NETWORK_ERRORS) {
1341
+ return this.fail(
1342
+ "NETWORK_ERROR",
1343
+ "Unable to confirm payment due to repeated network errors.",
1344
+ true
1345
+ );
1346
+ }
1347
+ await this.wait(this.pollIntervalMs);
1348
+ continue;
1349
+ }
1350
+ consecutiveErrors = 0;
1351
+ if (statusResult.value.reference) {
1352
+ paymentReference = statusResult.value.reference;
1353
+ }
1354
+ const normalized = normalizeStatusResponse(statusResult.value);
1355
+ if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
1356
+ return this.finalizeSuccess(latestCheckoutResult);
1357
+ }
1358
+ if (isPaymentStatusFailure(normalized.status)) {
1359
+ return this.fail(
1360
+ normalized.status ? normalized.status.toUpperCase() : "PAYMENT_FAILED",
1361
+ normalized.message || "Payment failed during confirmation.",
1362
+ false
1363
+ );
1364
+ }
1365
+ if (isPaymentStatusRequiresAction(normalized.status)) {
1366
+ const authorization = await this.handleAuthorization({
1367
+ authorizationType,
1368
+ paymentReference,
1369
+ displayText: normalized.message || latestCheckoutResult.display_text,
1370
+ provider: latestCheckoutResult.provider
1371
+ });
1372
+ if (!authorization.ok) {
1373
+ return authorization.result;
1374
+ }
1375
+ if (authorization.value) {
1376
+ latestCheckoutResult = authorization.value;
1377
+ paymentReference = authorization.value.payment_reference || paymentReference;
1378
+ authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
1379
+ if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1380
+ return this.finalizeSuccess(authorization.value);
1381
+ }
1382
+ if (this.isFailureStatus(authorization.value.payment_status)) {
1383
+ return this.fail(
1384
+ "AUTHORIZATION_FAILED",
1385
+ authorization.value.display_text || "Payment authorization failed.",
1386
+ false
1387
+ );
1388
+ }
1389
+ }
1390
+ }
1391
+ await this.wait(this.pollIntervalMs);
1392
+ }
1393
+ return this.fail(
1394
+ "PAYMENT_TIMEOUT",
1395
+ "Payment confirmation timed out. Please retry checkout.",
1396
+ true
1397
+ );
1026
1398
  }
1027
- async getSettings() {
1028
- return safe7(this.client.query("business.settings"));
1399
+ async handleAuthorization(input) {
1400
+ const authorizationType = input.authorizationType;
1401
+ const paymentReference = input.paymentReference;
1402
+ if (!authorizationType || !paymentReference) {
1403
+ return { ok: true };
1404
+ }
1405
+ this.emit("awaiting_authorization", {
1406
+ authorization_type: authorizationType,
1407
+ display_text: input.displayText,
1408
+ provider: input.provider
1409
+ });
1410
+ let authorizationResult;
1411
+ const submit = async (code) => {
1412
+ this.ensureNotAborted();
1413
+ const trimmedCode = code.trim();
1414
+ if (!trimmedCode) {
1415
+ throw new Error("Authorization code is required.");
1416
+ }
1417
+ const submitResult = await this.client.checkout.submitAuthorization({
1418
+ reference: paymentReference,
1419
+ auth_type: authorizationType,
1420
+ value: trimmedCode
1421
+ });
1422
+ if (!submitResult.ok) {
1423
+ throw new Error(submitResult.error.message || "Authorization failed.");
1424
+ }
1425
+ authorizationResult = submitResult.value;
1426
+ };
1427
+ try {
1428
+ if (this.onAuthorizationRequired) {
1429
+ await this.onAuthorizationRequired(authorizationType, submit);
1430
+ } else {
1431
+ return {
1432
+ ok: false,
1433
+ result: this.fail(
1434
+ "AUTHORIZATION_REQUIRED",
1435
+ "Authorization callback is required in headless checkout mode.",
1436
+ false
1437
+ )
1438
+ };
1439
+ }
1440
+ } catch (error) {
1441
+ if (isAbortError(error)) {
1442
+ return {
1443
+ ok: false,
1444
+ result: this.fail("CANCELLED", "Checkout was cancelled.", true)
1445
+ };
1446
+ }
1447
+ const message = error instanceof Error ? error.message : "Payment authorization failed.";
1448
+ return {
1449
+ ok: false,
1450
+ result: this.fail("AUTHORIZATION_FAILED", message, true)
1451
+ };
1452
+ }
1453
+ return {
1454
+ ok: true,
1455
+ value: authorizationResult
1456
+ };
1029
1457
  }
1030
- async getTheme() {
1031
- return safe7(this.client.query("business.theme"));
1458
+ async finalizeSuccess(checkoutResult) {
1459
+ this.emit("finalizing", {
1460
+ order_id: checkoutResult.order_id,
1461
+ order_number: checkoutResult.order_number
1462
+ });
1463
+ const enrolledInLink = await this.maybeEnrollInLink(checkoutResult.order_id);
1464
+ this.emit("success", {
1465
+ order_id: checkoutResult.order_id,
1466
+ order_number: checkoutResult.order_number
1467
+ });
1468
+ return {
1469
+ success: true,
1470
+ order: {
1471
+ id: checkoutResult.order_id,
1472
+ order_number: checkoutResult.order_number || checkoutResult.order_id,
1473
+ status: checkoutResult.payment_status || "paid",
1474
+ total: this.getOrderTotal(checkoutResult),
1475
+ currency: this.getOrderCurrency(checkoutResult)
1476
+ },
1477
+ enrolled_in_link: enrolledInLink
1478
+ };
1032
1479
  }
1033
- async getLocations() {
1034
- return safe7(this.client.query("business.locations"));
1480
+ async maybeEnrollInLink(orderId) {
1481
+ if (!this.enrollInLink || !orderId) {
1482
+ return false;
1483
+ }
1484
+ let businessId = this.businessId;
1485
+ if (!businessId) {
1486
+ const businessResult = await this.client.business.getInfo();
1487
+ if (!businessResult.ok || !businessResult.value?.id) {
1488
+ return false;
1489
+ }
1490
+ businessId = businessResult.value.id;
1491
+ }
1492
+ const address = this.getEnrollmentAddress();
1493
+ const mobileMoney = this.getEnrollmentMobileMoney();
1494
+ try {
1495
+ const enrollment = await this.client.link.enrollAndLinkOrder({
1496
+ order_id: orderId,
1497
+ business_id: businessId,
1498
+ order_type: this.orderType || this.checkoutData.order_type,
1499
+ address,
1500
+ mobile_money: mobileMoney
1501
+ });
1502
+ return enrollment.ok && enrollment.value.success;
1503
+ } catch {
1504
+ return false;
1505
+ }
1035
1506
  }
1036
- async getLocation(locationId) {
1037
- return safe7(this.client.query(`business.locations.${locationId}`));
1507
+ getEnrollmentAddress() {
1508
+ const source = this.addressData ?? this.checkoutData.address_info;
1509
+ if (!source?.street_address) {
1510
+ return void 0;
1511
+ }
1512
+ return {
1513
+ label: "Default",
1514
+ street_address: source.street_address,
1515
+ apartment: source.apartment || void 0,
1516
+ city: source.city || "",
1517
+ region: source.region || "",
1518
+ delivery_instructions: source.delivery_instructions || void 0,
1519
+ phone_for_delivery: source.phone_for_delivery || void 0
1520
+ };
1038
1521
  }
1039
- async getHours() {
1040
- return safe7(this.client.query("business.hours"));
1522
+ getEnrollmentMobileMoney() {
1523
+ if (this.paymentData?.type === "mobile_money" && this.paymentData.phone_number && this.paymentData.provider) {
1524
+ return {
1525
+ phone_number: this.paymentData.phone_number,
1526
+ provider: this.paymentData.provider,
1527
+ label: this.paymentData.label || "Mobile Money"
1528
+ };
1529
+ }
1530
+ if (this.checkoutData.mobile_money_details?.phone_number) {
1531
+ return {
1532
+ phone_number: this.checkoutData.mobile_money_details.phone_number,
1533
+ provider: this.checkoutData.mobile_money_details.provider,
1534
+ label: "Mobile Money"
1535
+ };
1536
+ }
1537
+ return void 0;
1041
1538
  }
1042
- async getLocationHours(locationId) {
1043
- return safe7(this.client.query(`business.locations.${locationId}.hours`));
1539
+ getOrderTotal(checkoutResult) {
1540
+ if (checkoutResult.fx?.pay_amount !== void 0) {
1541
+ return formatMoney2(checkoutResult.fx.pay_amount);
1542
+ }
1543
+ if (this.cartTotal !== void 0) {
1544
+ return formatMoney2(this.cartTotal);
1545
+ }
1546
+ return "0.00";
1044
1547
  }
1045
- async getBootstrap() {
1046
- const [businessResult, locationsResult, categoriesResult] = await Promise.all([
1047
- this.getInfo(),
1048
- this.getLocations(),
1049
- safe7(this.client.query("categories#select(id,name,slug)"))
1050
- ]);
1051
- if (!businessResult.ok) return businessResult;
1052
- if (!locationsResult.ok) return locationsResult;
1053
- if (!categoriesResult.ok) return categoriesResult;
1054
- const business = businessResult.value;
1055
- const locations = locationsResult.value;
1056
- const categories = categoriesResult.value;
1057
- const defaultLocation = locations[0];
1058
- return ok({
1059
- business,
1060
- location: defaultLocation,
1061
- locations,
1062
- categories,
1063
- currency: business.default_currency,
1064
- is_open: defaultLocation?.accepts_online_orders ?? false,
1065
- accepts_orders: defaultLocation?.accepts_online_orders ?? false
1548
+ getOrderCurrency(checkoutResult) {
1549
+ if (checkoutResult.fx?.pay_currency) {
1550
+ return checkoutResult.fx.pay_currency;
1551
+ }
1552
+ if (this.cartCurrency) {
1553
+ return this.cartCurrency;
1554
+ }
1555
+ if (this.checkoutData.pay_currency) {
1556
+ return this.checkoutData.pay_currency;
1557
+ }
1558
+ return "GHS";
1559
+ }
1560
+ emit(status, context = {}) {
1561
+ if (!this.onStatusChange) {
1562
+ return;
1563
+ }
1564
+ try {
1565
+ this.onStatusChange(status, context);
1566
+ } catch {
1567
+ }
1568
+ }
1569
+ fail(code, message, recoverable) {
1570
+ this.emit("failed", {
1571
+ display_text: message
1572
+ });
1573
+ return {
1574
+ success: false,
1575
+ error: {
1576
+ code,
1577
+ message,
1578
+ recoverable
1579
+ }
1580
+ };
1581
+ }
1582
+ isSuccessfulStatus(status) {
1583
+ const normalized = normalizeStatusResponse({ status, paid: false });
1584
+ return normalized.paid || isPaymentStatusSuccess(normalized.status);
1585
+ }
1586
+ isFailureStatus(status) {
1587
+ const normalized = normalizeStatusResponse({ status, paid: false });
1588
+ return isPaymentStatusFailure(normalized.status);
1589
+ }
1590
+ ensureNotAborted() {
1591
+ if (this.signal?.aborted) {
1592
+ throw createAbortError();
1593
+ }
1594
+ }
1595
+ wait(ms) {
1596
+ this.ensureNotAborted();
1597
+ return new Promise((resolve, reject) => {
1598
+ const timer = setTimeout(() => {
1599
+ if (this.signal) {
1600
+ this.signal.removeEventListener("abort", onAbort);
1601
+ }
1602
+ resolve();
1603
+ }, ms);
1604
+ const onAbort = () => {
1605
+ clearTimeout(timer);
1606
+ reject(createAbortError());
1607
+ };
1608
+ if (this.signal) {
1609
+ this.signal.addEventListener("abort", onAbort, { once: true });
1610
+ }
1066
1611
  });
1067
1612
  }
1068
1613
  };
1069
1614
 
1070
- // src/inventory.ts
1071
- function toCimplifyError8(error) {
1615
+ // src/checkout.ts
1616
+ function toCimplifyError3(error) {
1072
1617
  if (error instanceof CimplifyError) return error;
1073
1618
  if (error instanceof Error) {
1074
1619
  return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1075
1620
  }
1076
1621
  return new CimplifyError("UNKNOWN_ERROR", String(error), false);
1077
1622
  }
1078
- async function safe8(promise) {
1623
+ async function safe3(promise) {
1079
1624
  try {
1080
1625
  return ok(await promise);
1081
1626
  } catch (error) {
1082
- return err(toCimplifyError8(error));
1627
+ return err(toCimplifyError3(error));
1083
1628
  }
1084
1629
  }
1085
- var InventoryService = class {
1630
+ function toTerminalFailure(code, message, recoverable) {
1631
+ return {
1632
+ success: false,
1633
+ error: {
1634
+ code,
1635
+ message,
1636
+ recoverable
1637
+ }
1638
+ };
1639
+ }
1640
+ function generateIdempotencyKey() {
1641
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
1642
+ return `idem_${crypto.randomUUID()}`;
1643
+ }
1644
+ const timestamp = Date.now().toString(36);
1645
+ const random = Math.random().toString(36).substring(2, 15);
1646
+ return `idem_${timestamp}_${random}`;
1647
+ }
1648
+ var CheckoutService = class {
1086
1649
  constructor(client) {
1087
1650
  this.client = client;
1088
1651
  }
1089
- async getStockLevels() {
1090
- return safe8(this.client.query("inventory.stock_levels"));
1091
- }
1092
- async getProductStock(productId, locationId) {
1093
- if (locationId) {
1094
- return safe8(
1095
- this.client.query("inventory.product", {
1096
- product_id: productId,
1097
- location_id: locationId
1098
- })
1099
- );
1100
- }
1101
- return safe8(
1102
- this.client.query("inventory.product", {
1103
- product_id: productId
1652
+ async process(data) {
1653
+ const checkoutData = {
1654
+ ...data,
1655
+ idempotency_key: data.idempotency_key || generateIdempotencyKey()
1656
+ };
1657
+ return safe3(
1658
+ this.client.call(CHECKOUT_MUTATION.PROCESS, {
1659
+ checkout_data: checkoutData
1104
1660
  })
1105
1661
  );
1106
1662
  }
1107
- async getVariantStock(variantId, locationId) {
1108
- return safe8(
1109
- this.client.query("inventory.variant", {
1110
- variant_id: variantId,
1111
- location_id: locationId
1663
+ async initializePayment(orderId, method) {
1664
+ return safe3(
1665
+ this.client.call("order.initializePayment", {
1666
+ order_id: orderId,
1667
+ payment_method: method
1112
1668
  })
1113
1669
  );
1114
1670
  }
1115
- async checkProductAvailability(productId, quantity, locationId) {
1116
- return safe8(
1117
- this.client.query("inventory.check_availability", {
1118
- product_id: productId,
1119
- quantity,
1120
- location_id: locationId
1121
- })
1671
+ async submitAuthorization(input) {
1672
+ return safe3(
1673
+ this.client.call(PAYMENT_MUTATION.SUBMIT_AUTHORIZATION, input)
1122
1674
  );
1123
1675
  }
1124
- async checkVariantAvailability(variantId, quantity, locationId) {
1125
- return safe8(
1126
- this.client.query("inventory.check_availability", {
1127
- variant_id: variantId,
1128
- quantity,
1129
- location_id: locationId
1130
- })
1676
+ async pollPaymentStatus(orderId) {
1677
+ return safe3(
1678
+ this.client.call(PAYMENT_MUTATION.CHECK_STATUS, orderId)
1131
1679
  );
1132
1680
  }
1133
- async checkMultipleAvailability(items, locationId) {
1134
- const results = await Promise.all(
1135
- items.map(
1136
- (item) => item.variant_id ? this.checkVariantAvailability(item.variant_id, item.quantity, locationId) : this.checkProductAvailability(item.product_id, item.quantity, locationId)
1137
- )
1681
+ async updateOrderCustomer(orderId, customer) {
1682
+ return safe3(
1683
+ this.client.call(ORDER_MUTATION.UPDATE_CUSTOMER, {
1684
+ order_id: orderId,
1685
+ ...customer
1686
+ })
1138
1687
  );
1139
- for (const result of results) {
1140
- if (!result.ok) return result;
1141
- }
1142
- return ok(results.map((r) => r.value));
1143
- }
1144
- async getSummary() {
1145
- return safe8(this.client.query("inventory.summary"));
1146
1688
  }
1147
- async isInStock(productId, locationId) {
1148
- const result = await this.checkProductAvailability(productId, 1, locationId);
1149
- if (!result.ok) return result;
1150
- return ok(result.value.is_available);
1689
+ async verifyPayment(orderId) {
1690
+ return safe3(
1691
+ this.client.call("order.verifyPayment", {
1692
+ order_id: orderId
1693
+ })
1694
+ );
1151
1695
  }
1152
- async getAvailableQuantity(productId, locationId) {
1153
- const result = await this.getProductStock(productId, locationId);
1154
- if (!result.ok) return result;
1155
- return ok(result.value.available_quantity);
1696
+ async processAndResolve(data) {
1697
+ data.on_status_change?.("preparing", {});
1698
+ if (!data.cart_id) {
1699
+ return ok(
1700
+ toTerminalFailure(
1701
+ "INVALID_CART",
1702
+ "A valid cart is required before checkout can start.",
1703
+ false
1704
+ )
1705
+ );
1706
+ }
1707
+ const cartResult = await this.client.cart.get();
1708
+ if (!cartResult.ok || !cartResult.value?.id) {
1709
+ return ok(
1710
+ toTerminalFailure(
1711
+ "INVALID_CART",
1712
+ "Unable to load cart for checkout.",
1713
+ false
1714
+ )
1715
+ );
1716
+ }
1717
+ const cart = cartResult.value;
1718
+ if (cart.id !== data.cart_id || cart.items.length === 0) {
1719
+ return ok(
1720
+ toTerminalFailure(
1721
+ "INVALID_CART",
1722
+ "Cart is empty or no longer valid. Please refresh and try again.",
1723
+ false
1724
+ )
1725
+ );
1726
+ }
1727
+ const checkoutData = {
1728
+ cart_id: data.cart_id,
1729
+ location_id: data.location_id,
1730
+ customer: data.customer,
1731
+ order_type: data.order_type,
1732
+ address_info: data.address_info,
1733
+ payment_method: data.payment_method,
1734
+ mobile_money_details: data.mobile_money_details,
1735
+ special_instructions: data.special_instructions,
1736
+ link_address_id: data.link_address_id,
1737
+ link_payment_method_id: data.link_payment_method_id,
1738
+ idempotency_key: data.idempotency_key || generateIdempotencyKey(),
1739
+ metadata: data.metadata,
1740
+ pay_currency: data.pay_currency,
1741
+ fx_quote_id: data.fx_quote_id
1742
+ };
1743
+ const baseCurrency = (cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase();
1744
+ const payCurrency = data.pay_currency?.trim().toUpperCase();
1745
+ const cartTotalAmount = Number.parseFloat(cart.pricing.total_price || "0");
1746
+ if (payCurrency && payCurrency !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
1747
+ const fxQuoteResult = await this.client.fx.lockQuote({
1748
+ from: baseCurrency,
1749
+ to: payCurrency,
1750
+ amount: cartTotalAmount
1751
+ });
1752
+ if (!fxQuoteResult.ok) {
1753
+ return ok(
1754
+ toTerminalFailure(
1755
+ "FX_QUOTE_FAILED",
1756
+ fxQuoteResult.error.message || "Unable to lock FX quote for checkout.",
1757
+ true
1758
+ )
1759
+ );
1760
+ }
1761
+ checkoutData.pay_currency = payCurrency;
1762
+ checkoutData.fx_quote_id = fxQuoteResult.value.id;
1763
+ }
1764
+ data.on_status_change?.("processing", {});
1765
+ const processResult = await this.process(checkoutData);
1766
+ if (!processResult.ok) {
1767
+ return ok(
1768
+ toTerminalFailure(
1769
+ processResult.error.code || "CHECKOUT_FAILED",
1770
+ processResult.error.message || "Unable to process checkout.",
1771
+ processResult.error.retryable ?? true
1772
+ )
1773
+ );
1774
+ }
1775
+ const resolver = new CheckoutResolver({
1776
+ client: this.client,
1777
+ checkoutData,
1778
+ pollIntervalMs: data.poll_interval_ms,
1779
+ maxPollAttempts: data.max_poll_attempts,
1780
+ onStatusChange: data.on_status_change,
1781
+ onAuthorizationRequired: data.on_authorization_required,
1782
+ signal: data.signal,
1783
+ returnUrl: data.return_url,
1784
+ enrollInLink: data.enroll_in_link,
1785
+ cartTotal: cart.pricing.total_price,
1786
+ cartCurrency: checkoutData.pay_currency || cart.pricing.currency || "GHS",
1787
+ orderType: checkoutData.order_type,
1788
+ allowCardPopup: false
1789
+ });
1790
+ const resolved = await resolver.resolve(processResult.value);
1791
+ return ok(resolved);
1156
1792
  }
1157
1793
  };
1158
1794
 
1159
- // src/scheduling.ts
1160
- function toVariables(input) {
1161
- return Object.fromEntries(Object.entries(input));
1162
- }
1163
- function toCimplifyError9(error) {
1795
+ // src/orders.ts
1796
+ function toCimplifyError4(error) {
1164
1797
  if (error instanceof CimplifyError) return error;
1165
1798
  if (error instanceof Error) {
1166
1799
  return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1167
1800
  }
1168
1801
  return new CimplifyError("UNKNOWN_ERROR", String(error), false);
1169
1802
  }
1170
- async function safe9(promise) {
1803
+ async function safe4(promise) {
1171
1804
  try {
1172
1805
  return ok(await promise);
1173
1806
  } catch (error) {
1174
- return err(toCimplifyError9(error));
1807
+ return err(toCimplifyError4(error));
1175
1808
  }
1176
1809
  }
1177
- var SchedulingService = class {
1810
+ var OrderQueries = class {
1178
1811
  constructor(client) {
1179
1812
  this.client = client;
1180
1813
  }
1181
- async getServices() {
1182
- return safe9(this.client.query("scheduling.services"));
1183
- }
1184
- /**
1185
- * Get a specific service by ID
1186
- * Note: Filters from all services client-side (no single-service endpoint)
1187
- */
1188
- async getService(serviceId) {
1189
- const result = await this.getServices();
1190
- if (!result.ok) return result;
1191
- return ok(result.value.find((s) => s.id === serviceId) || null);
1814
+ async list(options) {
1815
+ let query2 = "orders";
1816
+ if (options?.status) {
1817
+ query2 += `[?(@.status=='${options.status}')]`;
1818
+ }
1819
+ query2 += "#sort(created_at,desc)";
1820
+ if (options?.limit) {
1821
+ query2 += `#limit(${options.limit})`;
1822
+ }
1823
+ if (options?.offset) {
1824
+ query2 += `#offset(${options.offset})`;
1825
+ }
1826
+ return safe4(this.client.query(query2));
1192
1827
  }
1193
- async getAvailableSlots(input) {
1194
- return safe9(
1195
- this.client.query("scheduling.slots", toVariables(input))
1196
- );
1828
+ async get(orderId) {
1829
+ return safe4(this.client.query(`orders.${orderId}`));
1197
1830
  }
1198
- async checkSlotAvailability(input) {
1199
- return safe9(
1200
- this.client.query(
1201
- "scheduling.check_availability",
1202
- toVariables(input)
1203
- )
1204
- );
1205
- }
1206
- async getServiceAvailability(params) {
1207
- return safe9(
1208
- this.client.query(
1209
- "scheduling.availability",
1210
- toVariables(params)
1211
- )
1212
- );
1213
- }
1214
- async getBooking(bookingId) {
1215
- return safe9(this.client.query(`scheduling.${bookingId}`));
1216
- }
1217
- async getCustomerBookings() {
1218
- return safe9(this.client.query("scheduling"));
1831
+ async getRecent(limit = 5) {
1832
+ return safe4(this.client.query(`orders#sort(created_at,desc)#limit(${limit})`));
1219
1833
  }
1220
- async getUpcomingBookings() {
1221
- return safe9(
1222
- this.client.query(
1223
- "scheduling[?(@.status!='completed' && @.status!='cancelled')]#sort(start_time,asc)"
1224
- )
1225
- );
1834
+ async getByStatus(status) {
1835
+ return safe4(this.client.query(`orders[?(@.status=='${status}')]`));
1226
1836
  }
1227
- async getPastBookings(limit = 10) {
1228
- return safe9(
1229
- this.client.query(
1230
- `scheduling[?(@.status=='completed')]#sort(start_time,desc)#limit(${limit})`
1231
- )
1837
+ async cancel(orderId, reason) {
1838
+ return safe4(
1839
+ this.client.call("order.cancelOrder", {
1840
+ order_id: orderId,
1841
+ reason
1842
+ })
1232
1843
  );
1233
1844
  }
1234
- async cancelBooking(input) {
1235
- return safe9(this.client.call("scheduling.cancel_booking", input));
1236
- }
1237
- async rescheduleBooking(input) {
1238
- return safe9(this.client.call("scheduling.reschedule_booking", input));
1239
- }
1240
- async getNextAvailableSlot(serviceId, fromDate) {
1241
- const date = fromDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1242
- const result = await this.getAvailableSlots({
1243
- service_id: serviceId,
1244
- date
1245
- });
1246
- if (!result.ok) return result;
1247
- return ok(result.value.find((slot) => slot.is_available) || null);
1248
- }
1249
- async hasAvailabilityOn(serviceId, date) {
1250
- const result = await this.getAvailableSlots({
1251
- service_id: serviceId,
1252
- date
1253
- });
1254
- if (!result.ok) return result;
1255
- return ok(result.value.some((slot) => slot.is_available));
1256
- }
1257
1845
  };
1258
1846
 
1259
- // src/lite.ts
1260
- function toCimplifyError10(error) {
1847
+ // src/link.ts
1848
+ function toCimplifyError5(error) {
1261
1849
  if (error instanceof CimplifyError) return error;
1262
1850
  if (error instanceof Error) {
1263
1851
  return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1264
1852
  }
1265
1853
  return new CimplifyError("UNKNOWN_ERROR", String(error), false);
1266
1854
  }
1267
- async function safe10(promise) {
1855
+ async function safe5(promise) {
1268
1856
  try {
1269
1857
  return ok(await promise);
1270
1858
  } catch (error) {
1271
- return err(toCimplifyError10(error));
1859
+ return err(toCimplifyError5(error));
1272
1860
  }
1273
1861
  }
1274
- var LiteService = class {
1862
+ var LinkService = class {
1275
1863
  constructor(client) {
1276
1864
  this.client = client;
1277
1865
  }
1278
- async getBootstrap() {
1279
- return safe10(this.client.query("lite.bootstrap"));
1866
+ async requestOtp(input) {
1867
+ return safe5(this.client.linkPost("/v1/link/auth/request-otp", input));
1280
1868
  }
1281
- async getTable(tableId) {
1282
- return safe10(this.client.query(`lite.table.${tableId}`));
1869
+ async verifyOtp(input) {
1870
+ const result = await safe5(
1871
+ this.client.linkPost("/v1/link/auth/verify-otp", input)
1872
+ );
1873
+ if (result.ok && result.value.session_token) {
1874
+ this.client.setSessionToken(result.value.session_token);
1875
+ }
1876
+ return result;
1283
1877
  }
1284
- async getTableByNumber(tableNumber, locationId) {
1285
- return safe10(
1286
- this.client.query("lite.table_by_number", {
1287
- table_number: tableNumber,
1288
- location_id: locationId
1878
+ async logout() {
1879
+ const result = await safe5(this.client.linkPost("/v1/link/auth/logout"));
1880
+ if (result.ok) {
1881
+ this.client.clearSession();
1882
+ }
1883
+ return result;
1884
+ }
1885
+ async checkStatus(contact) {
1886
+ return safe5(
1887
+ this.client.call(LINK_MUTATION.CHECK_STATUS, {
1888
+ contact
1289
1889
  })
1290
1890
  );
1291
1891
  }
1292
- async sendToKitchen(tableId, items) {
1293
- return safe10(
1294
- this.client.call("lite.send_to_kitchen", {
1295
- table_id: tableId,
1296
- items
1297
- })
1892
+ async getLinkData() {
1893
+ return safe5(this.client.query(LINK_QUERY.DATA));
1894
+ }
1895
+ async getAddresses() {
1896
+ return safe5(this.client.query(LINK_QUERY.ADDRESSES));
1897
+ }
1898
+ async getMobileMoney() {
1899
+ return safe5(this.client.query(LINK_QUERY.MOBILE_MONEY));
1900
+ }
1901
+ async getPreferences() {
1902
+ return safe5(this.client.query(LINK_QUERY.PREFERENCES));
1903
+ }
1904
+ async enroll(data) {
1905
+ return safe5(this.client.call(LINK_MUTATION.ENROLL, data));
1906
+ }
1907
+ async enrollAndLinkOrder(data) {
1908
+ return safe5(
1909
+ this.client.call(LINK_MUTATION.ENROLL_AND_LINK_ORDER, data)
1298
1910
  );
1299
1911
  }
1300
- async callWaiter(tableId, reason) {
1301
- return safe10(
1302
- this.client.call("lite.call_waiter", {
1303
- table_id: tableId,
1304
- reason
1912
+ async updatePreferences(preferences) {
1913
+ return safe5(this.client.call(LINK_MUTATION.UPDATE_PREFERENCES, preferences));
1914
+ }
1915
+ async createAddress(input) {
1916
+ return safe5(this.client.call(LINK_MUTATION.CREATE_ADDRESS, input));
1917
+ }
1918
+ async updateAddress(input) {
1919
+ return safe5(this.client.call(LINK_MUTATION.UPDATE_ADDRESS, input));
1920
+ }
1921
+ async deleteAddress(addressId) {
1922
+ return safe5(this.client.call(LINK_MUTATION.DELETE_ADDRESS, addressId));
1923
+ }
1924
+ async setDefaultAddress(addressId) {
1925
+ return safe5(this.client.call(LINK_MUTATION.SET_DEFAULT_ADDRESS, addressId));
1926
+ }
1927
+ async trackAddressUsage(addressId) {
1928
+ return safe5(
1929
+ this.client.call(LINK_MUTATION.TRACK_ADDRESS_USAGE, {
1930
+ address_id: addressId
1305
1931
  })
1306
1932
  );
1307
1933
  }
1308
- async requestBill(tableId) {
1309
- return safe10(
1310
- this.client.call("lite.request_bill", {
1311
- table_id: tableId
1934
+ async createMobileMoney(input) {
1935
+ return safe5(this.client.call(LINK_MUTATION.CREATE_MOBILE_MONEY, input));
1936
+ }
1937
+ async deleteMobileMoney(mobileMoneyId) {
1938
+ return safe5(this.client.call(LINK_MUTATION.DELETE_MOBILE_MONEY, mobileMoneyId));
1939
+ }
1940
+ async setDefaultMobileMoney(mobileMoneyId) {
1941
+ return safe5(
1942
+ this.client.call(LINK_MUTATION.SET_DEFAULT_MOBILE_MONEY, mobileMoneyId)
1943
+ );
1944
+ }
1945
+ async trackMobileMoneyUsage(mobileMoneyId) {
1946
+ return safe5(
1947
+ this.client.call(LINK_MUTATION.TRACK_MOBILE_MONEY_USAGE, {
1948
+ mobile_money_id: mobileMoneyId
1312
1949
  })
1313
1950
  );
1314
1951
  }
1315
- async getMenu() {
1316
- return safe10(this.client.query("lite.menu"));
1952
+ async verifyMobileMoney(mobileMoneyId) {
1953
+ return safe5(
1954
+ this.client.call(LINK_MUTATION.VERIFY_MOBILE_MONEY, mobileMoneyId)
1955
+ );
1317
1956
  }
1318
- async getMenuByCategory(categoryId) {
1319
- return safe10(this.client.query(`lite.menu.category.${categoryId}`));
1957
+ async getSessions() {
1958
+ return safe5(this.client.linkGet("/v1/link/sessions"));
1959
+ }
1960
+ async revokeSession(sessionId) {
1961
+ return safe5(this.client.linkDelete(`/v1/link/sessions/${sessionId}`));
1962
+ }
1963
+ async revokeAllSessions() {
1964
+ return safe5(this.client.linkDelete("/v1/link/sessions"));
1965
+ }
1966
+ async getAddressesRest() {
1967
+ return safe5(this.client.linkGet("/v1/link/addresses"));
1968
+ }
1969
+ async createAddressRest(input) {
1970
+ return safe5(this.client.linkPost("/v1/link/addresses", input));
1971
+ }
1972
+ async deleteAddressRest(addressId) {
1973
+ return safe5(this.client.linkDelete(`/v1/link/addresses/${addressId}`));
1974
+ }
1975
+ async setDefaultAddressRest(addressId) {
1976
+ return safe5(this.client.linkPost(`/v1/link/addresses/${addressId}/default`));
1977
+ }
1978
+ async getMobileMoneyRest() {
1979
+ return safe5(this.client.linkGet("/v1/link/mobile-money"));
1980
+ }
1981
+ async createMobileMoneyRest(input) {
1982
+ return safe5(this.client.linkPost("/v1/link/mobile-money", input));
1983
+ }
1984
+ async deleteMobileMoneyRest(mobileMoneyId) {
1985
+ return safe5(this.client.linkDelete(`/v1/link/mobile-money/${mobileMoneyId}`));
1986
+ }
1987
+ async setDefaultMobileMoneyRest(mobileMoneyId) {
1988
+ return safe5(
1989
+ this.client.linkPost(`/v1/link/mobile-money/${mobileMoneyId}/default`)
1990
+ );
1320
1991
  }
1321
1992
  };
1322
1993
 
1323
- // src/fx.ts
1324
- function toCimplifyError11(error) {
1994
+ // src/auth.ts
1995
+ function toCimplifyError6(error) {
1325
1996
  if (error instanceof CimplifyError) return error;
1326
1997
  if (error instanceof Error) {
1327
1998
  return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1328
1999
  }
1329
2000
  return new CimplifyError("UNKNOWN_ERROR", String(error), false);
1330
2001
  }
1331
- async function safe11(promise) {
2002
+ async function safe6(promise) {
1332
2003
  try {
1333
2004
  return ok(await promise);
1334
2005
  } catch (error) {
1335
- return err(toCimplifyError11(error));
2006
+ return err(toCimplifyError6(error));
1336
2007
  }
1337
2008
  }
1338
- var FxService = class {
2009
+ var AuthService = class {
1339
2010
  constructor(client) {
1340
2011
  this.client = client;
1341
2012
  }
1342
- async getRate(from, to) {
1343
- return safe11(this.client.call("fx.getRate", { from, to }));
2013
+ async getStatus() {
2014
+ return safe6(this.client.query("auth"));
1344
2015
  }
1345
- async lockQuote(request) {
1346
- return safe11(this.client.call("fx.lockQuote", request));
2016
+ async getCurrentUser() {
2017
+ const result = await this.getStatus();
2018
+ if (!result.ok) return result;
2019
+ return ok(result.value.customer || null);
2020
+ }
2021
+ async isAuthenticated() {
2022
+ const result = await this.getStatus();
2023
+ if (!result.ok) return result;
2024
+ return ok(result.value.is_authenticated);
2025
+ }
2026
+ async requestOtp(contact, contactType) {
2027
+ return safe6(
2028
+ this.client.call(AUTH_MUTATION.REQUEST_OTP, {
2029
+ contact,
2030
+ contact_type: contactType
2031
+ })
2032
+ );
2033
+ }
2034
+ async verifyOtp(code, contact) {
2035
+ return safe6(
2036
+ this.client.call(AUTH_MUTATION.VERIFY_OTP, {
2037
+ otp_code: code,
2038
+ contact
2039
+ })
2040
+ );
2041
+ }
2042
+ async logout() {
2043
+ return safe6(this.client.call("auth.logout"));
2044
+ }
2045
+ async updateProfile(input) {
2046
+ return safe6(this.client.call("auth.update_profile", input));
2047
+ }
2048
+ async changePassword(input) {
2049
+ return safe6(this.client.call("auth.change_password", input));
2050
+ }
2051
+ async resetPassword(email) {
2052
+ return safe6(this.client.call("auth.reset_password", { email }));
1347
2053
  }
1348
2054
  };
1349
2055
 
1350
- // src/types/elements.ts
1351
- var ELEMENT_TYPES = {
1352
- AUTH: "auth",
1353
- ADDRESS: "address",
1354
- PAYMENT: "payment"
1355
- };
1356
- var MESSAGE_TYPES = {
1357
- // Parent → Iframe
1358
- INIT: "init",
1359
- SET_TOKEN: "set_token",
1360
- GET_DATA: "get_data",
1361
- REFRESH_TOKEN: "refresh_token",
1362
- LOGOUT: "logout",
1363
- // Iframe → Parent
1364
- READY: "ready",
1365
- HEIGHT_CHANGE: "height_change",
1366
- AUTHENTICATED: "authenticated",
1367
- REQUIRES_OTP: "requires_otp",
1368
- ERROR: "error",
1369
- ADDRESS_CHANGED: "address_changed",
1370
- ADDRESS_SELECTED: "address_selected",
1371
- PAYMENT_METHOD_SELECTED: "payment_method_selected",
1372
- TOKEN_REFRESHED: "token_refreshed",
1373
- LOGOUT_COMPLETE: "logout_complete"
1374
- };
1375
- var EVENT_TYPES = {
1376
- READY: "ready",
1377
- AUTHENTICATED: "authenticated",
1378
- REQUIRES_OTP: "requires_otp",
1379
- ERROR: "error",
1380
- CHANGE: "change",
1381
- BLUR: "blur",
1382
- FOCUS: "focus"
1383
- };
1384
-
1385
- // src/elements.ts
1386
- function mapOrderType(orderType) {
1387
- if (orderType === "dine_in") return "dine-in";
1388
- return orderType ?? "delivery";
1389
- }
1390
- function toCheckoutFormData(data) {
1391
- return {
1392
- cart_id: data.cart_id,
1393
- customer: {
1394
- name: "",
1395
- email: "",
1396
- phone: "",
1397
- save_details: false
1398
- },
1399
- order_type: mapOrderType(data.order_type),
1400
- address_info: data.address ? {
1401
- street_address: data.address.street_address,
1402
- apartment: data.address.apartment,
1403
- city: data.address.city,
1404
- region: data.address.region,
1405
- postal_code: data.address.postal_code,
1406
- country: data.address.country,
1407
- delivery_instructions: data.address.delivery_instructions,
1408
- phone_for_delivery: data.address.phone_for_delivery
1409
- } : {},
1410
- payment_method: data.payment_method?.type ?? "mobile_money",
1411
- mobile_money_details: data.payment_method?.type === "mobile_money" && data.payment_method.phone_number ? {
1412
- phone_number: data.payment_method.phone_number,
1413
- provider: data.payment_method.provider ?? "mtn"
1414
- } : void 0,
1415
- special_instructions: data.notes,
1416
- link_address_id: data.address?.id,
1417
- link_payment_method_id: data.payment_method?.type === "card" && data.payment_method?.id ? data.payment_method.id : void 0
1418
- };
2056
+ // src/business.ts
2057
+ function toCimplifyError7(error) {
2058
+ if (error instanceof CimplifyError) return error;
2059
+ if (error instanceof Error) {
2060
+ return new CimplifyError("UNKNOWN_ERROR", error.message, false);
2061
+ }
2062
+ return new CimplifyError("UNKNOWN_ERROR", String(error), false);
1419
2063
  }
1420
- var DEFAULT_LINK_URL = "https://link.cimplify.io";
1421
- function isAllowedOrigin(origin) {
2064
+ async function safe7(promise) {
1422
2065
  try {
1423
- const url = new URL(origin);
1424
- const hostname = url.hostname;
1425
- if (hostname === "localhost" || hostname === "127.0.0.1") {
1426
- return true;
1427
- }
1428
- if (url.protocol !== "https:") {
1429
- return false;
1430
- }
1431
- return hostname === "cimplify.io" || hostname.endsWith(".cimplify.io");
1432
- } catch {
1433
- return false;
2066
+ return ok(await promise);
2067
+ } catch (error) {
2068
+ return err(toCimplifyError7(error));
1434
2069
  }
1435
2070
  }
1436
- var CimplifyElements = class {
1437
- constructor(client, businessId, options = {}) {
1438
- this.elements = /* @__PURE__ */ new Map();
1439
- this.accessToken = null;
1440
- this.accountId = null;
1441
- this.customerId = null;
1442
- this.addressData = null;
1443
- this.paymentData = null;
2071
+ var BusinessService = class {
2072
+ constructor(client) {
1444
2073
  this.client = client;
1445
- this.businessId = businessId;
1446
- this.linkUrl = options.linkUrl || DEFAULT_LINK_URL;
1447
- this.options = options;
1448
- this.boundHandleMessage = this.handleMessage.bind(this);
1449
- if (typeof window !== "undefined") {
1450
- window.addEventListener("message", this.boundHandleMessage);
1451
- }
1452
2074
  }
1453
- create(type, options = {}) {
1454
- const existing = this.elements.get(type);
1455
- if (existing) return existing;
1456
- const element = new CimplifyElement(type, this.businessId, this.linkUrl, options, this);
1457
- this.elements.set(type, element);
1458
- return element;
2075
+ async getInfo() {
2076
+ return safe7(this.client.query("business.info"));
1459
2077
  }
1460
- getElement(type) {
1461
- return this.elements.get(type);
2078
+ async getByHandle(handle) {
2079
+ return safe7(this.client.query(`business.handle.${handle}`));
1462
2080
  }
1463
- destroy() {
1464
- this.elements.forEach((element) => element.destroy());
1465
- this.elements.clear();
1466
- if (typeof window !== "undefined") {
1467
- window.removeEventListener("message", this.boundHandleMessage);
1468
- }
2081
+ async getByDomain(domain) {
2082
+ return safe7(this.client.query("business.domain", { domain }));
1469
2083
  }
1470
- async submitCheckout(data) {
1471
- const addressElement = this.elements.get(ELEMENT_TYPES.ADDRESS);
1472
- if (addressElement) await addressElement.getData();
1473
- const paymentElement = this.elements.get(ELEMENT_TYPES.PAYMENT);
1474
- if (paymentElement) await paymentElement.getData();
1475
- const internalData = {
1476
- ...data,
1477
- customer: this.accountId ? { account_id: this.accountId, customer_id: this.customerId } : void 0,
1478
- address: this.addressData,
1479
- payment_method: this.paymentData
1480
- };
1481
- const checkoutFormData = toCheckoutFormData(internalData);
1482
- const result = await this.client.checkout.process(checkoutFormData);
1483
- if (result.ok) {
1484
- return {
1485
- success: true,
1486
- order: {
1487
- id: result.value.order_id,
1488
- status: result.value.payment_status,
1489
- total: ""
1490
- // Total is not returned by checkout result
1491
- }
1492
- };
1493
- }
1494
- return {
1495
- success: false,
1496
- error: {
1497
- code: result.error.code || "CHECKOUT_FAILED",
1498
- message: result.error.message || "Checkout failed"
1499
- }
1500
- };
2084
+ async getSettings() {
2085
+ return safe7(this.client.query("business.settings"));
1501
2086
  }
1502
- isAuthenticated() {
1503
- return this.accessToken !== null;
2087
+ async getTheme() {
2088
+ return safe7(this.client.query("business.theme"));
1504
2089
  }
1505
- getAccessToken() {
1506
- return this.accessToken;
2090
+ async getLocations() {
2091
+ return safe7(this.client.query("business.locations"));
1507
2092
  }
1508
- handleMessage(event) {
1509
- if (!isAllowedOrigin(event.origin)) {
1510
- return;
1511
- }
1512
- const message = event.data;
1513
- if (!message?.type) return;
1514
- switch (message.type) {
1515
- case MESSAGE_TYPES.AUTHENTICATED:
1516
- this.accessToken = message.token;
1517
- this.accountId = message.accountId;
1518
- this.customerId = message.customerId;
1519
- this.elements.forEach((element, type) => {
1520
- if (type !== ELEMENT_TYPES.AUTH) {
1521
- element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token: message.token });
1522
- }
1523
- });
1524
- break;
1525
- case MESSAGE_TYPES.TOKEN_REFRESHED:
1526
- this.accessToken = message.token;
1527
- break;
1528
- case MESSAGE_TYPES.ADDRESS_CHANGED:
1529
- case MESSAGE_TYPES.ADDRESS_SELECTED:
1530
- this.addressData = message.address;
1531
- break;
1532
- case MESSAGE_TYPES.PAYMENT_METHOD_SELECTED:
1533
- this.paymentData = message.method;
1534
- break;
1535
- case MESSAGE_TYPES.LOGOUT_COMPLETE:
1536
- this.accessToken = null;
1537
- this.accountId = null;
1538
- this.customerId = null;
1539
- this.addressData = null;
1540
- this.paymentData = null;
1541
- break;
1542
- }
2093
+ async getLocation(locationId) {
2094
+ return safe7(this.client.query(`business.locations.${locationId}`));
1543
2095
  }
1544
- _setAddressData(data) {
1545
- this.addressData = data;
2096
+ async getHours() {
2097
+ return safe7(this.client.query("business.hours"));
1546
2098
  }
1547
- _setPaymentData(data) {
1548
- this.paymentData = data;
2099
+ async getLocationHours(locationId) {
2100
+ return safe7(this.client.query(`business.locations.${locationId}.hours`));
1549
2101
  }
1550
- };
1551
- var CimplifyElement = class {
1552
- constructor(type, businessId, linkUrl, options, parent) {
1553
- this.iframe = null;
1554
- this.container = null;
1555
- this.mounted = false;
1556
- this.eventHandlers = /* @__PURE__ */ new Map();
1557
- this.resolvers = /* @__PURE__ */ new Map();
1558
- this.type = type;
1559
- this.businessId = businessId;
1560
- this.linkUrl = linkUrl;
1561
- this.options = options;
1562
- this.parent = parent;
1563
- this.boundHandleMessage = this.handleMessage.bind(this);
1564
- if (typeof window !== "undefined") {
1565
- window.addEventListener("message", this.boundHandleMessage);
1566
- }
2102
+ async getBootstrap() {
2103
+ const [businessResult, locationsResult, categoriesResult] = await Promise.all([
2104
+ this.getInfo(),
2105
+ this.getLocations(),
2106
+ safe7(this.client.query("categories#select(id,name,slug)"))
2107
+ ]);
2108
+ if (!businessResult.ok) return businessResult;
2109
+ if (!locationsResult.ok) return locationsResult;
2110
+ if (!categoriesResult.ok) return categoriesResult;
2111
+ const business = businessResult.value;
2112
+ const locations = locationsResult.value;
2113
+ const categories = categoriesResult.value;
2114
+ const defaultLocation = locations[0];
2115
+ return ok({
2116
+ business,
2117
+ location: defaultLocation,
2118
+ locations,
2119
+ categories,
2120
+ currency: business.default_currency,
2121
+ is_open: defaultLocation?.accepts_online_orders ?? false,
2122
+ accepts_orders: defaultLocation?.accepts_online_orders ?? false
2123
+ });
1567
2124
  }
1568
- mount(container) {
1569
- if (this.mounted) {
1570
- console.warn(`Element ${this.type} is already mounted`);
1571
- return;
1572
- }
1573
- const target = typeof container === "string" ? document.querySelector(container) : container;
1574
- if (!target) {
1575
- console.error(`Container not found: ${container}`);
1576
- return;
1577
- }
1578
- this.container = target;
1579
- this.createIframe();
1580
- this.mounted = true;
2125
+ };
2126
+
2127
+ // src/inventory.ts
2128
+ function toCimplifyError8(error) {
2129
+ if (error instanceof CimplifyError) return error;
2130
+ if (error instanceof Error) {
2131
+ return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1581
2132
  }
1582
- destroy() {
1583
- if (this.iframe) {
1584
- this.iframe.remove();
1585
- this.iframe = null;
1586
- }
1587
- this.container = null;
1588
- this.mounted = false;
1589
- this.eventHandlers.clear();
1590
- if (typeof window !== "undefined") {
1591
- window.removeEventListener("message", this.boundHandleMessage);
1592
- }
2133
+ return new CimplifyError("UNKNOWN_ERROR", String(error), false);
2134
+ }
2135
+ async function safe8(promise) {
2136
+ try {
2137
+ return ok(await promise);
2138
+ } catch (error) {
2139
+ return err(toCimplifyError8(error));
1593
2140
  }
1594
- on(event, handler) {
1595
- if (!this.eventHandlers.has(event)) {
1596
- this.eventHandlers.set(event, /* @__PURE__ */ new Set());
1597
- }
1598
- this.eventHandlers.get(event).add(handler);
1599
- }
1600
- off(event, handler) {
1601
- this.eventHandlers.get(event)?.delete(handler);
2141
+ }
2142
+ var InventoryService = class {
2143
+ constructor(client) {
2144
+ this.client = client;
1602
2145
  }
1603
- async getData() {
1604
- return new Promise((resolve) => {
1605
- const id = Math.random().toString(36).slice(2);
1606
- this.resolvers.set(id, resolve);
1607
- this.sendMessage({ type: MESSAGE_TYPES.GET_DATA });
1608
- setTimeout(() => {
1609
- if (this.resolvers.has(id)) {
1610
- this.resolvers.delete(id);
1611
- resolve(null);
1612
- }
1613
- }, 5e3);
1614
- });
2146
+ async getStockLevels() {
2147
+ return safe8(this.client.query("inventory.stock_levels"));
1615
2148
  }
1616
- sendMessage(message) {
1617
- if (this.iframe?.contentWindow) {
1618
- this.iframe.contentWindow.postMessage(message, this.linkUrl);
2149
+ async getProductStock(productId, locationId) {
2150
+ if (locationId) {
2151
+ return safe8(
2152
+ this.client.query("inventory.product", {
2153
+ product_id: productId,
2154
+ location_id: locationId
2155
+ })
2156
+ );
1619
2157
  }
2158
+ return safe8(
2159
+ this.client.query("inventory.product", {
2160
+ product_id: productId
2161
+ })
2162
+ );
1620
2163
  }
1621
- createIframe() {
1622
- if (!this.container) return;
1623
- const iframe = document.createElement("iframe");
1624
- const url = new URL(`${this.linkUrl}/elements/${this.type}`);
1625
- url.searchParams.set("businessId", this.businessId);
1626
- if (this.options.prefillEmail) url.searchParams.set("email", this.options.prefillEmail);
1627
- if (this.options.mode) url.searchParams.set("mode", this.options.mode);
1628
- iframe.src = url.toString();
1629
- iframe.style.border = "none";
1630
- iframe.style.width = "100%";
1631
- iframe.style.display = "block";
1632
- iframe.style.overflow = "hidden";
1633
- iframe.setAttribute("allowtransparency", "true");
1634
- iframe.setAttribute("frameborder", "0");
1635
- iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-popups");
1636
- this.iframe = iframe;
1637
- this.container.appendChild(iframe);
1638
- iframe.onload = () => {
1639
- this.sendMessage({
1640
- type: MESSAGE_TYPES.INIT,
1641
- businessId: this.businessId,
1642
- prefillEmail: this.options.prefillEmail
1643
- });
1644
- const token = this.parent.getAccessToken();
1645
- if (token && this.type !== ELEMENT_TYPES.AUTH) {
1646
- this.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
1647
- }
1648
- };
2164
+ async getVariantStock(variantId, locationId) {
2165
+ return safe8(
2166
+ this.client.query("inventory.variant", {
2167
+ variant_id: variantId,
2168
+ location_id: locationId
2169
+ })
2170
+ );
1649
2171
  }
1650
- handleMessage(event) {
1651
- if (!isAllowedOrigin(event.origin)) {
1652
- return;
1653
- }
1654
- const message = event.data;
1655
- if (!message?.type) return;
1656
- switch (message.type) {
1657
- case MESSAGE_TYPES.READY:
1658
- this.emit(EVENT_TYPES.READY, { height: message.height });
1659
- break;
1660
- case MESSAGE_TYPES.HEIGHT_CHANGE:
1661
- if (this.iframe) this.iframe.style.height = `${message.height}px`;
1662
- break;
1663
- case MESSAGE_TYPES.AUTHENTICATED:
1664
- this.emit(EVENT_TYPES.AUTHENTICATED, {
1665
- accountId: message.accountId,
1666
- customerId: message.customerId,
1667
- token: message.token
1668
- });
1669
- break;
1670
- case MESSAGE_TYPES.REQUIRES_OTP:
1671
- this.emit(EVENT_TYPES.REQUIRES_OTP, { contactMasked: message.contactMasked });
1672
- break;
1673
- case MESSAGE_TYPES.ERROR:
1674
- this.emit(EVENT_TYPES.ERROR, { code: message.code, message: message.message });
1675
- break;
1676
- case MESSAGE_TYPES.ADDRESS_CHANGED:
1677
- case MESSAGE_TYPES.ADDRESS_SELECTED:
1678
- this.parent._setAddressData(message.address);
1679
- this.emit(EVENT_TYPES.CHANGE, { address: message.address });
1680
- this.resolveData(message.address);
1681
- break;
1682
- case MESSAGE_TYPES.PAYMENT_METHOD_SELECTED:
1683
- this.parent._setPaymentData(message.method);
1684
- this.emit(EVENT_TYPES.CHANGE, { paymentMethod: message.method });
1685
- this.resolveData(message.method);
1686
- break;
2172
+ async checkProductAvailability(productId, quantity, locationId) {
2173
+ return safe8(
2174
+ this.client.query("inventory.check_availability", {
2175
+ product_id: productId,
2176
+ quantity,
2177
+ location_id: locationId
2178
+ })
2179
+ );
2180
+ }
2181
+ async checkVariantAvailability(variantId, quantity, locationId) {
2182
+ return safe8(
2183
+ this.client.query("inventory.check_availability", {
2184
+ variant_id: variantId,
2185
+ quantity,
2186
+ location_id: locationId
2187
+ })
2188
+ );
2189
+ }
2190
+ async checkMultipleAvailability(items, locationId) {
2191
+ const results = await Promise.all(
2192
+ items.map(
2193
+ (item) => item.variant_id ? this.checkVariantAvailability(item.variant_id, item.quantity, locationId) : this.checkProductAvailability(item.product_id, item.quantity, locationId)
2194
+ )
2195
+ );
2196
+ for (const result of results) {
2197
+ if (!result.ok) return result;
1687
2198
  }
2199
+ return ok(results.map((r) => r.value));
1688
2200
  }
1689
- emit(event, data) {
1690
- this.eventHandlers.get(event)?.forEach((handler) => handler(data));
2201
+ async getSummary() {
2202
+ return safe8(this.client.query("inventory.summary"));
1691
2203
  }
1692
- resolveData(data) {
1693
- this.resolvers.forEach((resolve) => resolve(data));
1694
- this.resolvers.clear();
2204
+ async isInStock(productId, locationId) {
2205
+ const result = await this.checkProductAvailability(productId, 1, locationId);
2206
+ if (!result.ok) return result;
2207
+ return ok(result.value.is_available);
2208
+ }
2209
+ async getAvailableQuantity(productId, locationId) {
2210
+ const result = await this.getProductStock(productId, locationId);
2211
+ if (!result.ok) return result;
2212
+ return ok(result.value.available_quantity);
1695
2213
  }
1696
2214
  };
1697
- function createElements(client, businessId, options) {
1698
- return new CimplifyElements(client, businessId, options);
1699
- }
1700
2215
 
1701
- // src/client.ts
1702
- var ACCESS_TOKEN_STORAGE_KEY = "cimplify_access_token";
1703
- var DEFAULT_TIMEOUT_MS = 3e4;
1704
- var DEFAULT_MAX_RETRIES = 3;
1705
- var DEFAULT_RETRY_DELAY_MS = 1e3;
1706
- function sleep(ms) {
1707
- return new Promise((resolve) => setTimeout(resolve, ms));
2216
+ // src/scheduling.ts
2217
+ function toVariables(input) {
2218
+ return Object.fromEntries(Object.entries(input));
1708
2219
  }
1709
- function isRetryable(error) {
1710
- if (error instanceof TypeError && error.message.includes("fetch")) {
1711
- return true;
2220
+ function toCimplifyError9(error) {
2221
+ if (error instanceof CimplifyError) return error;
2222
+ if (error instanceof Error) {
2223
+ return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1712
2224
  }
1713
- if (error instanceof DOMException && error.name === "AbortError") {
1714
- return true;
2225
+ return new CimplifyError("UNKNOWN_ERROR", String(error), false);
2226
+ }
2227
+ async function safe9(promise) {
2228
+ try {
2229
+ return ok(await promise);
2230
+ } catch (error) {
2231
+ return err(toCimplifyError9(error));
1715
2232
  }
1716
- if (error instanceof CimplifyError) {
1717
- return error.retryable;
2233
+ }
2234
+ var SchedulingService = class {
2235
+ constructor(client) {
2236
+ this.client = client;
1718
2237
  }
1719
- if (error instanceof CimplifyError && error.code === "SERVER_ERROR") {
1720
- return true;
2238
+ async getServices() {
2239
+ return safe9(this.client.query("scheduling.services"));
1721
2240
  }
1722
- return false;
1723
- }
1724
- function toNetworkError(error) {
1725
- if (error instanceof DOMException && error.name === "AbortError") {
1726
- return new CimplifyError(
1727
- ErrorCode.TIMEOUT,
1728
- "Request timed out. Please check your connection and try again.",
1729
- true
2241
+ /**
2242
+ * Get a specific service by ID
2243
+ * Note: Filters from all services client-side (no single-service endpoint)
2244
+ */
2245
+ async getService(serviceId) {
2246
+ const result = await this.getServices();
2247
+ if (!result.ok) return result;
2248
+ return ok(result.value.find((s) => s.id === serviceId) || null);
2249
+ }
2250
+ async getAvailableSlots(input) {
2251
+ return safe9(
2252
+ this.client.query("scheduling.slots", toVariables(input))
1730
2253
  );
1731
2254
  }
1732
- if (error instanceof TypeError && error.message.includes("fetch")) {
1733
- return new CimplifyError(
1734
- ErrorCode.NETWORK_ERROR,
1735
- "Network error. Please check your internet connection.",
1736
- true
2255
+ async checkSlotAvailability(input) {
2256
+ return safe9(
2257
+ this.client.query(
2258
+ "scheduling.check_availability",
2259
+ toVariables(input)
2260
+ )
1737
2261
  );
1738
2262
  }
1739
- if (error instanceof CimplifyError) {
1740
- return error;
2263
+ async getServiceAvailability(params) {
2264
+ return safe9(
2265
+ this.client.query(
2266
+ "scheduling.availability",
2267
+ toVariables(params)
2268
+ )
2269
+ );
1741
2270
  }
1742
- return new CimplifyError(
1743
- ErrorCode.UNKNOWN_ERROR,
1744
- error instanceof Error ? error.message : "An unknown error occurred",
1745
- false
1746
- );
1747
- }
1748
- function deriveUrls() {
1749
- const hostname = typeof window !== "undefined" ? window.location.hostname : "";
1750
- if (hostname === "localhost" || hostname === "127.0.0.1") {
1751
- const protocol = window.location.protocol;
1752
- return {
1753
- baseUrl: `${protocol}//${hostname}:8082`,
1754
- linkApiUrl: `${protocol}//${hostname}:8080`
1755
- };
2271
+ async getBooking(bookingId) {
2272
+ return safe9(this.client.query(`scheduling.${bookingId}`));
1756
2273
  }
1757
- return {
1758
- baseUrl: "https://storefronts.cimplify.io",
1759
- linkApiUrl: "https://api.cimplify.io"
1760
- };
1761
- }
1762
- function getEnvPublicKey() {
1763
- try {
1764
- const env = globalThis.process;
1765
- return env?.env.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || void 0;
1766
- } catch {
1767
- return void 0;
2274
+ async getCustomerBookings() {
2275
+ return safe9(this.client.query("scheduling"));
1768
2276
  }
1769
- }
1770
- var CimplifyClient = class {
1771
- constructor(config = {}) {
1772
- this.accessToken = null;
1773
- this.context = {};
1774
- this.inflightRequests = /* @__PURE__ */ new Map();
1775
- this.publicKey = config.publicKey || getEnvPublicKey() || "";
1776
- const urls = deriveUrls();
1777
- this.baseUrl = urls.baseUrl;
1778
- this.linkApiUrl = urls.linkApiUrl;
1779
- this.credentials = config.credentials || "include";
1780
- this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
1781
- this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
1782
- this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
1783
- this.hooks = config.hooks ?? {};
1784
- this.accessToken = this.loadAccessToken();
1785
- if (!this.publicKey) {
1786
- console.warn(
1787
- '[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "pk_..." } to createCimplifyClient().'
1788
- );
1789
- }
2277
+ async getUpcomingBookings() {
2278
+ return safe9(
2279
+ this.client.query(
2280
+ "scheduling[?(@.status!='completed' && @.status!='cancelled')]#sort(start_time,asc)"
2281
+ )
2282
+ );
1790
2283
  }
1791
- /** @deprecated Use getAccessToken() instead */
1792
- getSessionToken() {
1793
- return this.accessToken;
2284
+ async getPastBookings(limit = 10) {
2285
+ return safe9(
2286
+ this.client.query(
2287
+ `scheduling[?(@.status=='completed')]#sort(start_time,desc)#limit(${limit})`
2288
+ )
2289
+ );
1794
2290
  }
1795
- /** @deprecated Use setAccessToken() instead */
1796
- setSessionToken(token) {
1797
- this.setAccessToken(token);
2291
+ async cancelBooking(input) {
2292
+ return safe9(this.client.call("scheduling.cancel_booking", input));
1798
2293
  }
1799
- getAccessToken() {
1800
- return this.accessToken;
2294
+ async rescheduleBooking(input) {
2295
+ return safe9(this.client.call("scheduling.reschedule_booking", input));
1801
2296
  }
1802
- setAccessToken(token) {
1803
- const previous = this.accessToken;
1804
- this.accessToken = token;
1805
- this.saveAccessToken(token);
1806
- this.hooks.onSessionChange?.({
1807
- previousToken: previous,
1808
- newToken: token,
1809
- source: "manual"
2297
+ async getNextAvailableSlot(serviceId, fromDate) {
2298
+ const date = fromDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2299
+ const result = await this.getAvailableSlots({
2300
+ service_id: serviceId,
2301
+ date
1810
2302
  });
2303
+ if (!result.ok) return result;
2304
+ return ok(result.value.find((slot) => slot.is_available) || null);
1811
2305
  }
1812
- clearSession() {
1813
- const previous = this.accessToken;
1814
- this.accessToken = null;
1815
- this.saveAccessToken(null);
1816
- this.hooks.onSessionChange?.({
1817
- previousToken: previous,
1818
- newToken: null,
1819
- source: "clear"
2306
+ async hasAvailabilityOn(serviceId, date) {
2307
+ const result = await this.getAvailableSlots({
2308
+ service_id: serviceId,
2309
+ date
1820
2310
  });
2311
+ if (!result.ok) return result;
2312
+ return ok(result.value.some((slot) => slot.is_available));
1821
2313
  }
1822
- /** Set the active location/branch for all subsequent requests */
1823
- setLocationId(locationId) {
1824
- if (locationId) {
1825
- this.context.location_id = locationId;
1826
- } else {
1827
- delete this.context.location_id;
1828
- }
2314
+ };
2315
+
2316
+ // src/lite.ts
2317
+ function toCimplifyError10(error) {
2318
+ if (error instanceof CimplifyError) return error;
2319
+ if (error instanceof Error) {
2320
+ return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1829
2321
  }
1830
- /** Get the currently active location ID */
1831
- getLocationId() {
1832
- return this.context.location_id ?? null;
2322
+ return new CimplifyError("UNKNOWN_ERROR", String(error), false);
2323
+ }
2324
+ async function safe10(promise) {
2325
+ try {
2326
+ return ok(await promise);
2327
+ } catch (error) {
2328
+ return err(toCimplifyError10(error));
1833
2329
  }
1834
- loadAccessToken() {
1835
- if (typeof window !== "undefined" && window.localStorage) {
1836
- return localStorage.getItem(ACCESS_TOKEN_STORAGE_KEY);
1837
- }
1838
- return null;
2330
+ }
2331
+ var LiteService = class {
2332
+ constructor(client) {
2333
+ this.client = client;
1839
2334
  }
1840
- saveAccessToken(token) {
1841
- if (typeof window !== "undefined" && window.localStorage) {
1842
- if (token) {
1843
- localStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, token);
1844
- } else {
1845
- localStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY);
1846
- }
1847
- }
2335
+ async getBootstrap() {
2336
+ return safe10(this.client.query("lite.bootstrap"));
1848
2337
  }
1849
- getHeaders() {
1850
- const headers = {
1851
- "Content-Type": "application/json",
1852
- "X-API-Key": this.publicKey
1853
- };
1854
- if (this.accessToken) {
1855
- headers["Authorization"] = `Bearer ${this.accessToken}`;
1856
- }
1857
- return headers;
2338
+ async getTable(tableId) {
2339
+ return safe10(this.client.query(`lite.table.${tableId}`));
1858
2340
  }
1859
- async resilientFetch(url, options) {
1860
- const method = options.method || "GET";
1861
- const path = url.replace(this.baseUrl, "").replace(this.linkApiUrl, "");
1862
- const startTime = Date.now();
1863
- let retryCount = 0;
1864
- const context = {
1865
- method,
1866
- path,
1867
- url,
1868
- body: options.body ? JSON.parse(options.body) : void 0,
1869
- startTime
1870
- };
1871
- this.hooks.onRequestStart?.(context);
1872
- let lastError;
1873
- for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
1874
- try {
1875
- const controller = new AbortController();
1876
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
1877
- const response = await fetch(url, {
1878
- ...options,
1879
- signal: controller.signal
1880
- });
1881
- clearTimeout(timeoutId);
1882
- if (response.ok || response.status >= 400 && response.status < 500) {
1883
- this.hooks.onRequestSuccess?.({
1884
- ...context,
1885
- status: response.status,
1886
- durationMs: Date.now() - startTime
1887
- });
1888
- return response;
1889
- }
1890
- if (response.status >= 500 && attempt < this.maxRetries) {
1891
- retryCount++;
1892
- const delay = this.retryDelay * Math.pow(2, attempt);
1893
- this.hooks.onRetry?.({
1894
- ...context,
1895
- attempt: retryCount,
1896
- delayMs: delay,
1897
- error: new Error(`Server error: ${response.status}`)
1898
- });
1899
- await sleep(delay);
1900
- continue;
1901
- }
1902
- this.hooks.onRequestSuccess?.({
1903
- ...context,
1904
- status: response.status,
1905
- durationMs: Date.now() - startTime
1906
- });
1907
- return response;
1908
- } catch (error) {
1909
- lastError = error;
1910
- const networkError = toNetworkError(error);
1911
- const errorRetryable = isRetryable(error);
1912
- if (!errorRetryable || attempt >= this.maxRetries) {
1913
- this.hooks.onRequestError?.({
1914
- ...context,
1915
- error: networkError,
1916
- durationMs: Date.now() - startTime,
1917
- retryCount,
1918
- retryable: errorRetryable
1919
- });
1920
- throw networkError;
1921
- }
1922
- retryCount++;
1923
- const delay = this.retryDelay * Math.pow(2, attempt);
1924
- this.hooks.onRetry?.({
1925
- ...context,
1926
- attempt: retryCount,
1927
- delayMs: delay,
1928
- error: networkError
1929
- });
1930
- await sleep(delay);
1931
- }
1932
- }
1933
- const finalError = toNetworkError(lastError);
1934
- this.hooks.onRequestError?.({
1935
- ...context,
1936
- error: finalError,
1937
- durationMs: Date.now() - startTime,
1938
- retryCount,
1939
- retryable: false
1940
- });
1941
- throw finalError;
2341
+ async getTableByNumber(tableNumber, locationId) {
2342
+ return safe10(
2343
+ this.client.query("lite.table_by_number", {
2344
+ table_number: tableNumber,
2345
+ location_id: locationId
2346
+ })
2347
+ );
1942
2348
  }
1943
- getDedupeKey(type, payload) {
1944
- return `${type}:${JSON.stringify(payload)}`;
2349
+ async sendToKitchen(tableId, items) {
2350
+ return safe10(
2351
+ this.client.call("lite.send_to_kitchen", {
2352
+ table_id: tableId,
2353
+ items
2354
+ })
2355
+ );
1945
2356
  }
1946
- async deduplicatedRequest(key, requestFn) {
1947
- const existing = this.inflightRequests.get(key);
1948
- if (existing) {
1949
- return existing;
1950
- }
1951
- const request = requestFn().finally(() => {
1952
- this.inflightRequests.delete(key);
1953
- });
1954
- this.inflightRequests.set(key, request);
1955
- return request;
2357
+ async callWaiter(tableId, reason) {
2358
+ return safe10(
2359
+ this.client.call("lite.call_waiter", {
2360
+ table_id: tableId,
2361
+ reason
2362
+ })
2363
+ );
1956
2364
  }
1957
- async query(query2, variables) {
1958
- const body = { query: query2 };
1959
- if (variables) {
1960
- body.variables = variables;
1961
- }
1962
- if (Object.keys(this.context).length > 0) {
1963
- body.context = this.context;
1964
- }
1965
- const key = this.getDedupeKey("query", body);
1966
- return this.deduplicatedRequest(key, async () => {
1967
- const response = await this.resilientFetch(`${this.baseUrl}/api/q`, {
1968
- method: "POST",
1969
- credentials: this.credentials,
1970
- headers: this.getHeaders(),
1971
- body: JSON.stringify(body)
1972
- });
1973
- return this.handleResponse(response);
1974
- });
2365
+ async requestBill(tableId) {
2366
+ return safe10(
2367
+ this.client.call("lite.request_bill", {
2368
+ table_id: tableId
2369
+ })
2370
+ );
1975
2371
  }
1976
- async call(method, args) {
1977
- const body = {
1978
- method,
1979
- args: args !== void 0 ? [args] : []
1980
- };
1981
- if (Object.keys(this.context).length > 0) {
1982
- body.context = this.context;
1983
- }
1984
- const response = await this.resilientFetch(`${this.baseUrl}/api/m`, {
1985
- method: "POST",
1986
- credentials: this.credentials,
1987
- headers: this.getHeaders(),
1988
- body: JSON.stringify(body)
1989
- });
1990
- return this.handleResponse(response);
2372
+ async getMenu() {
2373
+ return safe10(this.client.query("lite.menu"));
1991
2374
  }
1992
- async get(path) {
1993
- const key = this.getDedupeKey("get", path);
1994
- return this.deduplicatedRequest(key, async () => {
1995
- const response = await this.resilientFetch(`${this.baseUrl}${path}`, {
1996
- method: "GET",
1997
- credentials: this.credentials,
1998
- headers: this.getHeaders()
1999
- });
2000
- return this.handleRestResponse(response);
2001
- });
2375
+ async getMenuByCategory(categoryId) {
2376
+ return safe10(this.client.query(`lite.menu.category.${categoryId}`));
2002
2377
  }
2003
- async post(path, body) {
2004
- const response = await this.resilientFetch(`${this.baseUrl}${path}`, {
2005
- method: "POST",
2006
- credentials: this.credentials,
2007
- headers: this.getHeaders(),
2008
- body: body ? JSON.stringify(body) : void 0
2009
- });
2010
- return this.handleRestResponse(response);
2378
+ };
2379
+
2380
+ // src/fx.ts
2381
+ function toCimplifyError11(error) {
2382
+ if (error instanceof CimplifyError) return error;
2383
+ if (error instanceof Error) {
2384
+ return new CimplifyError("UNKNOWN_ERROR", error.message, false);
2011
2385
  }
2012
- async delete(path) {
2013
- const response = await this.resilientFetch(`${this.baseUrl}${path}`, {
2014
- method: "DELETE",
2015
- credentials: this.credentials,
2016
- headers: this.getHeaders()
2017
- });
2018
- return this.handleRestResponse(response);
2386
+ return new CimplifyError("UNKNOWN_ERROR", String(error), false);
2387
+ }
2388
+ async function safe11(promise) {
2389
+ try {
2390
+ return ok(await promise);
2391
+ } catch (error) {
2392
+ return err(toCimplifyError11(error));
2019
2393
  }
2020
- async linkGet(path) {
2021
- const key = this.getDedupeKey("linkGet", path);
2022
- return this.deduplicatedRequest(key, async () => {
2023
- const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
2024
- method: "GET",
2025
- credentials: this.credentials,
2026
- headers: this.getHeaders()
2027
- });
2028
- return this.handleRestResponse(response);
2029
- });
2394
+ }
2395
+ var FxService = class {
2396
+ constructor(client) {
2397
+ this.client = client;
2030
2398
  }
2031
- async linkPost(path, body) {
2032
- const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
2033
- method: "POST",
2034
- credentials: this.credentials,
2035
- headers: this.getHeaders(),
2036
- body: body ? JSON.stringify(body) : void 0
2037
- });
2038
- return this.handleRestResponse(response);
2399
+ async getRate(from, to) {
2400
+ return safe11(this.client.call("fx.getRate", { from, to }));
2039
2401
  }
2040
- async linkDelete(path) {
2041
- const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
2042
- method: "DELETE",
2043
- credentials: this.credentials,
2044
- headers: this.getHeaders()
2045
- });
2046
- return this.handleRestResponse(response);
2402
+ async lockQuote(request) {
2403
+ return safe11(this.client.call("fx.lockQuote", request));
2047
2404
  }
2048
- async handleRestResponse(response) {
2049
- const json = await response.json();
2050
- if (!response.ok) {
2051
- throw new CimplifyError(
2052
- json.error?.error_code || "API_ERROR",
2053
- json.error?.error_message || "An error occurred",
2054
- false
2055
- );
2405
+ };
2406
+
2407
+ // src/types/elements.ts
2408
+ var ELEMENT_TYPES = {
2409
+ AUTH: "auth",
2410
+ ADDRESS: "address",
2411
+ PAYMENT: "payment"
2412
+ };
2413
+ var MESSAGE_TYPES = {
2414
+ // Parent → Iframe
2415
+ INIT: "init",
2416
+ SET_TOKEN: "set_token",
2417
+ GET_DATA: "get_data",
2418
+ REFRESH_TOKEN: "refresh_token",
2419
+ LOGOUT: "logout",
2420
+ PROCESS_CHECKOUT: "process_checkout",
2421
+ ABORT_CHECKOUT: "abort_checkout",
2422
+ // Iframe → Parent
2423
+ READY: "ready",
2424
+ HEIGHT_CHANGE: "height_change",
2425
+ AUTHENTICATED: "authenticated",
2426
+ REQUIRES_OTP: "requires_otp",
2427
+ ERROR: "error",
2428
+ ADDRESS_CHANGED: "address_changed",
2429
+ ADDRESS_SELECTED: "address_selected",
2430
+ PAYMENT_METHOD_SELECTED: "payment_method_selected",
2431
+ TOKEN_REFRESHED: "token_refreshed",
2432
+ LOGOUT_COMPLETE: "logout_complete",
2433
+ CHECKOUT_STATUS: "checkout_status",
2434
+ CHECKOUT_COMPLETE: "checkout_complete"
2435
+ };
2436
+ var EVENT_TYPES = {
2437
+ READY: "ready",
2438
+ AUTHENTICATED: "authenticated",
2439
+ REQUIRES_OTP: "requires_otp",
2440
+ ERROR: "error",
2441
+ CHANGE: "change",
2442
+ BLUR: "blur",
2443
+ FOCUS: "focus"
2444
+ };
2445
+
2446
+ // src/elements.ts
2447
+ function toCheckoutError(code, message, recoverable) {
2448
+ return {
2449
+ success: false,
2450
+ error: {
2451
+ code,
2452
+ message,
2453
+ recoverable
2056
2454
  }
2057
- return json.data;
2058
- }
2059
- async handleResponse(response) {
2060
- const json = await response.json();
2061
- if (!json.success || json.error) {
2062
- throw new CimplifyError(
2063
- json.error?.code || "UNKNOWN_ERROR",
2064
- json.error?.message || "An unknown error occurred",
2065
- json.error?.retryable || false
2066
- );
2455
+ };
2456
+ }
2457
+ var DEFAULT_LINK_URL = "https://link.cimplify.io";
2458
+ function isAllowedOrigin(origin) {
2459
+ try {
2460
+ const url = new URL(origin);
2461
+ const hostname = url.hostname;
2462
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
2463
+ return true;
2067
2464
  }
2068
- return json.data;
2069
- }
2070
- get catalogue() {
2071
- if (!this._catalogue) {
2072
- this._catalogue = new CatalogueQueries(this);
2465
+ if (url.protocol !== "https:") {
2466
+ return false;
2073
2467
  }
2074
- return this._catalogue;
2468
+ return hostname === "cimplify.io" || hostname.endsWith(".cimplify.io");
2469
+ } catch {
2470
+ return false;
2075
2471
  }
2076
- get cart() {
2077
- if (!this._cart) {
2078
- this._cart = new CartOperations(this);
2079
- }
2080
- return this._cart;
2472
+ }
2473
+ function parseIframeMessage(data) {
2474
+ if (!data || typeof data !== "object") {
2475
+ return null;
2081
2476
  }
2082
- get checkout() {
2083
- if (!this._checkout) {
2084
- this._checkout = new CheckoutService(this);
2085
- }
2086
- return this._checkout;
2477
+ const maybeType = data.type;
2478
+ if (typeof maybeType !== "string") {
2479
+ return null;
2087
2480
  }
2088
- get orders() {
2089
- if (!this._orders) {
2090
- this._orders = new OrderQueries(this);
2481
+ return data;
2482
+ }
2483
+ var CimplifyElements = class {
2484
+ constructor(client, businessId, options = {}) {
2485
+ this.elements = /* @__PURE__ */ new Map();
2486
+ this.accessToken = null;
2487
+ this.accountId = null;
2488
+ this.customerId = null;
2489
+ this.customerData = null;
2490
+ this.addressData = null;
2491
+ this.paymentData = null;
2492
+ this.checkoutInProgress = false;
2493
+ this.activeCheckoutAbort = null;
2494
+ this.client = client;
2495
+ this.businessId = businessId;
2496
+ this.linkUrl = options.linkUrl || DEFAULT_LINK_URL;
2497
+ this.options = options;
2498
+ this.boundHandleMessage = this.handleMessage.bind(this);
2499
+ if (typeof window !== "undefined") {
2500
+ window.addEventListener("message", this.boundHandleMessage);
2091
2501
  }
2092
- return this._orders;
2093
2502
  }
2094
- get link() {
2095
- if (!this._link) {
2096
- this._link = new LinkService(this);
2097
- }
2098
- return this._link;
2503
+ create(type, options = {}) {
2504
+ const existing = this.elements.get(type);
2505
+ if (existing) return existing;
2506
+ const element = new CimplifyElement(type, this.businessId, this.linkUrl, options, this);
2507
+ this.elements.set(type, element);
2508
+ return element;
2099
2509
  }
2100
- get auth() {
2101
- if (!this._auth) {
2102
- this._auth = new AuthService(this);
2103
- }
2104
- return this._auth;
2510
+ getElement(type) {
2511
+ return this.elements.get(type);
2105
2512
  }
2106
- get business() {
2107
- if (!this._business) {
2108
- this._business = new BusinessService(this);
2513
+ destroy() {
2514
+ this.elements.forEach((element) => element.destroy());
2515
+ this.elements.clear();
2516
+ if (typeof window !== "undefined") {
2517
+ window.removeEventListener("message", this.boundHandleMessage);
2109
2518
  }
2110
- return this._business;
2111
2519
  }
2112
- get inventory() {
2113
- if (!this._inventory) {
2114
- this._inventory = new InventoryService(this);
2520
+ async submitCheckout(data) {
2521
+ const result = await this.processCheckout({
2522
+ cart_id: data.cart_id,
2523
+ order_type: data.order_type ?? "delivery",
2524
+ location_id: data.location_id,
2525
+ notes: data.notes,
2526
+ scheduled_time: data.scheduled_time,
2527
+ tip_amount: data.tip_amount,
2528
+ enroll_in_link: true
2529
+ });
2530
+ if (result.success) {
2531
+ return {
2532
+ success: true,
2533
+ order: {
2534
+ id: result.order?.id || "",
2535
+ status: result.order?.status || "unknown",
2536
+ total: result.order?.total || ""
2537
+ }
2538
+ };
2115
2539
  }
2116
- return this._inventory;
2540
+ return {
2541
+ success: false,
2542
+ error: {
2543
+ code: result.error?.code || "CHECKOUT_FAILED",
2544
+ message: result.error?.message || "Checkout failed"
2545
+ }
2546
+ };
2117
2547
  }
2118
- get scheduling() {
2119
- if (!this._scheduling) {
2120
- this._scheduling = new SchedulingService(this);
2121
- }
2122
- return this._scheduling;
2123
- }
2124
- get lite() {
2125
- if (!this._lite) {
2126
- this._lite = new LiteService(this);
2127
- }
2128
- return this._lite;
2548
+ processCheckout(options) {
2549
+ let abortFn = null;
2550
+ const task = (async () => {
2551
+ if (this.checkoutInProgress) {
2552
+ return toCheckoutError(
2553
+ "ALREADY_PROCESSING",
2554
+ "Checkout is already in progress.",
2555
+ false
2556
+ );
2557
+ }
2558
+ if (!options.cart_id) {
2559
+ return toCheckoutError(
2560
+ "INVALID_CART",
2561
+ "A valid cart is required before checkout can start.",
2562
+ false
2563
+ );
2564
+ }
2565
+ if (!options.order_type) {
2566
+ return toCheckoutError(
2567
+ "ORDER_TYPE_REQUIRED",
2568
+ "Order type is required before checkout can start.",
2569
+ false
2570
+ );
2571
+ }
2572
+ const paymentElement = this.elements.get(ELEMENT_TYPES.PAYMENT);
2573
+ if (!paymentElement) {
2574
+ return toCheckoutError(
2575
+ "NO_PAYMENT_ELEMENT",
2576
+ "Payment element must be mounted before checkout.",
2577
+ false
2578
+ );
2579
+ }
2580
+ if (!paymentElement.isMounted()) {
2581
+ return toCheckoutError(
2582
+ "PAYMENT_NOT_MOUNTED",
2583
+ "Payment element must be mounted before checkout.",
2584
+ false
2585
+ );
2586
+ }
2587
+ const authElement = this.elements.get(ELEMENT_TYPES.AUTH);
2588
+ if (authElement && !this.accessToken) {
2589
+ return toCheckoutError(
2590
+ "AUTH_INCOMPLETE",
2591
+ "Authentication must complete before checkout can start.",
2592
+ true
2593
+ );
2594
+ }
2595
+ const addressElement = this.elements.get(ELEMENT_TYPES.ADDRESS);
2596
+ if (addressElement) {
2597
+ await addressElement.getData();
2598
+ }
2599
+ await this.hydrateCustomerData();
2600
+ const processMessage = {
2601
+ type: MESSAGE_TYPES.PROCESS_CHECKOUT,
2602
+ cart_id: options.cart_id,
2603
+ order_type: options.order_type,
2604
+ location_id: options.location_id,
2605
+ notes: options.notes,
2606
+ scheduled_time: options.scheduled_time,
2607
+ tip_amount: options.tip_amount,
2608
+ pay_currency: options.pay_currency,
2609
+ enroll_in_link: options.enroll_in_link ?? true,
2610
+ address: this.addressData ?? void 0,
2611
+ customer: this.customerData ? {
2612
+ name: this.customerData.name,
2613
+ email: this.customerData.email,
2614
+ phone: this.customerData.phone
2615
+ } : null,
2616
+ account_id: this.accountId ?? void 0,
2617
+ customer_id: this.customerId ?? void 0
2618
+ };
2619
+ const timeoutMs = options.timeout_ms ?? 18e4;
2620
+ const paymentWindow = paymentElement.getContentWindow();
2621
+ this.checkoutInProgress = true;
2622
+ return new Promise((resolve) => {
2623
+ let settled = false;
2624
+ const cleanup = () => {
2625
+ if (typeof window !== "undefined") {
2626
+ window.removeEventListener("message", handleCheckoutMessage);
2627
+ }
2628
+ clearTimeout(timeout);
2629
+ this.checkoutInProgress = false;
2630
+ this.activeCheckoutAbort = null;
2631
+ };
2632
+ const settle = (result) => {
2633
+ if (settled) {
2634
+ return;
2635
+ }
2636
+ settled = true;
2637
+ cleanup();
2638
+ resolve(result);
2639
+ };
2640
+ const timeout = setTimeout(() => {
2641
+ settle(
2642
+ toCheckoutError(
2643
+ "TIMEOUT",
2644
+ "Checkout timed out before receiving a terminal response.",
2645
+ true
2646
+ )
2647
+ );
2648
+ }, timeoutMs);
2649
+ const handleCheckoutMessage = (event) => {
2650
+ if (!isAllowedOrigin(event.origin)) {
2651
+ return;
2652
+ }
2653
+ const message = parseIframeMessage(event.data);
2654
+ if (!message) {
2655
+ return;
2656
+ }
2657
+ if (message.type === MESSAGE_TYPES.LOGOUT_COMPLETE) {
2658
+ paymentElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
2659
+ settle(
2660
+ toCheckoutError(
2661
+ "AUTH_LOST",
2662
+ "Authentication was cleared during checkout.",
2663
+ true
2664
+ )
2665
+ );
2666
+ return;
2667
+ }
2668
+ if (paymentWindow && event.source && event.source !== paymentWindow) {
2669
+ return;
2670
+ }
2671
+ if (message.type === MESSAGE_TYPES.CHECKOUT_STATUS) {
2672
+ options.on_status_change?.(message.status, message.context);
2673
+ return;
2674
+ }
2675
+ if (message.type === MESSAGE_TYPES.CHECKOUT_COMPLETE) {
2676
+ settle({
2677
+ success: message.success,
2678
+ order: message.order,
2679
+ error: message.error,
2680
+ enrolled_in_link: message.enrolled_in_link
2681
+ });
2682
+ }
2683
+ };
2684
+ if (typeof window !== "undefined") {
2685
+ window.addEventListener("message", handleCheckoutMessage);
2686
+ }
2687
+ abortFn = () => {
2688
+ paymentElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
2689
+ settle(
2690
+ toCheckoutError(
2691
+ "CANCELLED",
2692
+ "Checkout was cancelled.",
2693
+ true
2694
+ )
2695
+ );
2696
+ };
2697
+ this.activeCheckoutAbort = abortFn;
2698
+ paymentElement.sendMessage(processMessage);
2699
+ });
2700
+ })();
2701
+ const abortable = task;
2702
+ abortable.abort = () => {
2703
+ if (abortFn) {
2704
+ abortFn();
2705
+ }
2706
+ };
2707
+ return abortable;
2129
2708
  }
2130
- get fx() {
2131
- if (!this._fx) {
2132
- this._fx = new FxService(this);
2133
- }
2134
- return this._fx;
2709
+ isAuthenticated() {
2710
+ return this.accessToken !== null;
2135
2711
  }
2136
- /**
2137
- * Create a CimplifyElements instance for embedding checkout components.
2138
- * Like Stripe's stripe.elements().
2139
- *
2140
- * @param businessId - The business ID for checkout context
2141
- * @param options - Optional configuration for elements
2142
- *
2143
- * @example
2144
- * ```ts
2145
- * const elements = client.elements('bus_xxx');
2146
- * const authElement = elements.create('auth');
2147
- * authElement.mount('#auth-container');
2148
- * ```
2149
- */
2150
- elements(businessId, options) {
2151
- return createElements(this, businessId, options);
2712
+ getAccessToken() {
2713
+ return this.accessToken;
2152
2714
  }
2153
- };
2154
- function createCimplifyClient(config = {}) {
2155
- return new CimplifyClient(config);
2156
- }
2157
-
2158
- // src/query/builder.ts
2159
- var QueryBuilder = class {
2160
- constructor(entity) {
2161
- this.filters = [];
2162
- this.modifiers = [];
2163
- this.pathSegments = [];
2164
- this.entity = entity;
2715
+ getPublicKey() {
2716
+ return this.client.getPublicKey();
2165
2717
  }
2166
- path(segment) {
2167
- this.pathSegments.push(segment);
2168
- return this;
2718
+ getAppearance() {
2719
+ return this.options.appearance;
2169
2720
  }
2170
- where(field, op, value) {
2171
- const v = typeof value === "string" ? `'${value}'` : value;
2172
- if (op === "contains" || op === "startsWith") {
2173
- this.filters.push(`@.${field} ${op} ${v}`);
2174
- } else {
2175
- this.filters.push(`@.${field}${op}${v}`);
2721
+ async hydrateCustomerData() {
2722
+ if (!this.accessToken || this.customerData) {
2723
+ return;
2176
2724
  }
2177
- return this;
2725
+ if (!this.client.getAccessToken()) {
2726
+ this.client.setAccessToken(this.accessToken);
2727
+ }
2728
+ const linkDataResult = await this.client.link.getLinkData();
2729
+ if (!linkDataResult.ok || !linkDataResult.value?.customer) {
2730
+ return;
2731
+ }
2732
+ const customer = linkDataResult.value.customer;
2733
+ this.customerData = {
2734
+ name: customer.name || "",
2735
+ email: customer.email || null,
2736
+ phone: customer.phone || null
2737
+ };
2178
2738
  }
2179
- and(field, op, value) {
2180
- return this.where(field, op, value);
2739
+ handleMessage(event) {
2740
+ if (!isAllowedOrigin(event.origin)) {
2741
+ return;
2742
+ }
2743
+ const message = parseIframeMessage(event.data);
2744
+ if (!message) return;
2745
+ switch (message.type) {
2746
+ case MESSAGE_TYPES.AUTHENTICATED:
2747
+ const customer = message.customer ?? {
2748
+ name: "",
2749
+ email: null,
2750
+ phone: null
2751
+ };
2752
+ this.accessToken = message.token;
2753
+ this.accountId = message.accountId;
2754
+ this.customerId = message.customerId;
2755
+ this.customerData = customer;
2756
+ this.client.setAccessToken(message.token);
2757
+ this.elements.forEach((element, type) => {
2758
+ if (type !== ELEMENT_TYPES.AUTH) {
2759
+ element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token: message.token });
2760
+ }
2761
+ });
2762
+ break;
2763
+ case MESSAGE_TYPES.TOKEN_REFRESHED:
2764
+ this.accessToken = message.token;
2765
+ break;
2766
+ case MESSAGE_TYPES.ADDRESS_CHANGED:
2767
+ case MESSAGE_TYPES.ADDRESS_SELECTED:
2768
+ this.addressData = message.address;
2769
+ break;
2770
+ case MESSAGE_TYPES.PAYMENT_METHOD_SELECTED:
2771
+ this.paymentData = message.method;
2772
+ break;
2773
+ case MESSAGE_TYPES.LOGOUT_COMPLETE:
2774
+ if (this.checkoutInProgress && this.activeCheckoutAbort) {
2775
+ this.activeCheckoutAbort();
2776
+ }
2777
+ this.accessToken = null;
2778
+ this.accountId = null;
2779
+ this.customerId = null;
2780
+ this.customerData = null;
2781
+ this.addressData = null;
2782
+ this.paymentData = null;
2783
+ this.client.clearSession();
2784
+ break;
2785
+ }
2181
2786
  }
2182
- sort(field, order = "asc") {
2183
- this.modifiers.push(`sort(${field},${order})`);
2184
- return this;
2787
+ _setAddressData(data) {
2788
+ this.addressData = data;
2185
2789
  }
2186
- limit(n) {
2187
- this.modifiers.push(`limit(${n})`);
2188
- return this;
2790
+ _setPaymentData(data) {
2791
+ this.paymentData = data;
2189
2792
  }
2190
- offset(n) {
2191
- this.modifiers.push(`offset(${n})`);
2192
- return this;
2793
+ };
2794
+ var CimplifyElement = class {
2795
+ constructor(type, businessId, linkUrl, options, parent) {
2796
+ this.iframe = null;
2797
+ this.container = null;
2798
+ this.mounted = false;
2799
+ this.eventHandlers = /* @__PURE__ */ new Map();
2800
+ this.resolvers = /* @__PURE__ */ new Map();
2801
+ this.type = type;
2802
+ this.businessId = businessId;
2803
+ this.linkUrl = linkUrl;
2804
+ this.options = options;
2805
+ this.parent = parent;
2806
+ this.boundHandleMessage = this.handleMessage.bind(this);
2807
+ if (typeof window !== "undefined") {
2808
+ window.addEventListener("message", this.boundHandleMessage);
2809
+ }
2193
2810
  }
2194
- count() {
2195
- this.modifiers.push("count");
2196
- return this;
2811
+ mount(container) {
2812
+ if (this.mounted) {
2813
+ console.warn(`Element ${this.type} is already mounted`);
2814
+ return;
2815
+ }
2816
+ const target = typeof container === "string" ? document.querySelector(container) : container;
2817
+ if (!target) {
2818
+ console.error(`Container not found: ${container}`);
2819
+ return;
2820
+ }
2821
+ this.container = target;
2822
+ this.createIframe();
2823
+ this.mounted = true;
2197
2824
  }
2198
- enriched() {
2199
- this.modifiers.push("enriched");
2200
- return this;
2825
+ destroy() {
2826
+ if (this.iframe) {
2827
+ this.iframe.remove();
2828
+ this.iframe = null;
2829
+ }
2830
+ this.container = null;
2831
+ this.mounted = false;
2832
+ this.eventHandlers.clear();
2833
+ this.resolvers.forEach((entry) => clearTimeout(entry.timeoutId));
2834
+ this.resolvers.clear();
2835
+ if (typeof window !== "undefined") {
2836
+ window.removeEventListener("message", this.boundHandleMessage);
2837
+ }
2201
2838
  }
2202
- build() {
2203
- let query2 = this.entity;
2204
- if (this.pathSegments.length > 0) {
2205
- query2 += "." + this.pathSegments.join(".");
2839
+ on(event, handler) {
2840
+ if (!this.eventHandlers.has(event)) {
2841
+ this.eventHandlers.set(event, /* @__PURE__ */ new Set());
2206
2842
  }
2207
- if (this.filters.length > 0) {
2208
- query2 += `[?(${this.filters.join(" && ")})]`;
2843
+ this.eventHandlers.get(event).add(handler);
2844
+ }
2845
+ off(event, handler) {
2846
+ this.eventHandlers.get(event)?.delete(handler);
2847
+ }
2848
+ async getData() {
2849
+ if (!this.isMounted()) {
2850
+ return null;
2209
2851
  }
2210
- for (const mod of this.modifiers) {
2211
- query2 += `#${mod}`;
2852
+ return new Promise((resolve) => {
2853
+ const id = Math.random().toString(36).slice(2);
2854
+ const timeoutId = setTimeout(() => {
2855
+ const entry = this.resolvers.get(id);
2856
+ if (!entry) {
2857
+ return;
2858
+ }
2859
+ this.resolvers.delete(id);
2860
+ entry.resolve(null);
2861
+ }, 5e3);
2862
+ this.resolvers.set(id, { resolve, timeoutId });
2863
+ this.sendMessage({ type: MESSAGE_TYPES.GET_DATA });
2864
+ });
2865
+ }
2866
+ sendMessage(message) {
2867
+ if (this.iframe?.contentWindow) {
2868
+ this.iframe.contentWindow.postMessage(message, this.linkUrl);
2212
2869
  }
2213
- return query2;
2214
2870
  }
2215
- toString() {
2216
- return this.build();
2871
+ getContentWindow() {
2872
+ return this.iframe?.contentWindow ?? null;
2217
2873
  }
2218
- };
2219
- function query(entity) {
2220
- return new QueryBuilder(entity);
2221
- }
2222
-
2223
- // src/utils/price.ts
2224
- var CURRENCY_SYMBOLS = {
2225
- // Major world currencies
2226
- USD: "$",
2227
- EUR: "\u20AC",
2228
- GBP: "\xA3",
2229
- JPY: "\xA5",
2230
- CNY: "\xA5",
2231
- CHF: "CHF",
2232
- CAD: "C$",
2233
- AUD: "A$",
2234
- NZD: "NZ$",
2235
- HKD: "HK$",
2236
- SGD: "S$",
2237
- INR: "\u20B9",
2238
- BRL: "R$",
2239
- MXN: "MX$",
2240
- KRW: "\u20A9",
2241
- RUB: "\u20BD",
2242
- TRY: "\u20BA",
2243
- THB: "\u0E3F",
2244
- PLN: "z\u0142",
2245
- SEK: "kr",
2246
- NOK: "kr",
2247
- DKK: "kr",
2248
- CZK: "K\u010D",
2249
- HUF: "Ft",
2250
- ILS: "\u20AA",
2251
- AED: "\u062F.\u0625",
2252
- SAR: "\uFDFC",
2253
- MYR: "RM",
2254
- PHP: "\u20B1",
2255
- IDR: "Rp",
2256
- VND: "\u20AB",
2257
- TWD: "NT$",
2258
- // African currencies
2259
- GHS: "GH\u20B5",
2260
- NGN: "\u20A6",
2261
- KES: "KSh",
2262
- ZAR: "R",
2263
- XOF: "CFA",
2264
- XAF: "FCFA",
2265
- EGP: "E\xA3",
2266
- MAD: "MAD",
2267
- TZS: "TSh",
2268
- UGX: "USh",
2269
- RWF: "FRw",
2270
- ETB: "Br",
2271
- ZMW: "ZK",
2272
- BWP: "P",
2273
- MUR: "\u20A8",
2274
- SCR: "\u20A8",
2275
- NAD: "N$",
2276
- SZL: "E",
2277
- LSL: "L",
2278
- MWK: "MK",
2279
- AOA: "Kz",
2280
- CDF: "FC",
2281
- GMD: "D",
2282
- GNF: "FG",
2283
- LRD: "L$",
2284
- SLL: "Le",
2285
- MZN: "MT",
2286
- SDG: "SDG",
2287
- SSP: "SSP",
2288
- SOS: "Sh.So.",
2289
- DJF: "Fdj",
2290
- ERN: "Nfk",
2291
- CVE: "$",
2292
- STN: "Db",
2293
- KMF: "CF",
2294
- BIF: "FBu"
2295
- };
2296
- function getCurrencySymbol(currencyCode) {
2297
- return CURRENCY_SYMBOLS[currencyCode.toUpperCase()] || currencyCode;
2298
- }
2299
- function formatNumberCompact(value, decimals = 1) {
2300
- const absValue = Math.abs(value);
2301
- const sign = value < 0 ? "-" : "";
2302
- if (absValue >= 1e9) {
2303
- return `${sign}${(absValue / 1e9).toFixed(decimals)}B`;
2304
- }
2305
- if (absValue >= 1e6) {
2306
- return `${sign}${(absValue / 1e6).toFixed(decimals)}M`;
2307
- }
2308
- if (absValue >= 1e3) {
2309
- return `${sign}${(absValue / 1e3).toFixed(decimals)}K`;
2310
- }
2311
- return `${sign}${absValue.toFixed(decimals)}`;
2312
- }
2313
- function formatPrice(amount, currency = "GHS", locale = "en-US") {
2314
- const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
2315
- if (isNaN(numAmount)) {
2316
- return `${getCurrencySymbol(currency)}0.00`;
2874
+ isMounted() {
2875
+ return this.mounted && Boolean(this.iframe) && this.iframe?.isConnected === true;
2317
2876
  }
2318
- try {
2319
- return new Intl.NumberFormat(locale, {
2320
- style: "currency",
2321
- currency: currency.toUpperCase(),
2322
- minimumFractionDigits: 2,
2323
- maximumFractionDigits: 2
2324
- }).format(numAmount);
2325
- } catch {
2326
- return `${getCurrencySymbol(currency)}${numAmount.toFixed(2)}`;
2877
+ createIframe() {
2878
+ if (!this.container) return;
2879
+ const iframe = document.createElement("iframe");
2880
+ const url = new URL(`${this.linkUrl}/elements/${this.type}`);
2881
+ url.searchParams.set("businessId", this.businessId);
2882
+ if (this.options.prefillEmail) url.searchParams.set("email", this.options.prefillEmail);
2883
+ if (this.options.mode) url.searchParams.set("mode", this.options.mode);
2884
+ iframe.src = url.toString();
2885
+ iframe.style.border = "none";
2886
+ iframe.style.width = "100%";
2887
+ iframe.style.display = "block";
2888
+ iframe.style.overflow = "hidden";
2889
+ iframe.setAttribute("allowtransparency", "true");
2890
+ iframe.setAttribute("frameborder", "0");
2891
+ iframe.setAttribute(
2892
+ "sandbox",
2893
+ "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
2894
+ );
2895
+ this.iframe = iframe;
2896
+ this.container.appendChild(iframe);
2897
+ iframe.onload = () => {
2898
+ const publicKey = this.parent.getPublicKey();
2899
+ this.sendMessage({
2900
+ type: MESSAGE_TYPES.INIT,
2901
+ businessId: this.businessId,
2902
+ publicKey,
2903
+ demoMode: publicKey.length === 0,
2904
+ prefillEmail: this.options.prefillEmail,
2905
+ appearance: this.parent.getAppearance()
2906
+ });
2907
+ const token = this.parent.getAccessToken();
2908
+ if (token && this.type !== ELEMENT_TYPES.AUTH) {
2909
+ this.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
2910
+ }
2911
+ };
2327
2912
  }
2328
- }
2329
- function formatPriceAdjustment(amount, currency = "GHS", locale = "en-US") {
2330
- const formatted = formatPrice(Math.abs(amount), currency, locale);
2331
- if (amount > 0) {
2332
- return `+${formatted}`;
2333
- } else if (amount < 0) {
2334
- return `-${formatted}`;
2913
+ handleMessage(event) {
2914
+ if (!isAllowedOrigin(event.origin)) {
2915
+ return;
2916
+ }
2917
+ const message = parseIframeMessage(event.data);
2918
+ if (!message) return;
2919
+ switch (message.type) {
2920
+ case MESSAGE_TYPES.READY:
2921
+ this.emit(EVENT_TYPES.READY, { height: message.height });
2922
+ break;
2923
+ case MESSAGE_TYPES.HEIGHT_CHANGE:
2924
+ if (this.iframe) this.iframe.style.height = `${message.height}px`;
2925
+ break;
2926
+ case MESSAGE_TYPES.AUTHENTICATED:
2927
+ const customer = message.customer ?? {
2928
+ name: "",
2929
+ email: null,
2930
+ phone: null
2931
+ };
2932
+ this.emit(EVENT_TYPES.AUTHENTICATED, {
2933
+ accountId: message.accountId,
2934
+ customerId: message.customerId,
2935
+ token: message.token,
2936
+ customer
2937
+ });
2938
+ break;
2939
+ case MESSAGE_TYPES.REQUIRES_OTP:
2940
+ this.emit(EVENT_TYPES.REQUIRES_OTP, { contactMasked: message.contactMasked });
2941
+ break;
2942
+ case MESSAGE_TYPES.ERROR:
2943
+ this.emit(EVENT_TYPES.ERROR, { code: message.code, message: message.message });
2944
+ break;
2945
+ case MESSAGE_TYPES.ADDRESS_CHANGED:
2946
+ case MESSAGE_TYPES.ADDRESS_SELECTED:
2947
+ this.parent._setAddressData(message.address);
2948
+ this.emit(EVENT_TYPES.CHANGE, { address: message.address });
2949
+ this.resolveData(message.address);
2950
+ break;
2951
+ case MESSAGE_TYPES.PAYMENT_METHOD_SELECTED:
2952
+ this.parent._setPaymentData(message.method);
2953
+ this.emit(EVENT_TYPES.CHANGE, { paymentMethod: message.method });
2954
+ this.resolveData(message.method);
2955
+ break;
2956
+ }
2335
2957
  }
2336
- return formatted;
2337
- }
2338
- function formatPriceCompact(amount, currency = "GHS", decimals = 1) {
2339
- const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
2340
- if (isNaN(numAmount)) {
2341
- return `${getCurrencySymbol(currency)}0`;
2958
+ emit(event, data) {
2959
+ this.eventHandlers.get(event)?.forEach((handler) => handler(data));
2342
2960
  }
2343
- const symbol = getCurrencySymbol(currency);
2344
- if (Math.abs(numAmount) < 1e3) {
2345
- return `${symbol}${numAmount.toFixed(2)}`;
2961
+ resolveData(data) {
2962
+ this.resolvers.forEach((entry) => {
2963
+ clearTimeout(entry.timeoutId);
2964
+ entry.resolve(data);
2965
+ });
2966
+ this.resolvers.clear();
2346
2967
  }
2347
- return `${symbol}${formatNumberCompact(numAmount, decimals)}`;
2968
+ };
2969
+ function createElements(client, businessId, options) {
2970
+ return new CimplifyElements(client, businessId, options);
2348
2971
  }
2349
- function formatMoney(amount, currency = "GHS") {
2350
- const symbol = getCurrencySymbol(currency);
2351
- const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
2352
- if (isNaN(numAmount)) {
2353
- return `${symbol}0.00`;
2354
- }
2355
- return `${symbol}${numAmount.toFixed(2)}`;
2972
+
2973
+ // src/client.ts
2974
+ var ACCESS_TOKEN_STORAGE_KEY = "cimplify_access_token";
2975
+ var DEFAULT_TIMEOUT_MS = 3e4;
2976
+ var DEFAULT_MAX_RETRIES = 3;
2977
+ var DEFAULT_RETRY_DELAY_MS = 1e3;
2978
+ function sleep(ms) {
2979
+ return new Promise((resolve) => setTimeout(resolve, ms));
2356
2980
  }
2357
- function parsePrice(value) {
2358
- if (value === void 0 || value === null) {
2359
- return 0;
2360
- }
2361
- if (typeof value === "number") {
2362
- return isNaN(value) ? 0 : value;
2981
+ function isRetryable(error) {
2982
+ if (error instanceof TypeError && error.message.includes("fetch")) {
2983
+ return true;
2363
2984
  }
2364
- const cleaned = value.replace(/[^\d.-]/g, "");
2365
- const parsed = parseFloat(cleaned);
2366
- return isNaN(parsed) ? 0 : parsed;
2367
- }
2368
- function getDisplayPrice(product) {
2369
- if (product.price_info) {
2370
- return parsePrice(product.price_info.final_price);
2985
+ if (error instanceof DOMException && error.name === "AbortError") {
2986
+ return true;
2371
2987
  }
2372
- if (product.final_price !== void 0 && product.final_price !== null) {
2373
- return parsePrice(product.final_price);
2988
+ if (error instanceof CimplifyError) {
2989
+ return error.retryable;
2374
2990
  }
2375
- if (product.default_price !== void 0 && product.default_price !== null) {
2376
- return parsePrice(product.default_price);
2991
+ if (error instanceof CimplifyError && error.code === "SERVER_ERROR") {
2992
+ return true;
2377
2993
  }
2378
- return 0;
2994
+ return false;
2379
2995
  }
2380
- function getBasePrice(product) {
2381
- if (product.price_info) {
2382
- return parsePrice(product.price_info.base_price);
2996
+ function toNetworkError(error) {
2997
+ if (error instanceof DOMException && error.name === "AbortError") {
2998
+ return new CimplifyError(
2999
+ ErrorCode.TIMEOUT,
3000
+ "Request timed out. Please check your connection and try again.",
3001
+ true
3002
+ );
2383
3003
  }
2384
- if (product.base_price !== void 0 && product.base_price !== null) {
2385
- return parsePrice(product.base_price);
3004
+ if (error instanceof TypeError && error.message.includes("fetch")) {
3005
+ return new CimplifyError(
3006
+ ErrorCode.NETWORK_ERROR,
3007
+ "Network error. Please check your internet connection.",
3008
+ true
3009
+ );
2386
3010
  }
2387
- if (product.default_price !== void 0 && product.default_price !== null) {
2388
- return parsePrice(product.default_price);
3011
+ if (error instanceof CimplifyError) {
3012
+ return error;
2389
3013
  }
2390
- return 0;
2391
- }
2392
- function isOnSale(product) {
2393
- const basePrice = getBasePrice(product);
2394
- const finalPrice = getDisplayPrice(product);
2395
- return basePrice > finalPrice && basePrice > 0;
3014
+ return new CimplifyError(
3015
+ ErrorCode.UNKNOWN_ERROR,
3016
+ error instanceof Error ? error.message : "An unknown error occurred",
3017
+ false
3018
+ );
2396
3019
  }
2397
- function getDiscountPercentage(product) {
2398
- const basePrice = getBasePrice(product);
2399
- const finalPrice = getDisplayPrice(product);
2400
- if (basePrice > finalPrice && basePrice > 0) {
2401
- return Math.round((basePrice - finalPrice) / basePrice * 100);
3020
+ function deriveUrls() {
3021
+ const hostname = typeof window !== "undefined" ? window.location.hostname : "";
3022
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
3023
+ const protocol = window.location.protocol;
3024
+ return {
3025
+ baseUrl: `${protocol}//${hostname}:8082`,
3026
+ linkApiUrl: `${protocol}//${hostname}:8080`
3027
+ };
2402
3028
  }
2403
- return 0;
3029
+ return {
3030
+ baseUrl: "https://storefronts.cimplify.io",
3031
+ linkApiUrl: "https://api.cimplify.io"
3032
+ };
2404
3033
  }
2405
- function getMarkupPercentage(product) {
2406
- const basePrice = getBasePrice(product);
2407
- const finalPrice = getDisplayPrice(product);
2408
- if (finalPrice > basePrice && basePrice > 0) {
2409
- return Math.round((finalPrice - basePrice) / basePrice * 100);
3034
+ function getEnvPublicKey() {
3035
+ try {
3036
+ const env = globalThis.process;
3037
+ return env?.env.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || void 0;
3038
+ } catch {
3039
+ return void 0;
2410
3040
  }
2411
- return 0;
2412
3041
  }
2413
- function getProductCurrency(product) {
2414
- if (product.price_info?.currency) {
2415
- return product.price_info.currency;
2416
- }
2417
- if (product.currency) {
2418
- return product.currency;
3042
+ var CimplifyClient = class {
3043
+ constructor(config = {}) {
3044
+ this.accessToken = null;
3045
+ this.context = {};
3046
+ this.inflightRequests = /* @__PURE__ */ new Map();
3047
+ this.publicKey = config.publicKey || getEnvPublicKey() || "";
3048
+ const urls = deriveUrls();
3049
+ this.baseUrl = urls.baseUrl;
3050
+ this.linkApiUrl = urls.linkApiUrl;
3051
+ this.credentials = config.credentials || "include";
3052
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
3053
+ this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
3054
+ this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
3055
+ this.hooks = config.hooks ?? {};
3056
+ this.accessToken = this.loadAccessToken();
3057
+ if (!this.publicKey) {
3058
+ console.warn(
3059
+ '[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "pk_..." } to createCimplifyClient().'
3060
+ );
3061
+ }
2419
3062
  }
2420
- return "GHS";
2421
- }
2422
- function formatProductPrice(product, locale = "en-US") {
2423
- const price = getDisplayPrice(product);
2424
- const currency = getProductCurrency(product);
2425
- return formatPrice(price, currency, locale);
2426
- }
2427
-
2428
- // src/utils/payment.ts
2429
- function categorizePaymentError(error, errorCode) {
2430
- let message = "An unexpected error occurred during payment processing. Please try again or contact support.";
2431
- let recoverable = true;
2432
- let code = errorCode || "PAYMENT_ERROR";
2433
- const technical = error.stack;
2434
- const errorMessage = error.message?.toLowerCase() || "";
2435
- if (errorCode === "INSUFFICIENT_FUNDS" || errorMessage.includes("insufficient") || errorMessage.includes("funds")) {
2436
- code = "INSUFFICIENT_FUNDS";
2437
- message = "Your payment method has insufficient funds. Please try another payment method.";
2438
- } else if (errorCode === "CARD_DECLINED" || errorMessage.includes("declined")) {
2439
- code = "CARD_DECLINED";
2440
- message = "Your card was declined. Please try another card or payment method.";
2441
- } else if (errorMessage.includes("cancelled") || errorMessage.includes("canceled") || errorCode === "PAYMENT_CANCELLED") {
2442
- code = "PAYMENT_CANCELLED";
2443
- message = "Payment was cancelled. You can try again when ready.";
2444
- } else if (errorMessage.includes("network") || errorMessage.includes("connection") || errorCode === "NETWORK_ERROR") {
2445
- code = "NETWORK_ERROR";
2446
- message = "Network connection issue. Please check your internet connection and try again.";
2447
- } else if (errorMessage.includes("timeout") || errorCode === "TIMEOUT") {
2448
- code = "TIMEOUT";
2449
- message = "Payment processing timed out. Please try again.";
2450
- } else if (errorCode === "PAYMENT_ACTION_NOT_COMPLETED") {
2451
- code = "PAYMENT_ACTION_NOT_COMPLETED";
2452
- message = "Payment action was not completed. Please try again.";
2453
- } else if (errorCode === "AUTHORIZATION_FAILED") {
2454
- code = "AUTHORIZATION_FAILED";
2455
- message = "Authorization failed. Please check your code and try again.";
2456
- } else if (errorCode === "INVALID_OTP") {
2457
- code = "INVALID_OTP";
2458
- message = "Invalid verification code. Please check and try again.";
3063
+ /** @deprecated Use getAccessToken() instead */
3064
+ getSessionToken() {
3065
+ return this.accessToken;
2459
3066
  }
2460
- return { code, message, recoverable, technical };
2461
- }
2462
- function isQuickPaymentResponse(response) {
2463
- return response !== null && typeof response === "object" && "payment" in response && typeof response.payment === "object";
2464
- }
2465
- function isWebPaymentResponse(response) {
2466
- return response !== null && typeof response === "object" && "transaction" in response && typeof response.transaction === "object";
2467
- }
2468
- function normalizePaymentResponse(response) {
2469
- if (!response) {
2470
- return {
2471
- method: "unknown",
2472
- provider: "unknown",
2473
- requires_action: false,
2474
- metadata: {}
3067
+ /** @deprecated Use setAccessToken() instead */
3068
+ setSessionToken(token) {
3069
+ this.setAccessToken(token);
3070
+ }
3071
+ getAccessToken() {
3072
+ return this.accessToken;
3073
+ }
3074
+ getPublicKey() {
3075
+ return this.publicKey;
3076
+ }
3077
+ setAccessToken(token) {
3078
+ const previous = this.accessToken;
3079
+ this.accessToken = token;
3080
+ this.saveAccessToken(token);
3081
+ this.hooks.onSessionChange?.({
3082
+ previousToken: previous,
3083
+ newToken: token,
3084
+ source: "manual"
3085
+ });
3086
+ }
3087
+ clearSession() {
3088
+ const previous = this.accessToken;
3089
+ this.accessToken = null;
3090
+ this.saveAccessToken(null);
3091
+ this.hooks.onSessionChange?.({
3092
+ previousToken: previous,
3093
+ newToken: null,
3094
+ source: "clear"
3095
+ });
3096
+ }
3097
+ /** Set the active location/branch for all subsequent requests */
3098
+ setLocationId(locationId) {
3099
+ if (locationId) {
3100
+ this.context.location_id = locationId;
3101
+ } else {
3102
+ delete this.context.location_id;
3103
+ }
3104
+ }
3105
+ /** Get the currently active location ID */
3106
+ getLocationId() {
3107
+ return this.context.location_id ?? null;
3108
+ }
3109
+ loadAccessToken() {
3110
+ if (typeof window !== "undefined" && window.localStorage) {
3111
+ return localStorage.getItem(ACCESS_TOKEN_STORAGE_KEY);
3112
+ }
3113
+ return null;
3114
+ }
3115
+ saveAccessToken(token) {
3116
+ if (typeof window !== "undefined" && window.localStorage) {
3117
+ if (token) {
3118
+ localStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, token);
3119
+ } else {
3120
+ localStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY);
3121
+ }
3122
+ }
3123
+ }
3124
+ getHeaders() {
3125
+ const headers = {
3126
+ "Content-Type": "application/json",
3127
+ "X-API-Key": this.publicKey
3128
+ };
3129
+ if (this.accessToken) {
3130
+ headers["Authorization"] = `Bearer ${this.accessToken}`;
3131
+ }
3132
+ return headers;
3133
+ }
3134
+ async resilientFetch(url, options) {
3135
+ const method = options.method || "GET";
3136
+ const path = url.replace(this.baseUrl, "").replace(this.linkApiUrl, "");
3137
+ const startTime = Date.now();
3138
+ let retryCount = 0;
3139
+ const context = {
3140
+ method,
3141
+ path,
3142
+ url,
3143
+ body: options.body ? JSON.parse(options.body) : void 0,
3144
+ startTime
2475
3145
  };
3146
+ this.hooks.onRequestStart?.(context);
3147
+ let lastError;
3148
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
3149
+ try {
3150
+ const controller = new AbortController();
3151
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
3152
+ const response = await fetch(url, {
3153
+ ...options,
3154
+ signal: controller.signal
3155
+ });
3156
+ clearTimeout(timeoutId);
3157
+ if (response.ok || response.status >= 400 && response.status < 500) {
3158
+ this.hooks.onRequestSuccess?.({
3159
+ ...context,
3160
+ status: response.status,
3161
+ durationMs: Date.now() - startTime
3162
+ });
3163
+ return response;
3164
+ }
3165
+ if (response.status >= 500 && attempt < this.maxRetries) {
3166
+ retryCount++;
3167
+ const delay = this.retryDelay * Math.pow(2, attempt);
3168
+ this.hooks.onRetry?.({
3169
+ ...context,
3170
+ attempt: retryCount,
3171
+ delayMs: delay,
3172
+ error: new Error(`Server error: ${response.status}`)
3173
+ });
3174
+ await sleep(delay);
3175
+ continue;
3176
+ }
3177
+ this.hooks.onRequestSuccess?.({
3178
+ ...context,
3179
+ status: response.status,
3180
+ durationMs: Date.now() - startTime
3181
+ });
3182
+ return response;
3183
+ } catch (error) {
3184
+ lastError = error;
3185
+ const networkError = toNetworkError(error);
3186
+ const errorRetryable = isRetryable(error);
3187
+ if (!errorRetryable || attempt >= this.maxRetries) {
3188
+ this.hooks.onRequestError?.({
3189
+ ...context,
3190
+ error: networkError,
3191
+ durationMs: Date.now() - startTime,
3192
+ retryCount,
3193
+ retryable: errorRetryable
3194
+ });
3195
+ throw networkError;
3196
+ }
3197
+ retryCount++;
3198
+ const delay = this.retryDelay * Math.pow(2, attempt);
3199
+ this.hooks.onRetry?.({
3200
+ ...context,
3201
+ attempt: retryCount,
3202
+ delayMs: delay,
3203
+ error: networkError
3204
+ });
3205
+ await sleep(delay);
3206
+ }
3207
+ }
3208
+ const finalError = toNetworkError(lastError);
3209
+ this.hooks.onRequestError?.({
3210
+ ...context,
3211
+ error: finalError,
3212
+ durationMs: Date.now() - startTime,
3213
+ retryCount,
3214
+ retryable: false
3215
+ });
3216
+ throw finalError;
3217
+ }
3218
+ getDedupeKey(type, payload) {
3219
+ return `${type}:${JSON.stringify(payload)}`;
3220
+ }
3221
+ async deduplicatedRequest(key, requestFn) {
3222
+ const existing = this.inflightRequests.get(key);
3223
+ if (existing) {
3224
+ return existing;
3225
+ }
3226
+ const request = requestFn().finally(() => {
3227
+ this.inflightRequests.delete(key);
3228
+ });
3229
+ this.inflightRequests.set(key, request);
3230
+ return request;
3231
+ }
3232
+ async query(query2, variables) {
3233
+ const body = { query: query2 };
3234
+ if (variables) {
3235
+ body.variables = variables;
3236
+ }
3237
+ if (Object.keys(this.context).length > 0) {
3238
+ body.context = this.context;
3239
+ }
3240
+ const key = this.getDedupeKey("query", body);
3241
+ return this.deduplicatedRequest(key, async () => {
3242
+ const response = await this.resilientFetch(`${this.baseUrl}/api/q`, {
3243
+ method: "POST",
3244
+ credentials: this.credentials,
3245
+ headers: this.getHeaders(),
3246
+ body: JSON.stringify(body)
3247
+ });
3248
+ return this.handleResponse(response);
3249
+ });
3250
+ }
3251
+ async call(method, args) {
3252
+ const body = {
3253
+ method,
3254
+ args: args !== void 0 ? [args] : []
3255
+ };
3256
+ if (Object.keys(this.context).length > 0) {
3257
+ body.context = this.context;
3258
+ }
3259
+ const response = await this.resilientFetch(`${this.baseUrl}/api/m`, {
3260
+ method: "POST",
3261
+ credentials: this.credentials,
3262
+ headers: this.getHeaders(),
3263
+ body: JSON.stringify(body)
3264
+ });
3265
+ return this.handleResponse(response);
3266
+ }
3267
+ async get(path) {
3268
+ const key = this.getDedupeKey("get", path);
3269
+ return this.deduplicatedRequest(key, async () => {
3270
+ const response = await this.resilientFetch(`${this.baseUrl}${path}`, {
3271
+ method: "GET",
3272
+ credentials: this.credentials,
3273
+ headers: this.getHeaders()
3274
+ });
3275
+ return this.handleRestResponse(response);
3276
+ });
3277
+ }
3278
+ async post(path, body) {
3279
+ const response = await this.resilientFetch(`${this.baseUrl}${path}`, {
3280
+ method: "POST",
3281
+ credentials: this.credentials,
3282
+ headers: this.getHeaders(),
3283
+ body: body ? JSON.stringify(body) : void 0
3284
+ });
3285
+ return this.handleRestResponse(response);
3286
+ }
3287
+ async delete(path) {
3288
+ const response = await this.resilientFetch(`${this.baseUrl}${path}`, {
3289
+ method: "DELETE",
3290
+ credentials: this.credentials,
3291
+ headers: this.getHeaders()
3292
+ });
3293
+ return this.handleRestResponse(response);
3294
+ }
3295
+ async linkGet(path) {
3296
+ const key = this.getDedupeKey("linkGet", path);
3297
+ return this.deduplicatedRequest(key, async () => {
3298
+ const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
3299
+ method: "GET",
3300
+ credentials: this.credentials,
3301
+ headers: this.getHeaders()
3302
+ });
3303
+ return this.handleRestResponse(response);
3304
+ });
3305
+ }
3306
+ async linkPost(path, body) {
3307
+ const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
3308
+ method: "POST",
3309
+ credentials: this.credentials,
3310
+ headers: this.getHeaders(),
3311
+ body: body ? JSON.stringify(body) : void 0
3312
+ });
3313
+ return this.handleRestResponse(response);
3314
+ }
3315
+ async linkDelete(path) {
3316
+ const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
3317
+ method: "DELETE",
3318
+ credentials: this.credentials,
3319
+ headers: this.getHeaders()
3320
+ });
3321
+ return this.handleRestResponse(response);
3322
+ }
3323
+ async handleRestResponse(response) {
3324
+ const json = await response.json();
3325
+ if (!response.ok) {
3326
+ throw new CimplifyError(
3327
+ json.error?.error_code || "API_ERROR",
3328
+ json.error?.error_message || "An error occurred",
3329
+ false
3330
+ );
3331
+ }
3332
+ return json.data;
3333
+ }
3334
+ async handleResponse(response) {
3335
+ const json = await response.json();
3336
+ if (!json.success || json.error) {
3337
+ throw new CimplifyError(
3338
+ json.error?.code || "UNKNOWN_ERROR",
3339
+ json.error?.message || "An unknown error occurred",
3340
+ json.error?.retryable || false
3341
+ );
3342
+ }
3343
+ return json.data;
3344
+ }
3345
+ get catalogue() {
3346
+ if (!this._catalogue) {
3347
+ this._catalogue = new CatalogueQueries(this);
3348
+ }
3349
+ return this._catalogue;
3350
+ }
3351
+ get cart() {
3352
+ if (!this._cart) {
3353
+ this._cart = new CartOperations(this);
3354
+ }
3355
+ return this._cart;
3356
+ }
3357
+ get checkout() {
3358
+ if (!this._checkout) {
3359
+ this._checkout = new CheckoutService(this);
3360
+ }
3361
+ return this._checkout;
3362
+ }
3363
+ get orders() {
3364
+ if (!this._orders) {
3365
+ this._orders = new OrderQueries(this);
3366
+ }
3367
+ return this._orders;
3368
+ }
3369
+ get link() {
3370
+ if (!this._link) {
3371
+ this._link = new LinkService(this);
3372
+ }
3373
+ return this._link;
3374
+ }
3375
+ get auth() {
3376
+ if (!this._auth) {
3377
+ this._auth = new AuthService(this);
3378
+ }
3379
+ return this._auth;
3380
+ }
3381
+ get business() {
3382
+ if (!this._business) {
3383
+ this._business = new BusinessService(this);
3384
+ }
3385
+ return this._business;
3386
+ }
3387
+ get inventory() {
3388
+ if (!this._inventory) {
3389
+ this._inventory = new InventoryService(this);
3390
+ }
3391
+ return this._inventory;
3392
+ }
3393
+ get scheduling() {
3394
+ if (!this._scheduling) {
3395
+ this._scheduling = new SchedulingService(this);
3396
+ }
3397
+ return this._scheduling;
3398
+ }
3399
+ get lite() {
3400
+ if (!this._lite) {
3401
+ this._lite = new LiteService(this);
3402
+ }
3403
+ return this._lite;
2476
3404
  }
2477
- if (isQuickPaymentResponse(response)) {
2478
- return {
2479
- method: response.payment.type?.toLowerCase() || "unknown",
2480
- provider: response.payment.provider?.toLowerCase() || "unknown",
2481
- requires_action: !!response.payment.redirect_url || !!response.payment.access_code,
2482
- public_key: response.payment.public_key,
2483
- client_secret: response.payment.access_code,
2484
- access_code: response.payment.access_code,
2485
- redirect_url: response.payment.redirect_url,
2486
- transaction_id: response.payment.reference,
2487
- order_id: response.order_id,
2488
- reference: response.payment.reference,
2489
- instructions: response.payment.instructions,
2490
- metadata: response.payment.metadata
2491
- };
3405
+ get fx() {
3406
+ if (!this._fx) {
3407
+ this._fx = new FxService(this);
3408
+ }
3409
+ return this._fx;
2492
3410
  }
2493
- if (isWebPaymentResponse(response)) {
2494
- const authType = response.authorization_type?.toLowerCase();
2495
- const validAuthTypes = ["otp", "pin", "phone", "birthday", "address"];
2496
- const safeAuthType = authType && validAuthTypes.includes(authType) ? authType : void 0;
2497
- return {
2498
- provider: response.transaction.provider_type?.toLowerCase() || "unknown",
2499
- requires_action: response.requires_action || false,
2500
- public_key: response.public_key,
2501
- client_secret: response.client_secret,
2502
- redirect_url: response.authorization_url,
2503
- transaction_id: response.transaction.id,
2504
- order_id: response.transaction.order_id,
2505
- reference: response.transaction.provider_reference,
2506
- metadata: response.transaction.metadata,
2507
- method: response.transaction.payment_method?.toLowerCase() || "unknown",
2508
- instructions: response.display_text,
2509
- display_text: response.display_text,
2510
- requires_authorization: response.requires_authorization,
2511
- authorization_type: safeAuthType,
2512
- provider_payment_id: response.provider_payment_id || response.transaction.provider_reference
2513
- };
3411
+ /**
3412
+ * Create a CimplifyElements instance for embedding checkout components.
3413
+ * Like Stripe's stripe.elements().
3414
+ *
3415
+ * @param businessId - The business ID for checkout context
3416
+ * @param options - Optional configuration for elements
3417
+ *
3418
+ * @example
3419
+ * ```ts
3420
+ * const elements = client.elements('bus_xxx');
3421
+ * const authElement = elements.create('auth');
3422
+ * authElement.mount('#auth-container');
3423
+ * ```
3424
+ */
3425
+ elements(businessId, options) {
3426
+ return createElements(this, businessId, options);
2514
3427
  }
2515
- return {
2516
- method: "unknown",
2517
- provider: "unknown",
2518
- requires_action: false,
2519
- metadata: {}
2520
- };
2521
- }
2522
- var PAYMENT_SUCCESS_STATUSES = /* @__PURE__ */ new Set([
2523
- "success",
2524
- "succeeded",
2525
- "paid",
2526
- "captured",
2527
- "completed",
2528
- "authorized"
2529
- ]);
2530
- var PAYMENT_FAILURE_STATUSES = /* @__PURE__ */ new Set([
2531
- "failed",
2532
- "declined",
2533
- "cancelled",
2534
- "voided",
2535
- "error"
2536
- ]);
2537
- var PAYMENT_REQUIRES_ACTION_STATUSES = /* @__PURE__ */ new Set([
2538
- "requires_action",
2539
- "requires_payment_method",
2540
- "requires_capture"
2541
- ]);
2542
- var PAYMENT_STATUS_ALIAS_MAP = {
2543
- ok: "success",
2544
- done: "success",
2545
- paid: "paid",
2546
- paid_in_full: "paid",
2547
- paid_successfully: "paid",
2548
- succeeded: "success",
2549
- captured: "captured",
2550
- completed: "completed",
2551
- pending_confirmation: "pending_confirmation",
2552
- requires_authorization: "requires_action",
2553
- requires_action: "requires_action",
2554
- requires_payment_method: "requires_payment_method",
2555
- requires_capture: "requires_capture",
2556
- partially_paid: "partially_paid",
2557
- partially_refunded: "partially_refunded",
2558
- card_declined: "declined",
2559
- canceled: "cancelled",
2560
- authorized: "authorized",
2561
- cancelled: "cancelled",
2562
- unresolved: "pending"
2563
3428
  };
2564
- var KNOWN_PAYMENT_STATUSES = /* @__PURE__ */ new Set([
2565
- "pending",
2566
- "processing",
2567
- "created",
2568
- "pending_confirmation",
2569
- "success",
2570
- "succeeded",
2571
- "failed",
2572
- "declined",
2573
- "authorized",
2574
- "refunded",
2575
- "partially_refunded",
2576
- "partially_paid",
2577
- "paid",
2578
- "unpaid",
2579
- "requires_action",
2580
- "requires_payment_method",
2581
- "requires_capture",
2582
- "captured",
2583
- "cancelled",
2584
- "completed",
2585
- "voided",
2586
- "error",
2587
- "unknown"
2588
- ]);
2589
- function normalizeStatusToken(status) {
2590
- return status?.trim().toLowerCase().replace(/[\s-]+/g, "_") ?? "";
3429
+ function createCimplifyClient(config = {}) {
3430
+ return new CimplifyClient(config);
2591
3431
  }
2592
- function normalizePaymentStatusValue(status) {
2593
- const normalized = normalizeStatusToken(status);
2594
- if (Object.prototype.hasOwnProperty.call(PAYMENT_STATUS_ALIAS_MAP, normalized)) {
2595
- return PAYMENT_STATUS_ALIAS_MAP[normalized];
3432
+
3433
+ // src/query/builder.ts
3434
+ var QueryBuilder = class {
3435
+ constructor(entity) {
3436
+ this.filters = [];
3437
+ this.modifiers = [];
3438
+ this.pathSegments = [];
3439
+ this.entity = entity;
2596
3440
  }
2597
- return KNOWN_PAYMENT_STATUSES.has(normalized) ? normalized : "unknown";
2598
- }
2599
- function isPaymentStatusSuccess(status) {
2600
- const normalizedStatus = normalizePaymentStatusValue(status);
2601
- return PAYMENT_SUCCESS_STATUSES.has(normalizedStatus);
2602
- }
2603
- function isPaymentStatusFailure(status) {
2604
- const normalizedStatus = normalizePaymentStatusValue(status);
2605
- return PAYMENT_FAILURE_STATUSES.has(normalizedStatus);
2606
- }
2607
- function isPaymentStatusRequiresAction(status) {
2608
- const normalizedStatus = normalizePaymentStatusValue(status);
2609
- return PAYMENT_REQUIRES_ACTION_STATUSES.has(normalizedStatus);
2610
- }
2611
- function normalizeStatusResponse(response) {
2612
- if (!response || typeof response !== "object") {
2613
- return {
2614
- status: "pending",
2615
- paid: false,
2616
- message: "No status available"
2617
- };
3441
+ path(segment) {
3442
+ this.pathSegments.push(segment);
3443
+ return this;
2618
3444
  }
2619
- const res = response;
2620
- const normalizedStatus = normalizePaymentStatusValue(res.status ?? void 0);
2621
- const paidValue = res.paid === true;
2622
- const derivedPaid = paidValue || [
2623
- "success",
2624
- "succeeded",
2625
- "paid",
2626
- "captured",
2627
- "authorized",
2628
- "completed"
2629
- ].includes(normalizedStatus);
2630
- return {
2631
- status: normalizedStatus,
2632
- paid: derivedPaid,
2633
- amount: res.amount,
2634
- currency: res.currency,
2635
- reference: res.reference,
2636
- message: res.message || ""
2637
- };
2638
- }
2639
- var MOBILE_MONEY_PROVIDERS = {
2640
- mtn: { name: "MTN Mobile Money", prefix: ["024", "054", "055", "059"] },
2641
- vodafone: { name: "Vodafone Cash", prefix: ["020", "050"] },
2642
- airtel: { name: "AirtelTigo Money", prefix: ["027", "057", "026", "056"] }
2643
- };
2644
- function detectMobileMoneyProvider(phoneNumber) {
2645
- const cleaned = phoneNumber.replace(/\D/g, "");
2646
- const prefix = cleaned.slice(-9, -6);
2647
- for (const [provider, info] of Object.entries(MOBILE_MONEY_PROVIDERS)) {
2648
- if (info.prefix.some((p) => prefix.startsWith(p.slice(1)))) {
2649
- return provider;
3445
+ where(field, op, value) {
3446
+ const v = typeof value === "string" ? `'${value}'` : value;
3447
+ if (op === "contains" || op === "startsWith") {
3448
+ this.filters.push(`@.${field} ${op} ${v}`);
3449
+ } else {
3450
+ this.filters.push(`@.${field}${op}${v}`);
2650
3451
  }
3452
+ return this;
2651
3453
  }
2652
- return null;
3454
+ and(field, op, value) {
3455
+ return this.where(field, op, value);
3456
+ }
3457
+ sort(field, order = "asc") {
3458
+ this.modifiers.push(`sort(${field},${order})`);
3459
+ return this;
3460
+ }
3461
+ limit(n) {
3462
+ this.modifiers.push(`limit(${n})`);
3463
+ return this;
3464
+ }
3465
+ offset(n) {
3466
+ this.modifiers.push(`offset(${n})`);
3467
+ return this;
3468
+ }
3469
+ count() {
3470
+ this.modifiers.push("count");
3471
+ return this;
3472
+ }
3473
+ enriched() {
3474
+ this.modifiers.push("enriched");
3475
+ return this;
3476
+ }
3477
+ build() {
3478
+ let query2 = this.entity;
3479
+ if (this.pathSegments.length > 0) {
3480
+ query2 += "." + this.pathSegments.join(".");
3481
+ }
3482
+ if (this.filters.length > 0) {
3483
+ query2 += `[?(${this.filters.join(" && ")})]`;
3484
+ }
3485
+ for (const mod of this.modifiers) {
3486
+ query2 += `#${mod}`;
3487
+ }
3488
+ return query2;
3489
+ }
3490
+ toString() {
3491
+ return this.build();
3492
+ }
3493
+ };
3494
+ function query(entity) {
3495
+ return new QueryBuilder(entity);
2653
3496
  }
2654
3497
 
2655
3498
  export { AUTHORIZATION_TYPE, AUTH_MUTATION, AuthService, BusinessService, CHECKOUT_MODE, CHECKOUT_MUTATION, CHECKOUT_STEP, CONTACT_TYPE, CURRENCY_SYMBOLS, CartOperations, CatalogueQueries, CheckoutService as CheckoutOperations, CheckoutService, CimplifyClient, CimplifyElement, CimplifyElements, CimplifyError, DEFAULT_COUNTRY, DEFAULT_CURRENCY, DEVICE_TYPE, ELEMENT_TYPES, EVENT_TYPES, ErrorCode, FxService, InventoryService, LINK_MUTATION, LINK_QUERY, LinkService, LiteService, MESSAGE_TYPES, MOBILE_MONEY_PROVIDER, MOBILE_MONEY_PROVIDERS, ORDER_MUTATION, ORDER_TYPE, OrderQueries, PAYMENT_METHOD, PAYMENT_MUTATION, PAYMENT_STATE, PICKUP_TIME_TYPE, QueryBuilder, SchedulingService, categorizePaymentError, combine, combineObject, createCimplifyClient, createElements, detectMobileMoneyProvider, err, flatMap, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatProductPrice, fromPromise, generateIdempotencyKey, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getOrElse, getProductCurrency, isCimplifyError, isErr, isOk, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isRetryableError, mapError, mapResult, normalizePaymentResponse, normalizeStatusResponse, ok, parsePrice, query, toNullable, tryCatch, unwrap };