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