@cimplify/sdk 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -149,6 +149,63 @@ async function safe(promise) {
149
149
  return err(toCimplifyError(error));
150
150
  }
151
151
  }
152
+ function isRecord(value) {
153
+ return typeof value === "object" && value !== null;
154
+ }
155
+ function readFinalPrice(value) {
156
+ if (!isRecord(value)) return void 0;
157
+ const finalPrice = value.final_price;
158
+ if (typeof finalPrice === "string" || typeof finalPrice === "number") {
159
+ return finalPrice;
160
+ }
161
+ return void 0;
162
+ }
163
+ function normalizeCatalogueProductPayload(product) {
164
+ const normalized = { ...product };
165
+ const defaultPrice = normalized["default_price"];
166
+ if (defaultPrice === void 0 || defaultPrice === null || defaultPrice === "") {
167
+ const derivedDefaultPrice = readFinalPrice(normalized["default_price_info"]) || readFinalPrice(normalized["price_info"]) || (typeof normalized["final_price"] === "string" || typeof normalized["final_price"] === "number" ? normalized["final_price"] : void 0);
168
+ normalized["default_price"] = derivedDefaultPrice ?? "0";
169
+ }
170
+ const variants = normalized["variants"];
171
+ if (Array.isArray(variants)) {
172
+ normalized["variants"] = variants.map((variant) => {
173
+ if (!isRecord(variant)) return variant;
174
+ const normalizedVariant = { ...variant };
175
+ const variantAdjustment = normalizedVariant["price_adjustment"];
176
+ if (variantAdjustment === void 0 || variantAdjustment === null || variantAdjustment === "") {
177
+ normalizedVariant["price_adjustment"] = readFinalPrice(normalizedVariant["price_info"]) ?? "0";
178
+ }
179
+ return normalizedVariant;
180
+ });
181
+ }
182
+ const addOns = normalized["add_ons"];
183
+ if (Array.isArray(addOns)) {
184
+ normalized["add_ons"] = addOns.map((addOn) => {
185
+ if (!isRecord(addOn)) return addOn;
186
+ const normalizedAddOn = { ...addOn };
187
+ const options = normalizedAddOn["options"];
188
+ if (!Array.isArray(options)) return normalizedAddOn;
189
+ normalizedAddOn["options"] = options.map((option) => {
190
+ if (!isRecord(option)) return option;
191
+ const normalizedOption = { ...option };
192
+ const optionPrice = normalizedOption["default_price"];
193
+ if (optionPrice === void 0 || optionPrice === null || optionPrice === "") {
194
+ normalizedOption["default_price"] = readFinalPrice(normalizedOption["default_price_info"]) || readFinalPrice(normalizedOption["price_info"]) || "0";
195
+ }
196
+ return normalizedOption;
197
+ });
198
+ return normalizedAddOn;
199
+ });
200
+ }
201
+ return normalized;
202
+ }
203
+ function findProductBySlug(products, slug) {
204
+ return products.find((product) => {
205
+ const value = product["slug"];
206
+ return typeof value === "string" && value === slug;
207
+ });
208
+ }
152
209
  var CatalogueQueries = class {
153
210
  constructor(client) {
154
211
  this.client = client;
@@ -186,20 +243,34 @@ var CatalogueQueries = class {
186
243
  if (options?.offset) {
187
244
  query2 += `#offset(${options.offset})`;
188
245
  }
189
- return safe(this.client.query(query2));
246
+ const result = await safe(this.client.query(query2));
247
+ if (!result.ok) return result;
248
+ return ok(result.value.map((product) => normalizeCatalogueProductPayload(product)));
190
249
  }
191
250
  async getProduct(id) {
192
- return safe(this.client.query(`products.${id}`));
251
+ const result = await safe(this.client.query(`products.${id}`));
252
+ if (!result.ok) return result;
253
+ return ok(normalizeCatalogueProductPayload(result.value));
193
254
  }
194
255
  async getProductBySlug(slug) {
195
- const result = await safe(
256
+ const filteredResult = await safe(
196
257
  this.client.query(`products[?(@.slug=='${slug}')]`)
197
258
  );
198
- if (!result.ok) return result;
199
- if (!result.value.length) {
259
+ if (!filteredResult.ok) return filteredResult;
260
+ const exactMatch = findProductBySlug(filteredResult.value, slug);
261
+ if (exactMatch) {
262
+ return ok(normalizeCatalogueProductPayload(exactMatch));
263
+ }
264
+ if (filteredResult.value.length === 1) {
265
+ return ok(normalizeCatalogueProductPayload(filteredResult.value[0]));
266
+ }
267
+ const unfilteredResult = await safe(this.client.query("products"));
268
+ if (!unfilteredResult.ok) return unfilteredResult;
269
+ const fallbackMatch = findProductBySlug(unfilteredResult.value, slug);
270
+ if (!fallbackMatch) {
200
271
  return err(new CimplifyError("NOT_FOUND", `Product not found: ${slug}`, false));
201
272
  }
202
- return ok(result.value[0]);
273
+ return ok(normalizeCatalogueProductPayload(fallbackMatch));
203
274
  }
204
275
  async getVariants(productId) {
205
276
  return safe(this.client.query(`products.${productId}.variants`));
@@ -591,535 +662,1592 @@ var ORDER_MUTATION = {
591
662
  var DEFAULT_CURRENCY = "GHS";
592
663
  var DEFAULT_COUNTRY = "GHA";
593
664
 
594
- // src/checkout.ts
595
- function toCimplifyError3(error) {
596
- if (error instanceof CimplifyError) return error;
597
- if (error instanceof Error) {
598
- return new CimplifyError("UNKNOWN_ERROR", error.message, false);
665
+ // src/utils/price.ts
666
+ var CURRENCY_SYMBOLS = {
667
+ // Major world currencies
668
+ USD: "$",
669
+ EUR: "\u20AC",
670
+ GBP: "\xA3",
671
+ JPY: "\xA5",
672
+ CNY: "\xA5",
673
+ CHF: "CHF",
674
+ CAD: "C$",
675
+ AUD: "A$",
676
+ NZD: "NZ$",
677
+ HKD: "HK$",
678
+ SGD: "S$",
679
+ INR: "\u20B9",
680
+ BRL: "R$",
681
+ MXN: "MX$",
682
+ KRW: "\u20A9",
683
+ RUB: "\u20BD",
684
+ TRY: "\u20BA",
685
+ THB: "\u0E3F",
686
+ PLN: "z\u0142",
687
+ SEK: "kr",
688
+ NOK: "kr",
689
+ DKK: "kr",
690
+ CZK: "K\u010D",
691
+ HUF: "Ft",
692
+ ILS: "\u20AA",
693
+ AED: "\u062F.\u0625",
694
+ SAR: "\uFDFC",
695
+ MYR: "RM",
696
+ PHP: "\u20B1",
697
+ IDR: "Rp",
698
+ VND: "\u20AB",
699
+ TWD: "NT$",
700
+ // African currencies
701
+ GHS: "GH\u20B5",
702
+ NGN: "\u20A6",
703
+ KES: "KSh",
704
+ ZAR: "R",
705
+ XOF: "CFA",
706
+ XAF: "FCFA",
707
+ EGP: "E\xA3",
708
+ MAD: "MAD",
709
+ TZS: "TSh",
710
+ UGX: "USh",
711
+ RWF: "FRw",
712
+ ETB: "Br",
713
+ ZMW: "ZK",
714
+ BWP: "P",
715
+ MUR: "\u20A8",
716
+ SCR: "\u20A8",
717
+ NAD: "N$",
718
+ SZL: "E",
719
+ LSL: "L",
720
+ MWK: "MK",
721
+ AOA: "Kz",
722
+ CDF: "FC",
723
+ GMD: "D",
724
+ GNF: "FG",
725
+ LRD: "L$",
726
+ SLL: "Le",
727
+ MZN: "MT",
728
+ SDG: "SDG",
729
+ SSP: "SSP",
730
+ SOS: "Sh.So.",
731
+ DJF: "Fdj",
732
+ ERN: "Nfk",
733
+ CVE: "$",
734
+ STN: "Db",
735
+ KMF: "CF",
736
+ BIF: "FBu"
737
+ };
738
+ function getCurrencySymbol(currencyCode) {
739
+ return CURRENCY_SYMBOLS[currencyCode.toUpperCase()] || currencyCode;
740
+ }
741
+ function formatNumberCompact(value, decimals = 1) {
742
+ const absValue = Math.abs(value);
743
+ const sign = value < 0 ? "-" : "";
744
+ if (absValue >= 1e9) {
745
+ return `${sign}${(absValue / 1e9).toFixed(decimals)}B`;
599
746
  }
600
- return new CimplifyError("UNKNOWN_ERROR", String(error), false);
747
+ if (absValue >= 1e6) {
748
+ return `${sign}${(absValue / 1e6).toFixed(decimals)}M`;
749
+ }
750
+ if (absValue >= 1e3) {
751
+ return `${sign}${(absValue / 1e3).toFixed(decimals)}K`;
752
+ }
753
+ return `${sign}${absValue.toFixed(decimals)}`;
601
754
  }
602
- async function safe3(promise) {
755
+ function formatPrice(amount, currency = "GHS", locale = "en-US") {
756
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
757
+ if (isNaN(numAmount)) {
758
+ return `${getCurrencySymbol(currency)}0.00`;
759
+ }
603
760
  try {
604
- return ok(await promise);
605
- } catch (error) {
606
- return err(toCimplifyError3(error));
761
+ return new Intl.NumberFormat(locale, {
762
+ style: "currency",
763
+ currency: currency.toUpperCase(),
764
+ minimumFractionDigits: 2,
765
+ maximumFractionDigits: 2
766
+ }).format(numAmount);
767
+ } catch {
768
+ return `${getCurrencySymbol(currency)}${numAmount.toFixed(2)}`;
607
769
  }
608
770
  }
609
- function generateIdempotencyKey() {
610
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
611
- return `idem_${crypto.randomUUID()}`;
771
+ function formatPriceAdjustment(amount, currency = "GHS", locale = "en-US") {
772
+ const formatted = formatPrice(Math.abs(amount), currency, locale);
773
+ if (amount > 0) {
774
+ return `+${formatted}`;
775
+ } else if (amount < 0) {
776
+ return `-${formatted}`;
612
777
  }
613
- const timestamp = Date.now().toString(36);
614
- const random = Math.random().toString(36).substring(2, 15);
615
- return `idem_${timestamp}_${random}`;
778
+ return formatted;
616
779
  }
617
- var CheckoutService = class {
618
- constructor(client) {
619
- this.client = client;
620
- }
621
- async process(data) {
622
- const checkoutData = {
623
- ...data,
624
- idempotency_key: data.idempotency_key || generateIdempotencyKey()
625
- };
626
- return safe3(
627
- this.client.call(CHECKOUT_MUTATION.PROCESS, {
628
- checkout_data: checkoutData
629
- })
630
- );
780
+ function formatPriceCompact(amount, currency = "GHS", decimals = 1) {
781
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
782
+ if (isNaN(numAmount)) {
783
+ return `${getCurrencySymbol(currency)}0`;
631
784
  }
632
- async initializePayment(orderId, method) {
633
- return safe3(
634
- this.client.call("order.initializePayment", {
635
- order_id: orderId,
636
- payment_method: method
637
- })
638
- );
785
+ const symbol = getCurrencySymbol(currency);
786
+ if (Math.abs(numAmount) < 1e3) {
787
+ return `${symbol}${numAmount.toFixed(2)}`;
639
788
  }
640
- async submitAuthorization(input) {
641
- return safe3(
642
- this.client.call(PAYMENT_MUTATION.SUBMIT_AUTHORIZATION, input)
643
- );
789
+ return `${symbol}${formatNumberCompact(numAmount, decimals)}`;
790
+ }
791
+ function formatMoney(amount, currency = "GHS") {
792
+ const symbol = getCurrencySymbol(currency);
793
+ const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
794
+ if (isNaN(numAmount)) {
795
+ return `${symbol}0.00`;
644
796
  }
645
- async pollPaymentStatus(orderId) {
646
- return safe3(
647
- this.client.call(PAYMENT_MUTATION.CHECK_STATUS, orderId)
648
- );
797
+ return `${symbol}${numAmount.toFixed(2)}`;
798
+ }
799
+ function parsePrice(value) {
800
+ if (value === void 0 || value === null) {
801
+ return 0;
649
802
  }
650
- async updateOrderCustomer(orderId, customer) {
651
- return safe3(
652
- this.client.call(ORDER_MUTATION.UPDATE_CUSTOMER, {
653
- order_id: orderId,
654
- ...customer
655
- })
656
- );
803
+ if (typeof value === "number") {
804
+ return isNaN(value) ? 0 : value;
657
805
  }
658
- async verifyPayment(orderId) {
659
- return safe3(
660
- this.client.call("order.verifyPayment", {
661
- order_id: orderId
662
- })
663
- );
806
+ const cleaned = value.replace(/[^\d.-]/g, "");
807
+ const parsed = parseFloat(cleaned);
808
+ return isNaN(parsed) ? 0 : parsed;
809
+ }
810
+ function getDisplayPrice(product) {
811
+ if (product.price_info) {
812
+ return parsePrice(product.price_info.final_price);
664
813
  }
665
- };
666
-
667
- // src/orders.ts
668
- function toCimplifyError4(error) {
669
- if (error instanceof CimplifyError) return error;
670
- if (error instanceof Error) {
671
- return new CimplifyError("UNKNOWN_ERROR", error.message, false);
814
+ if (product.final_price !== void 0 && product.final_price !== null) {
815
+ return parsePrice(product.final_price);
672
816
  }
673
- return new CimplifyError("UNKNOWN_ERROR", String(error), false);
674
- }
675
- async function safe4(promise) {
676
- try {
677
- return ok(await promise);
678
- } catch (error) {
679
- return err(toCimplifyError4(error));
817
+ if (product.default_price !== void 0 && product.default_price !== null) {
818
+ return parsePrice(product.default_price);
680
819
  }
820
+ return 0;
681
821
  }
682
- var OrderQueries = class {
683
- constructor(client) {
684
- this.client = client;
822
+ function getBasePrice(product) {
823
+ if (product.price_info) {
824
+ return parsePrice(product.price_info.base_price);
685
825
  }
686
- async list(options) {
687
- let query2 = "orders";
688
- if (options?.status) {
689
- query2 += `[?(@.status=='${options.status}')]`;
690
- }
691
- query2 += "#sort(created_at,desc)";
692
- if (options?.limit) {
693
- query2 += `#limit(${options.limit})`;
694
- }
695
- if (options?.offset) {
696
- query2 += `#offset(${options.offset})`;
697
- }
698
- return safe4(this.client.query(query2));
826
+ if (product.base_price !== void 0 && product.base_price !== null) {
827
+ return parsePrice(product.base_price);
699
828
  }
700
- async get(orderId) {
701
- return safe4(this.client.query(`orders.${orderId}`));
829
+ if (product.default_price !== void 0 && product.default_price !== null) {
830
+ return parsePrice(product.default_price);
702
831
  }
703
- async getRecent(limit = 5) {
704
- return safe4(this.client.query(`orders#sort(created_at,desc)#limit(${limit})`));
705
- }
706
- async getByStatus(status) {
707
- return safe4(this.client.query(`orders[?(@.status=='${status}')]`));
708
- }
709
- async cancel(orderId, reason) {
710
- return safe4(
711
- this.client.call("order.cancelOrder", {
712
- order_id: orderId,
713
- reason
714
- })
715
- );
716
- }
717
- };
718
-
719
- // src/link.ts
720
- function toCimplifyError5(error) {
721
- if (error instanceof CimplifyError) return error;
722
- if (error instanceof Error) {
723
- return new CimplifyError("UNKNOWN_ERROR", error.message, false);
724
- }
725
- return new CimplifyError("UNKNOWN_ERROR", String(error), false);
832
+ return 0;
726
833
  }
727
- async function safe5(promise) {
728
- try {
729
- return ok(await promise);
730
- } catch (error) {
731
- return err(toCimplifyError5(error));
732
- }
834
+ function isOnSale(product) {
835
+ const basePrice = getBasePrice(product);
836
+ const finalPrice = getDisplayPrice(product);
837
+ return basePrice > finalPrice && basePrice > 0;
733
838
  }
734
- var LinkService = class {
735
- constructor(client) {
736
- this.client = client;
737
- }
738
- async requestOtp(input) {
739
- return safe5(this.client.linkPost("/v1/link/auth/request-otp", input));
839
+ function getDiscountPercentage(product) {
840
+ const basePrice = getBasePrice(product);
841
+ const finalPrice = getDisplayPrice(product);
842
+ if (basePrice > finalPrice && basePrice > 0) {
843
+ return Math.round((basePrice - finalPrice) / basePrice * 100);
740
844
  }
741
- async verifyOtp(input) {
742
- const result = await safe5(
743
- this.client.linkPost("/v1/link/auth/verify-otp", input)
744
- );
745
- if (result.ok && result.value.session_token) {
746
- this.client.setSessionToken(result.value.session_token);
747
- }
748
- return result;
845
+ return 0;
846
+ }
847
+ function getMarkupPercentage(product) {
848
+ const basePrice = getBasePrice(product);
849
+ const finalPrice = getDisplayPrice(product);
850
+ if (finalPrice > basePrice && basePrice > 0) {
851
+ return Math.round((finalPrice - basePrice) / basePrice * 100);
749
852
  }
750
- async logout() {
751
- const result = await safe5(this.client.linkPost("/v1/link/auth/logout"));
752
- if (result.ok) {
753
- this.client.clearSession();
754
- }
755
- return result;
853
+ return 0;
854
+ }
855
+ function getProductCurrency(product) {
856
+ if (product.price_info?.currency) {
857
+ return product.price_info.currency;
756
858
  }
757
- async checkStatus(contact) {
758
- return safe5(
759
- this.client.call(LINK_MUTATION.CHECK_STATUS, {
760
- contact
761
- })
762
- );
859
+ if (product.currency) {
860
+ return product.currency;
763
861
  }
764
- async getLinkData() {
765
- return safe5(this.client.query(LINK_QUERY.DATA));
862
+ return "GHS";
863
+ }
864
+ function formatProductPrice(product, locale = "en-US") {
865
+ const price = getDisplayPrice(product);
866
+ const currency = getProductCurrency(product);
867
+ return formatPrice(price, currency, locale);
868
+ }
869
+
870
+ // src/utils/payment.ts
871
+ function categorizePaymentError(error, errorCode) {
872
+ let message = "An unexpected error occurred during payment processing. Please try again or contact support.";
873
+ let recoverable = true;
874
+ let code = errorCode || "PAYMENT_ERROR";
875
+ const technical = error.stack;
876
+ const errorMessage = error.message?.toLowerCase() || "";
877
+ if (errorCode === "INSUFFICIENT_FUNDS" || errorMessage.includes("insufficient") || errorMessage.includes("funds")) {
878
+ code = "INSUFFICIENT_FUNDS";
879
+ message = "Your payment method has insufficient funds. Please try another payment method.";
880
+ } else if (errorCode === "CARD_DECLINED" || errorMessage.includes("declined")) {
881
+ code = "CARD_DECLINED";
882
+ message = "Your card was declined. Please try another card or payment method.";
883
+ } else if (errorMessage.includes("cancelled") || errorMessage.includes("canceled") || errorCode === "PAYMENT_CANCELLED") {
884
+ code = "PAYMENT_CANCELLED";
885
+ message = "Payment was cancelled. You can try again when ready.";
886
+ } else if (errorMessage.includes("network") || errorMessage.includes("connection") || errorCode === "NETWORK_ERROR") {
887
+ code = "NETWORK_ERROR";
888
+ message = "Network connection issue. Please check your internet connection and try again.";
889
+ } else if (errorMessage.includes("timeout") || errorCode === "TIMEOUT") {
890
+ code = "TIMEOUT";
891
+ message = "Payment processing timed out. Please try again.";
892
+ } else if (errorCode === "PAYMENT_ACTION_NOT_COMPLETED") {
893
+ code = "PAYMENT_ACTION_NOT_COMPLETED";
894
+ message = "Payment action was not completed. Please try again.";
895
+ } else if (errorCode === "AUTHORIZATION_FAILED") {
896
+ code = "AUTHORIZATION_FAILED";
897
+ message = "Authorization failed. Please check your code and try again.";
898
+ } else if (errorCode === "INVALID_OTP") {
899
+ code = "INVALID_OTP";
900
+ message = "Invalid verification code. Please check and try again.";
766
901
  }
767
- async getAddresses() {
768
- return safe5(this.client.query(LINK_QUERY.ADDRESSES));
902
+ return { code, message, recoverable, technical };
903
+ }
904
+ function isQuickPaymentResponse(response) {
905
+ return response !== null && typeof response === "object" && "payment" in response && typeof response.payment === "object";
906
+ }
907
+ function isWebPaymentResponse(response) {
908
+ return response !== null && typeof response === "object" && "transaction" in response && typeof response.transaction === "object";
909
+ }
910
+ function normalizePaymentResponse(response) {
911
+ if (!response) {
912
+ return {
913
+ method: "unknown",
914
+ provider: "unknown",
915
+ requires_action: false,
916
+ metadata: {}
917
+ };
769
918
  }
770
- async getMobileMoney() {
771
- return safe5(this.client.query(LINK_QUERY.MOBILE_MONEY));
919
+ if (isQuickPaymentResponse(response)) {
920
+ return {
921
+ method: response.payment.type?.toLowerCase() || "unknown",
922
+ provider: response.payment.provider?.toLowerCase() || "unknown",
923
+ requires_action: !!response.payment.redirect_url || !!response.payment.access_code,
924
+ public_key: response.payment.public_key,
925
+ client_secret: response.payment.access_code,
926
+ access_code: response.payment.access_code,
927
+ redirect_url: response.payment.redirect_url,
928
+ transaction_id: response.payment.reference,
929
+ order_id: response.order_id,
930
+ reference: response.payment.reference,
931
+ instructions: response.payment.instructions,
932
+ metadata: response.payment.metadata
933
+ };
772
934
  }
773
- async getPreferences() {
774
- return safe5(this.client.query(LINK_QUERY.PREFERENCES));
935
+ if (isWebPaymentResponse(response)) {
936
+ const authType = response.authorization_type?.toLowerCase();
937
+ const validAuthTypes = ["otp", "pin", "phone", "birthday", "address"];
938
+ const safeAuthType = authType && validAuthTypes.includes(authType) ? authType : void 0;
939
+ return {
940
+ provider: response.transaction.provider_type?.toLowerCase() || "unknown",
941
+ requires_action: response.requires_action || false,
942
+ public_key: response.public_key,
943
+ client_secret: response.client_secret,
944
+ redirect_url: response.authorization_url,
945
+ transaction_id: response.transaction.id,
946
+ order_id: response.transaction.order_id,
947
+ reference: response.transaction.provider_reference,
948
+ metadata: response.transaction.metadata,
949
+ method: response.transaction.payment_method?.toLowerCase() || "unknown",
950
+ instructions: response.display_text,
951
+ display_text: response.display_text,
952
+ requires_authorization: response.requires_authorization,
953
+ authorization_type: safeAuthType,
954
+ provider_payment_id: response.provider_payment_id || response.transaction.provider_reference
955
+ };
775
956
  }
776
- async enroll(data) {
777
- return safe5(this.client.call(LINK_MUTATION.ENROLL, data));
957
+ return {
958
+ method: "unknown",
959
+ provider: "unknown",
960
+ requires_action: false,
961
+ metadata: {}
962
+ };
963
+ }
964
+ var PAYMENT_SUCCESS_STATUSES = /* @__PURE__ */ new Set([
965
+ "success",
966
+ "succeeded",
967
+ "paid",
968
+ "captured",
969
+ "completed",
970
+ "authorized"
971
+ ]);
972
+ var PAYMENT_FAILURE_STATUSES = /* @__PURE__ */ new Set([
973
+ "failed",
974
+ "declined",
975
+ "cancelled",
976
+ "voided",
977
+ "error"
978
+ ]);
979
+ var PAYMENT_REQUIRES_ACTION_STATUSES = /* @__PURE__ */ new Set([
980
+ "requires_action",
981
+ "requires_payment_method",
982
+ "requires_capture"
983
+ ]);
984
+ var PAYMENT_STATUS_ALIAS_MAP = {
985
+ ok: "success",
986
+ done: "success",
987
+ paid: "paid",
988
+ paid_in_full: "paid",
989
+ paid_successfully: "paid",
990
+ succeeded: "success",
991
+ captured: "captured",
992
+ completed: "completed",
993
+ pending_confirmation: "pending_confirmation",
994
+ requires_authorization: "requires_action",
995
+ requires_action: "requires_action",
996
+ requires_payment_method: "requires_payment_method",
997
+ requires_capture: "requires_capture",
998
+ partially_paid: "partially_paid",
999
+ partially_refunded: "partially_refunded",
1000
+ card_declined: "declined",
1001
+ canceled: "cancelled",
1002
+ authorized: "authorized",
1003
+ cancelled: "cancelled",
1004
+ unresolved: "pending"
1005
+ };
1006
+ var KNOWN_PAYMENT_STATUSES = /* @__PURE__ */ new Set([
1007
+ "pending",
1008
+ "processing",
1009
+ "created",
1010
+ "pending_confirmation",
1011
+ "success",
1012
+ "succeeded",
1013
+ "failed",
1014
+ "declined",
1015
+ "authorized",
1016
+ "refunded",
1017
+ "partially_refunded",
1018
+ "partially_paid",
1019
+ "paid",
1020
+ "unpaid",
1021
+ "requires_action",
1022
+ "requires_payment_method",
1023
+ "requires_capture",
1024
+ "captured",
1025
+ "cancelled",
1026
+ "completed",
1027
+ "voided",
1028
+ "error",
1029
+ "unknown"
1030
+ ]);
1031
+ function normalizeStatusToken(status) {
1032
+ return status?.trim().toLowerCase().replace(/[\s-]+/g, "_") ?? "";
1033
+ }
1034
+ function normalizePaymentStatusValue(status) {
1035
+ const normalized = normalizeStatusToken(status);
1036
+ if (Object.prototype.hasOwnProperty.call(PAYMENT_STATUS_ALIAS_MAP, normalized)) {
1037
+ return PAYMENT_STATUS_ALIAS_MAP[normalized];
1038
+ }
1039
+ return KNOWN_PAYMENT_STATUSES.has(normalized) ? normalized : "unknown";
1040
+ }
1041
+ function isPaymentStatusSuccess(status) {
1042
+ const normalizedStatus = normalizePaymentStatusValue(status);
1043
+ return PAYMENT_SUCCESS_STATUSES.has(normalizedStatus);
1044
+ }
1045
+ function isPaymentStatusFailure(status) {
1046
+ const normalizedStatus = normalizePaymentStatusValue(status);
1047
+ return PAYMENT_FAILURE_STATUSES.has(normalizedStatus);
1048
+ }
1049
+ function isPaymentStatusRequiresAction(status) {
1050
+ const normalizedStatus = normalizePaymentStatusValue(status);
1051
+ return PAYMENT_REQUIRES_ACTION_STATUSES.has(normalizedStatus);
1052
+ }
1053
+ function normalizeStatusResponse(response) {
1054
+ if (!response || typeof response !== "object") {
1055
+ return {
1056
+ status: "pending",
1057
+ paid: false,
1058
+ message: "No status available"
1059
+ };
778
1060
  }
779
- async enrollAndLinkOrder(data) {
780
- return safe5(
781
- this.client.call(LINK_MUTATION.ENROLL_AND_LINK_ORDER, data)
782
- );
1061
+ const res = response;
1062
+ const normalizedStatus = normalizePaymentStatusValue(res.status ?? void 0);
1063
+ const paidValue = res.paid === true;
1064
+ const derivedPaid = paidValue || [
1065
+ "success",
1066
+ "succeeded",
1067
+ "paid",
1068
+ "captured",
1069
+ "authorized",
1070
+ "completed"
1071
+ ].includes(normalizedStatus);
1072
+ return {
1073
+ status: normalizedStatus,
1074
+ paid: derivedPaid,
1075
+ amount: res.amount,
1076
+ currency: res.currency,
1077
+ reference: res.reference,
1078
+ message: res.message || ""
1079
+ };
1080
+ }
1081
+ var MOBILE_MONEY_PROVIDERS = {
1082
+ mtn: { name: "MTN Mobile Money", prefix: ["024", "054", "055", "059"] },
1083
+ vodafone: { name: "Vodafone Cash", prefix: ["020", "050"] },
1084
+ airtel: { name: "AirtelTigo Money", prefix: ["027", "057", "026", "056"] }
1085
+ };
1086
+ function detectMobileMoneyProvider(phoneNumber) {
1087
+ const cleaned = phoneNumber.replace(/\D/g, "");
1088
+ const prefix = cleaned.slice(-9, -6);
1089
+ for (const [provider, info] of Object.entries(MOBILE_MONEY_PROVIDERS)) {
1090
+ if (info.prefix.some((p) => prefix.startsWith(p.slice(1)))) {
1091
+ return provider;
1092
+ }
783
1093
  }
784
- async updatePreferences(preferences) {
785
- return safe5(this.client.call(LINK_MUTATION.UPDATE_PREFERENCES, preferences));
1094
+ return null;
1095
+ }
1096
+
1097
+ // src/utils/paystack.ts
1098
+ async function openPaystackPopup(options, signal) {
1099
+ if (typeof window === "undefined") {
1100
+ return { success: false, error: "PAYSTACK_UNAVAILABLE" };
786
1101
  }
787
- async createAddress(input) {
788
- return safe5(this.client.call(LINK_MUTATION.CREATE_ADDRESS, input));
1102
+ let PaystackPop;
1103
+ try {
1104
+ const imported = await import('@paystack/inline-js');
1105
+ PaystackPop = imported.default;
1106
+ } catch {
1107
+ return { success: false, error: "PAYSTACK_UNAVAILABLE" };
789
1108
  }
790
- async updateAddress(input) {
791
- return safe5(this.client.call(LINK_MUTATION.UPDATE_ADDRESS, input));
1109
+ return new Promise((resolve) => {
1110
+ let settled = false;
1111
+ const resolveOnce = (result) => {
1112
+ if (settled) {
1113
+ return;
1114
+ }
1115
+ settled = true;
1116
+ if (signal) {
1117
+ signal.removeEventListener("abort", onAbort);
1118
+ }
1119
+ resolve(result);
1120
+ };
1121
+ const onAbort = () => {
1122
+ resolveOnce({ success: false, error: "CANCELLED" });
1123
+ };
1124
+ if (signal?.aborted) {
1125
+ resolveOnce({ success: false, error: "CANCELLED" });
1126
+ return;
1127
+ }
1128
+ if (signal) {
1129
+ signal.addEventListener("abort", onAbort, { once: true });
1130
+ }
1131
+ try {
1132
+ const popup = new PaystackPop();
1133
+ popup.newTransaction({
1134
+ key: options.key,
1135
+ email: options.email,
1136
+ amount: options.amount,
1137
+ currency: options.currency,
1138
+ reference: options.reference,
1139
+ accessCode: options.accessCode,
1140
+ onSuccess: (transaction) => {
1141
+ resolveOnce({
1142
+ success: true,
1143
+ reference: transaction.reference ?? options.reference
1144
+ });
1145
+ },
1146
+ onCancel: () => {
1147
+ resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
1148
+ },
1149
+ onError: (error) => {
1150
+ resolveOnce({
1151
+ success: false,
1152
+ error: error?.message || "PAYMENT_FAILED"
1153
+ });
1154
+ }
1155
+ });
1156
+ } catch {
1157
+ resolveOnce({ success: false, error: "POPUP_BLOCKED" });
1158
+ }
1159
+ });
1160
+ }
1161
+
1162
+ // src/utils/checkout-resolver.ts
1163
+ var DEFAULT_POLL_INTERVAL_MS = 3e3;
1164
+ var DEFAULT_MAX_POLL_ATTEMPTS = 60;
1165
+ var MAX_CONSECUTIVE_NETWORK_ERRORS = 5;
1166
+ function normalizeAuthorizationType(value) {
1167
+ return value === "otp" || value === "pin" ? value : void 0;
1168
+ }
1169
+ function formatMoney2(value) {
1170
+ if (typeof value === "number" && Number.isFinite(value)) {
1171
+ return value.toFixed(2);
792
1172
  }
793
- async deleteAddress(addressId) {
794
- return safe5(this.client.call(LINK_MUTATION.DELETE_ADDRESS, addressId));
1173
+ if (typeof value === "string" && value.trim().length > 0) {
1174
+ return value;
795
1175
  }
796
- async setDefaultAddress(addressId) {
797
- return safe5(this.client.call(LINK_MUTATION.SET_DEFAULT_ADDRESS, addressId));
1176
+ return "0.00";
1177
+ }
1178
+ function createAbortError() {
1179
+ const error = new Error("CANCELLED");
1180
+ error.name = "AbortError";
1181
+ return error;
1182
+ }
1183
+ function isAbortError(error) {
1184
+ if (error instanceof DOMException && error.name === "AbortError") {
1185
+ return true;
798
1186
  }
799
- async trackAddressUsage(addressId) {
800
- return safe5(
801
- this.client.call(LINK_MUTATION.TRACK_ADDRESS_USAGE, {
802
- address_id: addressId
803
- })
804
- );
805
- }
806
- async createMobileMoney(input) {
807
- return safe5(this.client.call(LINK_MUTATION.CREATE_MOBILE_MONEY, input));
808
- }
809
- async deleteMobileMoney(mobileMoneyId) {
810
- return safe5(this.client.call(LINK_MUTATION.DELETE_MOBILE_MONEY, mobileMoneyId));
811
- }
812
- async setDefaultMobileMoney(mobileMoneyId) {
813
- return safe5(
814
- this.client.call(LINK_MUTATION.SET_DEFAULT_MOBILE_MONEY, mobileMoneyId)
1187
+ return error instanceof Error && (error.name === "AbortError" || error.message === "CANCELLED");
1188
+ }
1189
+ var CheckoutResolver = class {
1190
+ constructor(options) {
1191
+ this.client = options.client;
1192
+ this.checkoutData = options.checkoutData;
1193
+ this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
1194
+ this.maxPollAttempts = options.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;
1195
+ this.onStatusChange = options.onStatusChange;
1196
+ this.onAuthorizationRequired = options.onAuthorizationRequired;
1197
+ this.signal = options.signal;
1198
+ this.returnUrl = options.returnUrl;
1199
+ this.enrollInLink = options.enrollInLink !== false;
1200
+ this.businessId = options.businessId;
1201
+ this.addressData = options.addressData;
1202
+ this.paymentData = options.paymentData;
1203
+ this.orderType = options.orderType;
1204
+ this.cartTotal = options.cartTotal;
1205
+ this.cartCurrency = options.cartCurrency;
1206
+ this.allowCardPopup = options.allowCardPopup ?? false;
1207
+ }
1208
+ async resolve(checkoutResult) {
1209
+ try {
1210
+ this.ensureNotAborted();
1211
+ if (!checkoutResult.order_id) {
1212
+ return this.fail("CHECKOUT_FAILED", "Checkout did not return an order ID.", false);
1213
+ }
1214
+ let latestCheckoutResult = checkoutResult;
1215
+ let authorizationType = normalizeAuthorizationType(checkoutResult.authorization_type);
1216
+ let paymentReference = checkoutResult.payment_reference;
1217
+ if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
1218
+ return this.finalizeSuccess(checkoutResult);
1219
+ }
1220
+ if (checkoutResult.client_secret && checkoutResult.public_key) {
1221
+ if (!this.allowCardPopup) {
1222
+ return this.fail(
1223
+ "PAYSTACK_HANDOFF_REQUIRED",
1224
+ "Card authorization requires Elements. Use elements.processCheckout() for card flows.",
1225
+ true
1226
+ );
1227
+ }
1228
+ this.emit("awaiting_authorization", {
1229
+ authorization_type: authorizationType,
1230
+ display_text: "Complete payment in the card authorization popup.",
1231
+ order_id: checkoutResult.order_id,
1232
+ order_number: checkoutResult.order_number,
1233
+ provider: checkoutResult.provider ?? "paystack"
1234
+ });
1235
+ const popupResult = await openPaystackPopup(
1236
+ {
1237
+ key: checkoutResult.public_key,
1238
+ email: this.checkoutData.customer.email || "customer@cimplify.io",
1239
+ accessCode: checkoutResult.client_secret,
1240
+ reference: paymentReference || checkoutResult.client_secret,
1241
+ currency: this.getOrderCurrency(checkoutResult)
1242
+ },
1243
+ this.signal
1244
+ );
1245
+ if (!popupResult.success) {
1246
+ if (popupResult.error === "POPUP_BLOCKED" || popupResult.error === "PAYSTACK_UNAVAILABLE") {
1247
+ return this.fail(
1248
+ "POPUP_BLOCKED",
1249
+ "Unable to open card payment popup. Please allow popups and try again.",
1250
+ true
1251
+ );
1252
+ }
1253
+ return this.fail(
1254
+ popupResult.error || "PAYMENT_CANCELLED",
1255
+ "Card payment was cancelled before completion.",
1256
+ true
1257
+ );
1258
+ }
1259
+ }
1260
+ if (checkoutResult.authorization_url) {
1261
+ if (typeof window !== "undefined" && this.returnUrl) {
1262
+ window.location.assign(checkoutResult.authorization_url);
1263
+ return this.fail(
1264
+ "REDIRECT_REQUIRED",
1265
+ "Redirecting to complete payment authorization.",
1266
+ true
1267
+ );
1268
+ }
1269
+ if (typeof window !== "undefined") {
1270
+ const popup = window.open(
1271
+ checkoutResult.authorization_url,
1272
+ "cimplify-auth",
1273
+ "width=520,height=760,noopener,noreferrer"
1274
+ );
1275
+ if (!popup) {
1276
+ return this.fail(
1277
+ "POPUP_BLOCKED",
1278
+ "Authorization popup was blocked. Please allow popups and retry.",
1279
+ true
1280
+ );
1281
+ }
1282
+ }
1283
+ }
1284
+ if (checkoutResult.requires_authorization || isPaymentStatusRequiresAction(checkoutResult.payment_status)) {
1285
+ const authorization = await this.handleAuthorization({
1286
+ authorizationType,
1287
+ paymentReference,
1288
+ displayText: checkoutResult.display_text,
1289
+ provider: checkoutResult.provider
1290
+ });
1291
+ if (!authorization.ok) {
1292
+ return authorization.result;
1293
+ }
1294
+ if (authorization.value) {
1295
+ latestCheckoutResult = authorization.value;
1296
+ paymentReference = authorization.value.payment_reference || paymentReference;
1297
+ authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
1298
+ if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1299
+ return this.finalizeSuccess(authorization.value);
1300
+ }
1301
+ if (this.isFailureStatus(authorization.value.payment_status)) {
1302
+ return this.fail(
1303
+ "AUTHORIZATION_FAILED",
1304
+ authorization.value.display_text || "Payment authorization failed.",
1305
+ false
1306
+ );
1307
+ }
1308
+ }
1309
+ }
1310
+ return this.pollUntilTerminal({
1311
+ orderId: checkoutResult.order_id,
1312
+ latestCheckoutResult,
1313
+ paymentReference,
1314
+ authorizationType
1315
+ });
1316
+ } catch (error) {
1317
+ if (isAbortError(error)) {
1318
+ return this.fail("CANCELLED", "Checkout was cancelled.", true);
1319
+ }
1320
+ const message = error instanceof Error ? error.message : "Checkout failed unexpectedly.";
1321
+ return this.fail("CHECKOUT_FAILED", message, true);
1322
+ }
1323
+ }
1324
+ async pollUntilTerminal(input) {
1325
+ let consecutiveErrors = 0;
1326
+ let latestCheckoutResult = input.latestCheckoutResult;
1327
+ let paymentReference = input.paymentReference;
1328
+ let authorizationType = input.authorizationType;
1329
+ for (let attempt = 0; attempt < this.maxPollAttempts; attempt++) {
1330
+ this.ensureNotAborted();
1331
+ this.emit("polling", {
1332
+ poll_attempt: attempt,
1333
+ max_poll_attempts: this.maxPollAttempts,
1334
+ order_id: input.orderId,
1335
+ order_number: latestCheckoutResult.order_number
1336
+ });
1337
+ const statusResult = await this.client.checkout.pollPaymentStatus(input.orderId);
1338
+ if (!statusResult.ok) {
1339
+ consecutiveErrors += 1;
1340
+ if (consecutiveErrors >= MAX_CONSECUTIVE_NETWORK_ERRORS) {
1341
+ return this.fail(
1342
+ "NETWORK_ERROR",
1343
+ "Unable to confirm payment due to repeated network errors.",
1344
+ true
1345
+ );
1346
+ }
1347
+ await this.wait(this.pollIntervalMs);
1348
+ continue;
1349
+ }
1350
+ consecutiveErrors = 0;
1351
+ if (statusResult.value.reference) {
1352
+ paymentReference = statusResult.value.reference;
1353
+ }
1354
+ const normalized = normalizeStatusResponse(statusResult.value);
1355
+ if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
1356
+ return this.finalizeSuccess(latestCheckoutResult);
1357
+ }
1358
+ if (isPaymentStatusFailure(normalized.status)) {
1359
+ return this.fail(
1360
+ normalized.status ? normalized.status.toUpperCase() : "PAYMENT_FAILED",
1361
+ normalized.message || "Payment failed during confirmation.",
1362
+ false
1363
+ );
1364
+ }
1365
+ if (isPaymentStatusRequiresAction(normalized.status)) {
1366
+ const authorization = await this.handleAuthorization({
1367
+ authorizationType,
1368
+ paymentReference,
1369
+ displayText: normalized.message || latestCheckoutResult.display_text,
1370
+ provider: latestCheckoutResult.provider
1371
+ });
1372
+ if (!authorization.ok) {
1373
+ return authorization.result;
1374
+ }
1375
+ if (authorization.value) {
1376
+ latestCheckoutResult = authorization.value;
1377
+ paymentReference = authorization.value.payment_reference || paymentReference;
1378
+ authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
1379
+ if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1380
+ return this.finalizeSuccess(authorization.value);
1381
+ }
1382
+ if (this.isFailureStatus(authorization.value.payment_status)) {
1383
+ return this.fail(
1384
+ "AUTHORIZATION_FAILED",
1385
+ authorization.value.display_text || "Payment authorization failed.",
1386
+ false
1387
+ );
1388
+ }
1389
+ }
1390
+ }
1391
+ await this.wait(this.pollIntervalMs);
1392
+ }
1393
+ return this.fail(
1394
+ "PAYMENT_TIMEOUT",
1395
+ "Payment confirmation timed out. Please retry checkout.",
1396
+ true
815
1397
  );
816
1398
  }
817
- async trackMobileMoneyUsage(mobileMoneyId) {
818
- return safe5(
819
- this.client.call(LINK_MUTATION.TRACK_MOBILE_MONEY_USAGE, {
820
- mobile_money_id: mobileMoneyId
821
- })
822
- );
1399
+ async handleAuthorization(input) {
1400
+ const authorizationType = input.authorizationType;
1401
+ const paymentReference = input.paymentReference;
1402
+ if (!authorizationType || !paymentReference) {
1403
+ return { ok: true };
1404
+ }
1405
+ this.emit("awaiting_authorization", {
1406
+ authorization_type: authorizationType,
1407
+ display_text: input.displayText,
1408
+ provider: input.provider
1409
+ });
1410
+ let authorizationResult;
1411
+ const submit = async (code) => {
1412
+ this.ensureNotAborted();
1413
+ const trimmedCode = code.trim();
1414
+ if (!trimmedCode) {
1415
+ throw new Error("Authorization code is required.");
1416
+ }
1417
+ const submitResult = await this.client.checkout.submitAuthorization({
1418
+ reference: paymentReference,
1419
+ auth_type: authorizationType,
1420
+ value: trimmedCode
1421
+ });
1422
+ if (!submitResult.ok) {
1423
+ throw new Error(submitResult.error.message || "Authorization failed.");
1424
+ }
1425
+ authorizationResult = submitResult.value;
1426
+ };
1427
+ try {
1428
+ if (this.onAuthorizationRequired) {
1429
+ await this.onAuthorizationRequired(authorizationType, submit);
1430
+ } else {
1431
+ return {
1432
+ ok: false,
1433
+ result: this.fail(
1434
+ "AUTHORIZATION_REQUIRED",
1435
+ "Authorization callback is required in headless checkout mode.",
1436
+ false
1437
+ )
1438
+ };
1439
+ }
1440
+ } catch (error) {
1441
+ if (isAbortError(error)) {
1442
+ return {
1443
+ ok: false,
1444
+ result: this.fail("CANCELLED", "Checkout was cancelled.", true)
1445
+ };
1446
+ }
1447
+ const message = error instanceof Error ? error.message : "Payment authorization failed.";
1448
+ return {
1449
+ ok: false,
1450
+ result: this.fail("AUTHORIZATION_FAILED", message, true)
1451
+ };
1452
+ }
1453
+ return {
1454
+ ok: true,
1455
+ value: authorizationResult
1456
+ };
823
1457
  }
824
- async verifyMobileMoney(mobileMoneyId) {
825
- return safe5(
826
- this.client.call(LINK_MUTATION.VERIFY_MOBILE_MONEY, mobileMoneyId)
827
- );
1458
+ async finalizeSuccess(checkoutResult) {
1459
+ this.emit("finalizing", {
1460
+ order_id: checkoutResult.order_id,
1461
+ order_number: checkoutResult.order_number
1462
+ });
1463
+ const enrolledInLink = await this.maybeEnrollInLink(checkoutResult.order_id);
1464
+ this.emit("success", {
1465
+ order_id: checkoutResult.order_id,
1466
+ order_number: checkoutResult.order_number
1467
+ });
1468
+ return {
1469
+ success: true,
1470
+ order: {
1471
+ id: checkoutResult.order_id,
1472
+ order_number: checkoutResult.order_number || checkoutResult.order_id,
1473
+ status: checkoutResult.payment_status || "paid",
1474
+ total: this.getOrderTotal(checkoutResult),
1475
+ currency: this.getOrderCurrency(checkoutResult)
1476
+ },
1477
+ enrolled_in_link: enrolledInLink
1478
+ };
828
1479
  }
829
- async getSessions() {
830
- return safe5(this.client.linkGet("/v1/link/sessions"));
1480
+ async maybeEnrollInLink(orderId) {
1481
+ if (!this.enrollInLink || !orderId) {
1482
+ return false;
1483
+ }
1484
+ let businessId = this.businessId;
1485
+ if (!businessId) {
1486
+ const businessResult = await this.client.business.getInfo();
1487
+ if (!businessResult.ok || !businessResult.value?.id) {
1488
+ return false;
1489
+ }
1490
+ businessId = businessResult.value.id;
1491
+ }
1492
+ const address = this.getEnrollmentAddress();
1493
+ const mobileMoney = this.getEnrollmentMobileMoney();
1494
+ try {
1495
+ const enrollment = await this.client.link.enrollAndLinkOrder({
1496
+ order_id: orderId,
1497
+ business_id: businessId,
1498
+ order_type: this.orderType || this.checkoutData.order_type,
1499
+ address,
1500
+ mobile_money: mobileMoney
1501
+ });
1502
+ return enrollment.ok && enrollment.value.success;
1503
+ } catch {
1504
+ return false;
1505
+ }
831
1506
  }
832
- async revokeSession(sessionId) {
833
- return safe5(this.client.linkDelete(`/v1/link/sessions/${sessionId}`));
1507
+ getEnrollmentAddress() {
1508
+ const source = this.addressData ?? this.checkoutData.address_info;
1509
+ if (!source?.street_address) {
1510
+ return void 0;
1511
+ }
1512
+ return {
1513
+ label: "Default",
1514
+ street_address: source.street_address,
1515
+ apartment: source.apartment || void 0,
1516
+ city: source.city || "",
1517
+ region: source.region || "",
1518
+ delivery_instructions: source.delivery_instructions || void 0,
1519
+ phone_for_delivery: source.phone_for_delivery || void 0
1520
+ };
834
1521
  }
835
- async revokeAllSessions() {
836
- return safe5(this.client.linkDelete("/v1/link/sessions"));
1522
+ getEnrollmentMobileMoney() {
1523
+ if (this.paymentData?.type === "mobile_money" && this.paymentData.phone_number && this.paymentData.provider) {
1524
+ return {
1525
+ phone_number: this.paymentData.phone_number,
1526
+ provider: this.paymentData.provider,
1527
+ label: this.paymentData.label || "Mobile Money"
1528
+ };
1529
+ }
1530
+ if (this.checkoutData.mobile_money_details?.phone_number) {
1531
+ return {
1532
+ phone_number: this.checkoutData.mobile_money_details.phone_number,
1533
+ provider: this.checkoutData.mobile_money_details.provider,
1534
+ label: "Mobile Money"
1535
+ };
1536
+ }
1537
+ return void 0;
837
1538
  }
838
- async getAddressesRest() {
839
- return safe5(this.client.linkGet("/v1/link/addresses"));
1539
+ getOrderTotal(checkoutResult) {
1540
+ if (checkoutResult.fx?.pay_amount !== void 0) {
1541
+ return formatMoney2(checkoutResult.fx.pay_amount);
1542
+ }
1543
+ if (this.cartTotal !== void 0) {
1544
+ return formatMoney2(this.cartTotal);
1545
+ }
1546
+ return "0.00";
840
1547
  }
841
- async createAddressRest(input) {
842
- return safe5(this.client.linkPost("/v1/link/addresses", input));
1548
+ getOrderCurrency(checkoutResult) {
1549
+ if (checkoutResult.fx?.pay_currency) {
1550
+ return checkoutResult.fx.pay_currency;
1551
+ }
1552
+ if (this.cartCurrency) {
1553
+ return this.cartCurrency;
1554
+ }
1555
+ if (this.checkoutData.pay_currency) {
1556
+ return this.checkoutData.pay_currency;
1557
+ }
1558
+ return "GHS";
843
1559
  }
844
- async deleteAddressRest(addressId) {
845
- return safe5(this.client.linkDelete(`/v1/link/addresses/${addressId}`));
1560
+ emit(status, context = {}) {
1561
+ if (!this.onStatusChange) {
1562
+ return;
1563
+ }
1564
+ try {
1565
+ this.onStatusChange(status, context);
1566
+ } catch {
1567
+ }
846
1568
  }
847
- async setDefaultAddressRest(addressId) {
848
- return safe5(this.client.linkPost(`/v1/link/addresses/${addressId}/default`));
1569
+ fail(code, message, recoverable) {
1570
+ this.emit("failed", {
1571
+ display_text: message
1572
+ });
1573
+ return {
1574
+ success: false,
1575
+ error: {
1576
+ code,
1577
+ message,
1578
+ recoverable
1579
+ }
1580
+ };
849
1581
  }
850
- async getMobileMoneyRest() {
851
- return safe5(this.client.linkGet("/v1/link/mobile-money"));
1582
+ isSuccessfulStatus(status) {
1583
+ const normalized = normalizeStatusResponse({ status, paid: false });
1584
+ return normalized.paid || isPaymentStatusSuccess(normalized.status);
852
1585
  }
853
- async createMobileMoneyRest(input) {
854
- return safe5(this.client.linkPost("/v1/link/mobile-money", input));
1586
+ isFailureStatus(status) {
1587
+ const normalized = normalizeStatusResponse({ status, paid: false });
1588
+ return isPaymentStatusFailure(normalized.status);
855
1589
  }
856
- async deleteMobileMoneyRest(mobileMoneyId) {
857
- return safe5(this.client.linkDelete(`/v1/link/mobile-money/${mobileMoneyId}`));
1590
+ ensureNotAborted() {
1591
+ if (this.signal?.aborted) {
1592
+ throw createAbortError();
1593
+ }
858
1594
  }
859
- async setDefaultMobileMoneyRest(mobileMoneyId) {
860
- return safe5(
861
- this.client.linkPost(`/v1/link/mobile-money/${mobileMoneyId}/default`)
862
- );
1595
+ wait(ms) {
1596
+ this.ensureNotAborted();
1597
+ return new Promise((resolve, reject) => {
1598
+ const timer = setTimeout(() => {
1599
+ if (this.signal) {
1600
+ this.signal.removeEventListener("abort", onAbort);
1601
+ }
1602
+ resolve();
1603
+ }, ms);
1604
+ const onAbort = () => {
1605
+ clearTimeout(timer);
1606
+ reject(createAbortError());
1607
+ };
1608
+ if (this.signal) {
1609
+ this.signal.addEventListener("abort", onAbort, { once: true });
1610
+ }
1611
+ });
863
1612
  }
864
1613
  };
865
1614
 
866
- // src/auth.ts
867
- function toCimplifyError6(error) {
1615
+ // src/checkout.ts
1616
+ function toCimplifyError3(error) {
868
1617
  if (error instanceof CimplifyError) return error;
869
1618
  if (error instanceof Error) {
870
1619
  return new CimplifyError("UNKNOWN_ERROR", error.message, false);
871
1620
  }
872
1621
  return new CimplifyError("UNKNOWN_ERROR", String(error), false);
873
1622
  }
874
- async function safe6(promise) {
1623
+ async function safe3(promise) {
875
1624
  try {
876
1625
  return ok(await promise);
877
1626
  } catch (error) {
878
- return err(toCimplifyError6(error));
1627
+ return err(toCimplifyError3(error));
879
1628
  }
880
1629
  }
881
- var AuthService = class {
1630
+ function toTerminalFailure(code, message, recoverable) {
1631
+ return {
1632
+ success: false,
1633
+ error: {
1634
+ code,
1635
+ message,
1636
+ recoverable
1637
+ }
1638
+ };
1639
+ }
1640
+ function generateIdempotencyKey() {
1641
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
1642
+ return `idem_${crypto.randomUUID()}`;
1643
+ }
1644
+ const timestamp = Date.now().toString(36);
1645
+ const random = Math.random().toString(36).substring(2, 15);
1646
+ return `idem_${timestamp}_${random}`;
1647
+ }
1648
+ var CheckoutService = class {
882
1649
  constructor(client) {
883
1650
  this.client = client;
884
1651
  }
885
- async getStatus() {
886
- return safe6(this.client.query("auth"));
887
- }
888
- async getCurrentUser() {
889
- const result = await this.getStatus();
890
- if (!result.ok) return result;
891
- return ok(result.value.customer || null);
892
- }
893
- async isAuthenticated() {
894
- const result = await this.getStatus();
895
- if (!result.ok) return result;
896
- return ok(result.value.is_authenticated);
897
- }
898
- async requestOtp(contact, contactType) {
899
- return safe6(
900
- this.client.call(AUTH_MUTATION.REQUEST_OTP, {
901
- contact,
902
- contact_type: contactType
1652
+ async process(data) {
1653
+ const checkoutData = {
1654
+ ...data,
1655
+ idempotency_key: data.idempotency_key || generateIdempotencyKey()
1656
+ };
1657
+ return safe3(
1658
+ this.client.call(CHECKOUT_MUTATION.PROCESS, {
1659
+ checkout_data: checkoutData
903
1660
  })
904
1661
  );
905
1662
  }
906
- async verifyOtp(code, contact) {
907
- return safe6(
908
- this.client.call(AUTH_MUTATION.VERIFY_OTP, {
909
- otp_code: code,
910
- contact
1663
+ async initializePayment(orderId, method) {
1664
+ return safe3(
1665
+ this.client.call("order.initializePayment", {
1666
+ order_id: orderId,
1667
+ payment_method: method
911
1668
  })
912
1669
  );
913
1670
  }
914
- async logout() {
915
- return safe6(this.client.call("auth.logout"));
1671
+ async submitAuthorization(input) {
1672
+ return safe3(
1673
+ this.client.call(PAYMENT_MUTATION.SUBMIT_AUTHORIZATION, input)
1674
+ );
916
1675
  }
917
- async updateProfile(input) {
918
- return safe6(this.client.call("auth.update_profile", input));
1676
+ async pollPaymentStatus(orderId) {
1677
+ return safe3(
1678
+ this.client.call(PAYMENT_MUTATION.CHECK_STATUS, orderId)
1679
+ );
919
1680
  }
920
- async changePassword(input) {
921
- return safe6(this.client.call("auth.change_password", input));
1681
+ async updateOrderCustomer(orderId, customer) {
1682
+ return safe3(
1683
+ this.client.call(ORDER_MUTATION.UPDATE_CUSTOMER, {
1684
+ order_id: orderId,
1685
+ ...customer
1686
+ })
1687
+ );
922
1688
  }
923
- async resetPassword(email) {
924
- return safe6(this.client.call("auth.reset_password", { email }));
1689
+ async verifyPayment(orderId) {
1690
+ return safe3(
1691
+ this.client.call("order.verifyPayment", {
1692
+ order_id: orderId
1693
+ })
1694
+ );
1695
+ }
1696
+ async processAndResolve(data) {
1697
+ data.on_status_change?.("preparing", {});
1698
+ if (!data.cart_id) {
1699
+ return ok(
1700
+ toTerminalFailure(
1701
+ "INVALID_CART",
1702
+ "A valid cart is required before checkout can start.",
1703
+ false
1704
+ )
1705
+ );
1706
+ }
1707
+ const cartResult = await this.client.cart.get();
1708
+ if (!cartResult.ok || !cartResult.value?.id) {
1709
+ return ok(
1710
+ toTerminalFailure(
1711
+ "INVALID_CART",
1712
+ "Unable to load cart for checkout.",
1713
+ false
1714
+ )
1715
+ );
1716
+ }
1717
+ const cart = cartResult.value;
1718
+ if (cart.id !== data.cart_id || cart.items.length === 0) {
1719
+ return ok(
1720
+ toTerminalFailure(
1721
+ "INVALID_CART",
1722
+ "Cart is empty or no longer valid. Please refresh and try again.",
1723
+ false
1724
+ )
1725
+ );
1726
+ }
1727
+ const checkoutData = {
1728
+ cart_id: data.cart_id,
1729
+ location_id: data.location_id,
1730
+ customer: data.customer,
1731
+ order_type: data.order_type,
1732
+ address_info: data.address_info,
1733
+ payment_method: data.payment_method,
1734
+ mobile_money_details: data.mobile_money_details,
1735
+ special_instructions: data.special_instructions,
1736
+ link_address_id: data.link_address_id,
1737
+ link_payment_method_id: data.link_payment_method_id,
1738
+ idempotency_key: data.idempotency_key || generateIdempotencyKey(),
1739
+ metadata: data.metadata,
1740
+ pay_currency: data.pay_currency,
1741
+ fx_quote_id: data.fx_quote_id
1742
+ };
1743
+ const baseCurrency = (cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase();
1744
+ const payCurrency = data.pay_currency?.trim().toUpperCase();
1745
+ const cartTotalAmount = Number.parseFloat(cart.pricing.total_price || "0");
1746
+ if (payCurrency && payCurrency !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
1747
+ const fxQuoteResult = await this.client.fx.lockQuote({
1748
+ from: baseCurrency,
1749
+ to: payCurrency,
1750
+ amount: cartTotalAmount
1751
+ });
1752
+ if (!fxQuoteResult.ok) {
1753
+ return ok(
1754
+ toTerminalFailure(
1755
+ "FX_QUOTE_FAILED",
1756
+ fxQuoteResult.error.message || "Unable to lock FX quote for checkout.",
1757
+ true
1758
+ )
1759
+ );
1760
+ }
1761
+ checkoutData.pay_currency = payCurrency;
1762
+ checkoutData.fx_quote_id = fxQuoteResult.value.id;
1763
+ }
1764
+ data.on_status_change?.("processing", {});
1765
+ const processResult = await this.process(checkoutData);
1766
+ if (!processResult.ok) {
1767
+ return ok(
1768
+ toTerminalFailure(
1769
+ processResult.error.code || "CHECKOUT_FAILED",
1770
+ processResult.error.message || "Unable to process checkout.",
1771
+ processResult.error.retryable ?? true
1772
+ )
1773
+ );
1774
+ }
1775
+ const resolver = new CheckoutResolver({
1776
+ client: this.client,
1777
+ checkoutData,
1778
+ pollIntervalMs: data.poll_interval_ms,
1779
+ maxPollAttempts: data.max_poll_attempts,
1780
+ onStatusChange: data.on_status_change,
1781
+ onAuthorizationRequired: data.on_authorization_required,
1782
+ signal: data.signal,
1783
+ returnUrl: data.return_url,
1784
+ enrollInLink: data.enroll_in_link,
1785
+ cartTotal: cart.pricing.total_price,
1786
+ cartCurrency: checkoutData.pay_currency || cart.pricing.currency || "GHS",
1787
+ orderType: checkoutData.order_type,
1788
+ allowCardPopup: false
1789
+ });
1790
+ const resolved = await resolver.resolve(processResult.value);
1791
+ return ok(resolved);
925
1792
  }
926
1793
  };
927
1794
 
928
- // src/business.ts
929
- function toCimplifyError7(error) {
1795
+ // src/orders.ts
1796
+ function toCimplifyError4(error) {
930
1797
  if (error instanceof CimplifyError) return error;
931
1798
  if (error instanceof Error) {
932
1799
  return new CimplifyError("UNKNOWN_ERROR", error.message, false);
933
1800
  }
934
1801
  return new CimplifyError("UNKNOWN_ERROR", String(error), false);
935
1802
  }
936
- async function safe7(promise) {
1803
+ async function safe4(promise) {
937
1804
  try {
938
1805
  return ok(await promise);
939
1806
  } catch (error) {
940
- return err(toCimplifyError7(error));
1807
+ return err(toCimplifyError4(error));
941
1808
  }
942
1809
  }
943
- var BusinessService = class {
1810
+ var OrderQueries = class {
944
1811
  constructor(client) {
945
1812
  this.client = client;
946
1813
  }
947
- async getInfo() {
948
- return safe7(this.client.query("business.info"));
949
- }
950
- async getByHandle(handle) {
951
- return safe7(this.client.query(`business.handle.${handle}`));
952
- }
953
- async getByDomain(domain) {
954
- return safe7(this.client.query("business.domain", { domain }));
955
- }
956
- async getSettings() {
957
- return safe7(this.client.query("business.settings"));
958
- }
959
- async getTheme() {
960
- return safe7(this.client.query("business.theme"));
961
- }
962
- async getLocations() {
963
- return safe7(this.client.query("business.locations"));
1814
+ async list(options) {
1815
+ let query2 = "orders";
1816
+ if (options?.status) {
1817
+ query2 += `[?(@.status=='${options.status}')]`;
1818
+ }
1819
+ query2 += "#sort(created_at,desc)";
1820
+ if (options?.limit) {
1821
+ query2 += `#limit(${options.limit})`;
1822
+ }
1823
+ if (options?.offset) {
1824
+ query2 += `#offset(${options.offset})`;
1825
+ }
1826
+ return safe4(this.client.query(query2));
964
1827
  }
965
- async getLocation(locationId) {
966
- return safe7(this.client.query(`business.locations.${locationId}`));
1828
+ async get(orderId) {
1829
+ return safe4(this.client.query(`orders.${orderId}`));
967
1830
  }
968
- async getHours() {
969
- return safe7(this.client.query("business.hours"));
1831
+ async getRecent(limit = 5) {
1832
+ return safe4(this.client.query(`orders#sort(created_at,desc)#limit(${limit})`));
970
1833
  }
971
- async getLocationHours(locationId) {
972
- return safe7(this.client.query(`business.locations.${locationId}.hours`));
1834
+ async getByStatus(status) {
1835
+ return safe4(this.client.query(`orders[?(@.status=='${status}')]`));
973
1836
  }
974
- async getBootstrap() {
975
- const [businessResult, locationsResult, categoriesResult] = await Promise.all([
976
- this.getInfo(),
977
- this.getLocations(),
978
- safe7(this.client.query("categories#select(id,name,slug)"))
979
- ]);
980
- if (!businessResult.ok) return businessResult;
981
- if (!locationsResult.ok) return locationsResult;
982
- if (!categoriesResult.ok) return categoriesResult;
983
- const business = businessResult.value;
984
- const locations = locationsResult.value;
985
- const categories = categoriesResult.value;
986
- const defaultLocation = locations[0];
987
- return ok({
988
- business,
989
- location: defaultLocation,
990
- locations,
991
- categories,
992
- currency: business.default_currency,
993
- is_open: defaultLocation?.accepts_online_orders ?? false,
994
- accepts_orders: defaultLocation?.accepts_online_orders ?? false
995
- });
1837
+ async cancel(orderId, reason) {
1838
+ return safe4(
1839
+ this.client.call("order.cancelOrder", {
1840
+ order_id: orderId,
1841
+ reason
1842
+ })
1843
+ );
996
1844
  }
997
1845
  };
998
1846
 
999
- // src/inventory.ts
1000
- function toCimplifyError8(error) {
1847
+ // src/link.ts
1848
+ function toCimplifyError5(error) {
1001
1849
  if (error instanceof CimplifyError) return error;
1002
1850
  if (error instanceof Error) {
1003
1851
  return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1004
1852
  }
1005
1853
  return new CimplifyError("UNKNOWN_ERROR", String(error), false);
1006
1854
  }
1007
- async function safe8(promise) {
1855
+ async function safe5(promise) {
1008
1856
  try {
1009
1857
  return ok(await promise);
1010
1858
  } catch (error) {
1011
- return err(toCimplifyError8(error));
1859
+ return err(toCimplifyError5(error));
1012
1860
  }
1013
1861
  }
1014
- var InventoryService = class {
1862
+ var LinkService = class {
1015
1863
  constructor(client) {
1016
1864
  this.client = client;
1017
1865
  }
1018
- async getStockLevels() {
1019
- return safe8(this.client.query("inventory.stock_levels"));
1866
+ async requestOtp(input) {
1867
+ return safe5(this.client.linkPost("/v1/link/auth/request-otp", input));
1020
1868
  }
1021
- async getProductStock(productId, locationId) {
1022
- if (locationId) {
1023
- return safe8(
1024
- this.client.query("inventory.product", {
1025
- product_id: productId,
1026
- location_id: locationId
1027
- })
1028
- );
1869
+ async verifyOtp(input) {
1870
+ const result = await safe5(
1871
+ this.client.linkPost("/v1/link/auth/verify-otp", input)
1872
+ );
1873
+ if (result.ok && result.value.session_token) {
1874
+ this.client.setSessionToken(result.value.session_token);
1029
1875
  }
1030
- return safe8(
1031
- this.client.query("inventory.product", {
1032
- product_id: productId
1876
+ return result;
1877
+ }
1878
+ async logout() {
1879
+ const result = await safe5(this.client.linkPost("/v1/link/auth/logout"));
1880
+ if (result.ok) {
1881
+ this.client.clearSession();
1882
+ }
1883
+ return result;
1884
+ }
1885
+ async checkStatus(contact) {
1886
+ return safe5(
1887
+ this.client.call(LINK_MUTATION.CHECK_STATUS, {
1888
+ contact
1033
1889
  })
1034
1890
  );
1035
1891
  }
1036
- async getVariantStock(variantId, locationId) {
1037
- return safe8(
1038
- this.client.query("inventory.variant", {
1039
- variant_id: variantId,
1040
- location_id: locationId
1041
- })
1892
+ async getLinkData() {
1893
+ return safe5(this.client.query(LINK_QUERY.DATA));
1894
+ }
1895
+ async getAddresses() {
1896
+ return safe5(this.client.query(LINK_QUERY.ADDRESSES));
1897
+ }
1898
+ async getMobileMoney() {
1899
+ return safe5(this.client.query(LINK_QUERY.MOBILE_MONEY));
1900
+ }
1901
+ async getPreferences() {
1902
+ return safe5(this.client.query(LINK_QUERY.PREFERENCES));
1903
+ }
1904
+ async enroll(data) {
1905
+ return safe5(this.client.call(LINK_MUTATION.ENROLL, data));
1906
+ }
1907
+ async enrollAndLinkOrder(data) {
1908
+ return safe5(
1909
+ this.client.call(LINK_MUTATION.ENROLL_AND_LINK_ORDER, data)
1042
1910
  );
1043
1911
  }
1044
- async checkProductAvailability(productId, quantity, locationId) {
1045
- return safe8(
1046
- this.client.query("inventory.check_availability", {
1047
- product_id: productId,
1048
- quantity,
1049
- location_id: locationId
1912
+ async updatePreferences(preferences) {
1913
+ return safe5(this.client.call(LINK_MUTATION.UPDATE_PREFERENCES, preferences));
1914
+ }
1915
+ async createAddress(input) {
1916
+ return safe5(this.client.call(LINK_MUTATION.CREATE_ADDRESS, input));
1917
+ }
1918
+ async updateAddress(input) {
1919
+ return safe5(this.client.call(LINK_MUTATION.UPDATE_ADDRESS, input));
1920
+ }
1921
+ async deleteAddress(addressId) {
1922
+ return safe5(this.client.call(LINK_MUTATION.DELETE_ADDRESS, addressId));
1923
+ }
1924
+ async setDefaultAddress(addressId) {
1925
+ return safe5(this.client.call(LINK_MUTATION.SET_DEFAULT_ADDRESS, addressId));
1926
+ }
1927
+ async trackAddressUsage(addressId) {
1928
+ return safe5(
1929
+ this.client.call(LINK_MUTATION.TRACK_ADDRESS_USAGE, {
1930
+ address_id: addressId
1050
1931
  })
1051
1932
  );
1052
1933
  }
1053
- async checkVariantAvailability(variantId, quantity, locationId) {
1054
- return safe8(
1055
- this.client.query("inventory.check_availability", {
1056
- variant_id: variantId,
1057
- quantity,
1058
- location_id: locationId
1934
+ async createMobileMoney(input) {
1935
+ return safe5(this.client.call(LINK_MUTATION.CREATE_MOBILE_MONEY, input));
1936
+ }
1937
+ async deleteMobileMoney(mobileMoneyId) {
1938
+ return safe5(this.client.call(LINK_MUTATION.DELETE_MOBILE_MONEY, mobileMoneyId));
1939
+ }
1940
+ async setDefaultMobileMoney(mobileMoneyId) {
1941
+ return safe5(
1942
+ this.client.call(LINK_MUTATION.SET_DEFAULT_MOBILE_MONEY, mobileMoneyId)
1943
+ );
1944
+ }
1945
+ async trackMobileMoneyUsage(mobileMoneyId) {
1946
+ return safe5(
1947
+ this.client.call(LINK_MUTATION.TRACK_MOBILE_MONEY_USAGE, {
1948
+ mobile_money_id: mobileMoneyId
1059
1949
  })
1060
1950
  );
1061
1951
  }
1062
- async checkMultipleAvailability(items, locationId) {
1063
- const results = await Promise.all(
1064
- items.map(
1065
- (item) => item.variant_id ? this.checkVariantAvailability(item.variant_id, item.quantity, locationId) : this.checkProductAvailability(item.product_id, item.quantity, locationId)
1066
- )
1952
+ async verifyMobileMoney(mobileMoneyId) {
1953
+ return safe5(
1954
+ this.client.call(LINK_MUTATION.VERIFY_MOBILE_MONEY, mobileMoneyId)
1067
1955
  );
1068
- for (const result of results) {
1069
- if (!result.ok) return result;
1070
- }
1071
- return ok(results.map((r) => r.value));
1072
1956
  }
1073
- async getSummary() {
1074
- return safe8(this.client.query("inventory.summary"));
1957
+ async getSessions() {
1958
+ return safe5(this.client.linkGet("/v1/link/sessions"));
1075
1959
  }
1076
- async isInStock(productId, locationId) {
1077
- const result = await this.checkProductAvailability(productId, 1, locationId);
1078
- if (!result.ok) return result;
1079
- return ok(result.value.is_available);
1960
+ async revokeSession(sessionId) {
1961
+ return safe5(this.client.linkDelete(`/v1/link/sessions/${sessionId}`));
1080
1962
  }
1081
- async getAvailableQuantity(productId, locationId) {
1082
- const result = await this.getProductStock(productId, locationId);
1083
- if (!result.ok) return result;
1084
- return ok(result.value.available_quantity);
1963
+ async revokeAllSessions() {
1964
+ return safe5(this.client.linkDelete("/v1/link/sessions"));
1965
+ }
1966
+ async getAddressesRest() {
1967
+ return safe5(this.client.linkGet("/v1/link/addresses"));
1968
+ }
1969
+ async createAddressRest(input) {
1970
+ return safe5(this.client.linkPost("/v1/link/addresses", input));
1971
+ }
1972
+ async deleteAddressRest(addressId) {
1973
+ return safe5(this.client.linkDelete(`/v1/link/addresses/${addressId}`));
1974
+ }
1975
+ async setDefaultAddressRest(addressId) {
1976
+ return safe5(this.client.linkPost(`/v1/link/addresses/${addressId}/default`));
1977
+ }
1978
+ async getMobileMoneyRest() {
1979
+ return safe5(this.client.linkGet("/v1/link/mobile-money"));
1980
+ }
1981
+ async createMobileMoneyRest(input) {
1982
+ return safe5(this.client.linkPost("/v1/link/mobile-money", input));
1983
+ }
1984
+ async deleteMobileMoneyRest(mobileMoneyId) {
1985
+ return safe5(this.client.linkDelete(`/v1/link/mobile-money/${mobileMoneyId}`));
1986
+ }
1987
+ async setDefaultMobileMoneyRest(mobileMoneyId) {
1988
+ return safe5(
1989
+ this.client.linkPost(`/v1/link/mobile-money/${mobileMoneyId}/default`)
1990
+ );
1085
1991
  }
1086
1992
  };
1087
1993
 
1088
- // src/scheduling.ts
1089
- function toVariables(input) {
1090
- return Object.fromEntries(Object.entries(input));
1091
- }
1092
- function toCimplifyError9(error) {
1994
+ // src/auth.ts
1995
+ function toCimplifyError6(error) {
1093
1996
  if (error instanceof CimplifyError) return error;
1094
1997
  if (error instanceof Error) {
1095
1998
  return new CimplifyError("UNKNOWN_ERROR", error.message, false);
1096
1999
  }
1097
2000
  return new CimplifyError("UNKNOWN_ERROR", String(error), false);
1098
2001
  }
1099
- async function safe9(promise) {
2002
+ async function safe6(promise) {
1100
2003
  try {
1101
2004
  return ok(await promise);
1102
2005
  } catch (error) {
1103
- return err(toCimplifyError9(error));
2006
+ return err(toCimplifyError6(error));
1104
2007
  }
1105
2008
  }
1106
- var SchedulingService = class {
2009
+ var AuthService = class {
1107
2010
  constructor(client) {
1108
2011
  this.client = client;
1109
2012
  }
1110
- async getServices() {
1111
- return safe9(this.client.query("scheduling.services"));
2013
+ async getStatus() {
2014
+ return safe6(this.client.query("auth"));
1112
2015
  }
1113
- /**
1114
- * Get a specific service by ID
1115
- * Note: Filters from all services client-side (no single-service endpoint)
1116
- */
1117
- async getService(serviceId) {
1118
- const result = await this.getServices();
2016
+ async getCurrentUser() {
2017
+ const result = await this.getStatus();
1119
2018
  if (!result.ok) return result;
1120
- return ok(result.value.find((s) => s.id === serviceId) || null);
2019
+ return ok(result.value.customer || null);
1121
2020
  }
1122
- async getAvailableSlots(input) {
2021
+ async isAuthenticated() {
2022
+ const result = await this.getStatus();
2023
+ if (!result.ok) return result;
2024
+ return ok(result.value.is_authenticated);
2025
+ }
2026
+ async requestOtp(contact, contactType) {
2027
+ return safe6(
2028
+ this.client.call(AUTH_MUTATION.REQUEST_OTP, {
2029
+ contact,
2030
+ contact_type: contactType
2031
+ })
2032
+ );
2033
+ }
2034
+ async verifyOtp(code, contact) {
2035
+ return safe6(
2036
+ this.client.call(AUTH_MUTATION.VERIFY_OTP, {
2037
+ otp_code: code,
2038
+ contact
2039
+ })
2040
+ );
2041
+ }
2042
+ async logout() {
2043
+ return safe6(this.client.call("auth.logout"));
2044
+ }
2045
+ async updateProfile(input) {
2046
+ return safe6(this.client.call("auth.update_profile", input));
2047
+ }
2048
+ async changePassword(input) {
2049
+ return safe6(this.client.call("auth.change_password", input));
2050
+ }
2051
+ async resetPassword(email) {
2052
+ return safe6(this.client.call("auth.reset_password", { email }));
2053
+ }
2054
+ };
2055
+
2056
+ // src/business.ts
2057
+ function toCimplifyError7(error) {
2058
+ if (error instanceof CimplifyError) return error;
2059
+ if (error instanceof Error) {
2060
+ return new CimplifyError("UNKNOWN_ERROR", error.message, false);
2061
+ }
2062
+ return new CimplifyError("UNKNOWN_ERROR", String(error), false);
2063
+ }
2064
+ async function safe7(promise) {
2065
+ try {
2066
+ return ok(await promise);
2067
+ } catch (error) {
2068
+ return err(toCimplifyError7(error));
2069
+ }
2070
+ }
2071
+ var BusinessService = class {
2072
+ constructor(client) {
2073
+ this.client = client;
2074
+ }
2075
+ async getInfo() {
2076
+ return safe7(this.client.query("business.info"));
2077
+ }
2078
+ async getByHandle(handle) {
2079
+ return safe7(this.client.query(`business.handle.${handle}`));
2080
+ }
2081
+ async getByDomain(domain) {
2082
+ return safe7(this.client.query("business.domain", { domain }));
2083
+ }
2084
+ async getSettings() {
2085
+ return safe7(this.client.query("business.settings"));
2086
+ }
2087
+ async getTheme() {
2088
+ return safe7(this.client.query("business.theme"));
2089
+ }
2090
+ async getLocations() {
2091
+ return safe7(this.client.query("business.locations"));
2092
+ }
2093
+ async getLocation(locationId) {
2094
+ return safe7(this.client.query(`business.locations.${locationId}`));
2095
+ }
2096
+ async getHours() {
2097
+ return safe7(this.client.query("business.hours"));
2098
+ }
2099
+ async getLocationHours(locationId) {
2100
+ return safe7(this.client.query(`business.locations.${locationId}.hours`));
2101
+ }
2102
+ async getBootstrap() {
2103
+ const [businessResult, locationsResult, categoriesResult] = await Promise.all([
2104
+ this.getInfo(),
2105
+ this.getLocations(),
2106
+ safe7(this.client.query("categories#select(id,name,slug)"))
2107
+ ]);
2108
+ if (!businessResult.ok) return businessResult;
2109
+ if (!locationsResult.ok) return locationsResult;
2110
+ if (!categoriesResult.ok) return categoriesResult;
2111
+ const business = businessResult.value;
2112
+ const locations = locationsResult.value;
2113
+ const categories = categoriesResult.value;
2114
+ const defaultLocation = locations[0];
2115
+ return ok({
2116
+ business,
2117
+ location: defaultLocation,
2118
+ locations,
2119
+ categories,
2120
+ currency: business.default_currency,
2121
+ is_open: defaultLocation?.accepts_online_orders ?? false,
2122
+ accepts_orders: defaultLocation?.accepts_online_orders ?? false
2123
+ });
2124
+ }
2125
+ };
2126
+
2127
+ // src/inventory.ts
2128
+ function toCimplifyError8(error) {
2129
+ if (error instanceof CimplifyError) return error;
2130
+ if (error instanceof Error) {
2131
+ return new CimplifyError("UNKNOWN_ERROR", error.message, false);
2132
+ }
2133
+ return new CimplifyError("UNKNOWN_ERROR", String(error), false);
2134
+ }
2135
+ async function safe8(promise) {
2136
+ try {
2137
+ return ok(await promise);
2138
+ } catch (error) {
2139
+ return err(toCimplifyError8(error));
2140
+ }
2141
+ }
2142
+ var InventoryService = class {
2143
+ constructor(client) {
2144
+ this.client = client;
2145
+ }
2146
+ async getStockLevels() {
2147
+ return safe8(this.client.query("inventory.stock_levels"));
2148
+ }
2149
+ async getProductStock(productId, locationId) {
2150
+ if (locationId) {
2151
+ return safe8(
2152
+ this.client.query("inventory.product", {
2153
+ product_id: productId,
2154
+ location_id: locationId
2155
+ })
2156
+ );
2157
+ }
2158
+ return safe8(
2159
+ this.client.query("inventory.product", {
2160
+ product_id: productId
2161
+ })
2162
+ );
2163
+ }
2164
+ async getVariantStock(variantId, locationId) {
2165
+ return safe8(
2166
+ this.client.query("inventory.variant", {
2167
+ variant_id: variantId,
2168
+ location_id: locationId
2169
+ })
2170
+ );
2171
+ }
2172
+ async checkProductAvailability(productId, quantity, locationId) {
2173
+ return safe8(
2174
+ this.client.query("inventory.check_availability", {
2175
+ product_id: productId,
2176
+ quantity,
2177
+ location_id: locationId
2178
+ })
2179
+ );
2180
+ }
2181
+ async checkVariantAvailability(variantId, quantity, locationId) {
2182
+ return safe8(
2183
+ this.client.query("inventory.check_availability", {
2184
+ variant_id: variantId,
2185
+ quantity,
2186
+ location_id: locationId
2187
+ })
2188
+ );
2189
+ }
2190
+ async checkMultipleAvailability(items, locationId) {
2191
+ const results = await Promise.all(
2192
+ items.map(
2193
+ (item) => item.variant_id ? this.checkVariantAvailability(item.variant_id, item.quantity, locationId) : this.checkProductAvailability(item.product_id, item.quantity, locationId)
2194
+ )
2195
+ );
2196
+ for (const result of results) {
2197
+ if (!result.ok) return result;
2198
+ }
2199
+ return ok(results.map((r) => r.value));
2200
+ }
2201
+ async getSummary() {
2202
+ return safe8(this.client.query("inventory.summary"));
2203
+ }
2204
+ async isInStock(productId, locationId) {
2205
+ const result = await this.checkProductAvailability(productId, 1, locationId);
2206
+ if (!result.ok) return result;
2207
+ return ok(result.value.is_available);
2208
+ }
2209
+ async getAvailableQuantity(productId, locationId) {
2210
+ const result = await this.getProductStock(productId, locationId);
2211
+ if (!result.ok) return result;
2212
+ return ok(result.value.available_quantity);
2213
+ }
2214
+ };
2215
+
2216
+ // src/scheduling.ts
2217
+ function toVariables(input) {
2218
+ return Object.fromEntries(Object.entries(input));
2219
+ }
2220
+ function toCimplifyError9(error) {
2221
+ if (error instanceof CimplifyError) return error;
2222
+ if (error instanceof Error) {
2223
+ return new CimplifyError("UNKNOWN_ERROR", error.message, false);
2224
+ }
2225
+ return new CimplifyError("UNKNOWN_ERROR", String(error), false);
2226
+ }
2227
+ async function safe9(promise) {
2228
+ try {
2229
+ return ok(await promise);
2230
+ } catch (error) {
2231
+ return err(toCimplifyError9(error));
2232
+ }
2233
+ }
2234
+ var SchedulingService = class {
2235
+ constructor(client) {
2236
+ this.client = client;
2237
+ }
2238
+ async getServices() {
2239
+ return safe9(this.client.query("scheduling.services"));
2240
+ }
2241
+ /**
2242
+ * Get a specific service by ID
2243
+ * Note: Filters from all services client-side (no single-service endpoint)
2244
+ */
2245
+ async getService(serviceId) {
2246
+ const result = await this.getServices();
2247
+ if (!result.ok) return result;
2248
+ return ok(result.value.find((s) => s.id === serviceId) || null);
2249
+ }
2250
+ async getAvailableSlots(input) {
1123
2251
  return safe9(
1124
2252
  this.client.query("scheduling.slots", toVariables(input))
1125
2253
  );
@@ -1289,6 +2417,8 @@ var MESSAGE_TYPES = {
1289
2417
  GET_DATA: "get_data",
1290
2418
  REFRESH_TOKEN: "refresh_token",
1291
2419
  LOGOUT: "logout",
2420
+ PROCESS_CHECKOUT: "process_checkout",
2421
+ ABORT_CHECKOUT: "abort_checkout",
1292
2422
  // Iframe → Parent
1293
2423
  READY: "ready",
1294
2424
  HEIGHT_CHANGE: "height_change",
@@ -1299,7 +2429,9 @@ var MESSAGE_TYPES = {
1299
2429
  ADDRESS_SELECTED: "address_selected",
1300
2430
  PAYMENT_METHOD_SELECTED: "payment_method_selected",
1301
2431
  TOKEN_REFRESHED: "token_refreshed",
1302
- LOGOUT_COMPLETE: "logout_complete"
2432
+ LOGOUT_COMPLETE: "logout_complete",
2433
+ CHECKOUT_STATUS: "checkout_status",
2434
+ CHECKOUT_COMPLETE: "checkout_complete"
1303
2435
  };
1304
2436
  var EVENT_TYPES = {
1305
2437
  READY: "ready",
@@ -1312,38 +2444,14 @@ var EVENT_TYPES = {
1312
2444
  };
1313
2445
 
1314
2446
  // src/elements.ts
1315
- function mapOrderType(orderType) {
1316
- if (orderType === "dine_in") return "dine-in";
1317
- return orderType ?? "delivery";
1318
- }
1319
- function toCheckoutFormData(data) {
2447
+ function toCheckoutError(code, message, recoverable) {
1320
2448
  return {
1321
- cart_id: data.cart_id,
1322
- customer: {
1323
- name: "",
1324
- email: "",
1325
- phone: "",
1326
- save_details: false
1327
- },
1328
- order_type: mapOrderType(data.order_type),
1329
- address_info: data.address ? {
1330
- street_address: data.address.street_address,
1331
- apartment: data.address.apartment,
1332
- city: data.address.city,
1333
- region: data.address.region,
1334
- postal_code: data.address.postal_code,
1335
- country: data.address.country,
1336
- delivery_instructions: data.address.delivery_instructions,
1337
- phone_for_delivery: data.address.phone_for_delivery
1338
- } : {},
1339
- payment_method: data.payment_method?.type ?? "mobile_money",
1340
- mobile_money_details: data.payment_method?.type === "mobile_money" && data.payment_method.phone_number ? {
1341
- phone_number: data.payment_method.phone_number,
1342
- provider: data.payment_method.provider ?? "mtn"
1343
- } : void 0,
1344
- special_instructions: data.notes,
1345
- link_address_id: data.address?.id,
1346
- link_payment_method_id: data.payment_method?.id
2449
+ success: false,
2450
+ error: {
2451
+ code,
2452
+ message,
2453
+ recoverable
2454
+ }
1347
2455
  };
1348
2456
  }
1349
2457
  var DEFAULT_LINK_URL = "https://link.cimplify.io";
@@ -1362,14 +2470,27 @@ function isAllowedOrigin(origin) {
1362
2470
  return false;
1363
2471
  }
1364
2472
  }
2473
+ function parseIframeMessage(data) {
2474
+ if (!data || typeof data !== "object") {
2475
+ return null;
2476
+ }
2477
+ const maybeType = data.type;
2478
+ if (typeof maybeType !== "string") {
2479
+ return null;
2480
+ }
2481
+ return data;
2482
+ }
1365
2483
  var CimplifyElements = class {
1366
2484
  constructor(client, businessId, options = {}) {
1367
2485
  this.elements = /* @__PURE__ */ new Map();
1368
2486
  this.accessToken = null;
1369
2487
  this.accountId = null;
1370
2488
  this.customerId = null;
2489
+ this.customerData = null;
1371
2490
  this.addressData = null;
1372
2491
  this.paymentData = null;
2492
+ this.checkoutInProgress = false;
2493
+ this.activeCheckoutAbort = null;
1373
2494
  this.client = client;
1374
2495
  this.businessId = businessId;
1375
2496
  this.linkUrl = options.linkUrl || DEFAULT_LINK_URL;
@@ -1397,54 +2518,242 @@ var CimplifyElements = class {
1397
2518
  }
1398
2519
  }
1399
2520
  async submitCheckout(data) {
1400
- const addressElement = this.elements.get(ELEMENT_TYPES.ADDRESS);
1401
- if (addressElement) await addressElement.getData();
1402
- const paymentElement = this.elements.get(ELEMENT_TYPES.PAYMENT);
1403
- if (paymentElement) await paymentElement.getData();
1404
- const internalData = {
1405
- ...data,
1406
- customer: this.accountId ? { account_id: this.accountId, customer_id: this.customerId } : void 0,
1407
- address: this.addressData,
1408
- payment_method: this.paymentData
1409
- };
1410
- const checkoutFormData = toCheckoutFormData(internalData);
1411
- const result = await this.client.checkout.process(checkoutFormData);
1412
- if (result.ok) {
2521
+ const result = await this.processCheckout({
2522
+ cart_id: data.cart_id,
2523
+ order_type: data.order_type ?? "delivery",
2524
+ location_id: data.location_id,
2525
+ notes: data.notes,
2526
+ scheduled_time: data.scheduled_time,
2527
+ tip_amount: data.tip_amount,
2528
+ enroll_in_link: true
2529
+ });
2530
+ if (result.success) {
1413
2531
  return {
1414
2532
  success: true,
1415
2533
  order: {
1416
- id: result.value.order_id,
1417
- status: result.value.payment_status,
1418
- total: ""
1419
- // Total is not returned by checkout result
2534
+ id: result.order?.id || "",
2535
+ status: result.order?.status || "unknown",
2536
+ total: result.order?.total || ""
1420
2537
  }
1421
2538
  };
1422
2539
  }
1423
2540
  return {
1424
2541
  success: false,
1425
2542
  error: {
1426
- code: result.error.code || "CHECKOUT_FAILED",
1427
- message: result.error.message || "Checkout failed"
2543
+ code: result.error?.code || "CHECKOUT_FAILED",
2544
+ message: result.error?.message || "Checkout failed"
1428
2545
  }
1429
2546
  };
1430
2547
  }
1431
- isAuthenticated() {
1432
- return this.accessToken !== null;
1433
- }
1434
- getAccessToken() {
1435
- return this.accessToken;
1436
- }
1437
- handleMessage(event) {
1438
- if (!isAllowedOrigin(event.origin)) {
1439
- return;
1440
- }
1441
- const message = event.data;
1442
- if (!message?.type) return;
1443
- switch (message.type) {
1444
- case MESSAGE_TYPES.AUTHENTICATED:
1445
- this.accessToken = message.token;
1446
- this.accountId = message.accountId;
1447
- this.customerId = message.customerId;
2548
+ processCheckout(options) {
2549
+ let abortFn = null;
2550
+ const task = (async () => {
2551
+ if (this.checkoutInProgress) {
2552
+ return toCheckoutError(
2553
+ "ALREADY_PROCESSING",
2554
+ "Checkout is already in progress.",
2555
+ false
2556
+ );
2557
+ }
2558
+ if (!options.cart_id) {
2559
+ return toCheckoutError(
2560
+ "INVALID_CART",
2561
+ "A valid cart is required before checkout can start.",
2562
+ false
2563
+ );
2564
+ }
2565
+ if (!options.order_type) {
2566
+ return toCheckoutError(
2567
+ "ORDER_TYPE_REQUIRED",
2568
+ "Order type is required before checkout can start.",
2569
+ false
2570
+ );
2571
+ }
2572
+ const paymentElement = this.elements.get(ELEMENT_TYPES.PAYMENT);
2573
+ if (!paymentElement) {
2574
+ return toCheckoutError(
2575
+ "NO_PAYMENT_ELEMENT",
2576
+ "Payment element must be mounted before checkout.",
2577
+ false
2578
+ );
2579
+ }
2580
+ if (!paymentElement.isMounted()) {
2581
+ return toCheckoutError(
2582
+ "PAYMENT_NOT_MOUNTED",
2583
+ "Payment element must be mounted before checkout.",
2584
+ false
2585
+ );
2586
+ }
2587
+ const authElement = this.elements.get(ELEMENT_TYPES.AUTH);
2588
+ if (authElement && !this.accessToken) {
2589
+ return toCheckoutError(
2590
+ "AUTH_INCOMPLETE",
2591
+ "Authentication must complete before checkout can start.",
2592
+ true
2593
+ );
2594
+ }
2595
+ const addressElement = this.elements.get(ELEMENT_TYPES.ADDRESS);
2596
+ if (addressElement) {
2597
+ await addressElement.getData();
2598
+ }
2599
+ await this.hydrateCustomerData();
2600
+ const processMessage = {
2601
+ type: MESSAGE_TYPES.PROCESS_CHECKOUT,
2602
+ cart_id: options.cart_id,
2603
+ order_type: options.order_type,
2604
+ location_id: options.location_id,
2605
+ notes: options.notes,
2606
+ scheduled_time: options.scheduled_time,
2607
+ tip_amount: options.tip_amount,
2608
+ pay_currency: options.pay_currency,
2609
+ enroll_in_link: options.enroll_in_link ?? true,
2610
+ address: this.addressData ?? void 0,
2611
+ customer: this.customerData ? {
2612
+ name: this.customerData.name,
2613
+ email: this.customerData.email,
2614
+ phone: this.customerData.phone
2615
+ } : null,
2616
+ account_id: this.accountId ?? void 0,
2617
+ customer_id: this.customerId ?? void 0
2618
+ };
2619
+ const timeoutMs = options.timeout_ms ?? 18e4;
2620
+ const paymentWindow = paymentElement.getContentWindow();
2621
+ this.checkoutInProgress = true;
2622
+ return new Promise((resolve) => {
2623
+ let settled = false;
2624
+ const cleanup = () => {
2625
+ if (typeof window !== "undefined") {
2626
+ window.removeEventListener("message", handleCheckoutMessage);
2627
+ }
2628
+ clearTimeout(timeout);
2629
+ this.checkoutInProgress = false;
2630
+ this.activeCheckoutAbort = null;
2631
+ };
2632
+ const settle = (result) => {
2633
+ if (settled) {
2634
+ return;
2635
+ }
2636
+ settled = true;
2637
+ cleanup();
2638
+ resolve(result);
2639
+ };
2640
+ const timeout = setTimeout(() => {
2641
+ settle(
2642
+ toCheckoutError(
2643
+ "TIMEOUT",
2644
+ "Checkout timed out before receiving a terminal response.",
2645
+ true
2646
+ )
2647
+ );
2648
+ }, timeoutMs);
2649
+ const handleCheckoutMessage = (event) => {
2650
+ if (!isAllowedOrigin(event.origin)) {
2651
+ return;
2652
+ }
2653
+ const message = parseIframeMessage(event.data);
2654
+ if (!message) {
2655
+ return;
2656
+ }
2657
+ if (message.type === MESSAGE_TYPES.LOGOUT_COMPLETE) {
2658
+ paymentElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
2659
+ settle(
2660
+ toCheckoutError(
2661
+ "AUTH_LOST",
2662
+ "Authentication was cleared during checkout.",
2663
+ true
2664
+ )
2665
+ );
2666
+ return;
2667
+ }
2668
+ if (paymentWindow && event.source && event.source !== paymentWindow) {
2669
+ return;
2670
+ }
2671
+ if (message.type === MESSAGE_TYPES.CHECKOUT_STATUS) {
2672
+ options.on_status_change?.(message.status, message.context);
2673
+ return;
2674
+ }
2675
+ if (message.type === MESSAGE_TYPES.CHECKOUT_COMPLETE) {
2676
+ settle({
2677
+ success: message.success,
2678
+ order: message.order,
2679
+ error: message.error,
2680
+ enrolled_in_link: message.enrolled_in_link
2681
+ });
2682
+ }
2683
+ };
2684
+ if (typeof window !== "undefined") {
2685
+ window.addEventListener("message", handleCheckoutMessage);
2686
+ }
2687
+ abortFn = () => {
2688
+ paymentElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
2689
+ settle(
2690
+ toCheckoutError(
2691
+ "CANCELLED",
2692
+ "Checkout was cancelled.",
2693
+ true
2694
+ )
2695
+ );
2696
+ };
2697
+ this.activeCheckoutAbort = abortFn;
2698
+ paymentElement.sendMessage(processMessage);
2699
+ });
2700
+ })();
2701
+ const abortable = task;
2702
+ abortable.abort = () => {
2703
+ if (abortFn) {
2704
+ abortFn();
2705
+ }
2706
+ };
2707
+ return abortable;
2708
+ }
2709
+ isAuthenticated() {
2710
+ return this.accessToken !== null;
2711
+ }
2712
+ getAccessToken() {
2713
+ return this.accessToken;
2714
+ }
2715
+ getPublicKey() {
2716
+ return this.client.getPublicKey();
2717
+ }
2718
+ getAppearance() {
2719
+ return this.options.appearance;
2720
+ }
2721
+ async hydrateCustomerData() {
2722
+ if (!this.accessToken || this.customerData) {
2723
+ return;
2724
+ }
2725
+ if (!this.client.getAccessToken()) {
2726
+ this.client.setAccessToken(this.accessToken);
2727
+ }
2728
+ const linkDataResult = await this.client.link.getLinkData();
2729
+ if (!linkDataResult.ok || !linkDataResult.value?.customer) {
2730
+ return;
2731
+ }
2732
+ const customer = linkDataResult.value.customer;
2733
+ this.customerData = {
2734
+ name: customer.name || "",
2735
+ email: customer.email || null,
2736
+ phone: customer.phone || null
2737
+ };
2738
+ }
2739
+ handleMessage(event) {
2740
+ if (!isAllowedOrigin(event.origin)) {
2741
+ return;
2742
+ }
2743
+ const message = parseIframeMessage(event.data);
2744
+ if (!message) return;
2745
+ switch (message.type) {
2746
+ case MESSAGE_TYPES.AUTHENTICATED:
2747
+ const customer = message.customer ?? {
2748
+ name: "",
2749
+ email: null,
2750
+ phone: null
2751
+ };
2752
+ this.accessToken = message.token;
2753
+ this.accountId = message.accountId;
2754
+ this.customerId = message.customerId;
2755
+ this.customerData = customer;
2756
+ this.client.setAccessToken(message.token);
1448
2757
  this.elements.forEach((element, type) => {
1449
2758
  if (type !== ELEMENT_TYPES.AUTH) {
1450
2759
  element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token: message.token });
@@ -1462,11 +2771,16 @@ var CimplifyElements = class {
1462
2771
  this.paymentData = message.method;
1463
2772
  break;
1464
2773
  case MESSAGE_TYPES.LOGOUT_COMPLETE:
2774
+ if (this.checkoutInProgress && this.activeCheckoutAbort) {
2775
+ this.activeCheckoutAbort();
2776
+ }
1465
2777
  this.accessToken = null;
1466
2778
  this.accountId = null;
1467
2779
  this.customerId = null;
2780
+ this.customerData = null;
1468
2781
  this.addressData = null;
1469
2782
  this.paymentData = null;
2783
+ this.client.clearSession();
1470
2784
  break;
1471
2785
  }
1472
2786
  }
@@ -1516,6 +2830,8 @@ var CimplifyElement = class {
1516
2830
  this.container = null;
1517
2831
  this.mounted = false;
1518
2832
  this.eventHandlers.clear();
2833
+ this.resolvers.forEach((entry) => clearTimeout(entry.timeoutId));
2834
+ this.resolvers.clear();
1519
2835
  if (typeof window !== "undefined") {
1520
2836
  window.removeEventListener("message", this.boundHandleMessage);
1521
2837
  }
@@ -1530,16 +2846,21 @@ var CimplifyElement = class {
1530
2846
  this.eventHandlers.get(event)?.delete(handler);
1531
2847
  }
1532
2848
  async getData() {
2849
+ if (!this.isMounted()) {
2850
+ return null;
2851
+ }
1533
2852
  return new Promise((resolve) => {
1534
2853
  const id = Math.random().toString(36).slice(2);
1535
- this.resolvers.set(id, resolve);
1536
- this.sendMessage({ type: MESSAGE_TYPES.GET_DATA });
1537
- setTimeout(() => {
1538
- if (this.resolvers.has(id)) {
1539
- this.resolvers.delete(id);
1540
- resolve(null);
2854
+ const timeoutId = setTimeout(() => {
2855
+ const entry = this.resolvers.get(id);
2856
+ if (!entry) {
2857
+ return;
1541
2858
  }
2859
+ this.resolvers.delete(id);
2860
+ entry.resolve(null);
1542
2861
  }, 5e3);
2862
+ this.resolvers.set(id, { resolve, timeoutId });
2863
+ this.sendMessage({ type: MESSAGE_TYPES.GET_DATA });
1543
2864
  });
1544
2865
  }
1545
2866
  sendMessage(message) {
@@ -1547,6 +2868,12 @@ var CimplifyElement = class {
1547
2868
  this.iframe.contentWindow.postMessage(message, this.linkUrl);
1548
2869
  }
1549
2870
  }
2871
+ getContentWindow() {
2872
+ return this.iframe?.contentWindow ?? null;
2873
+ }
2874
+ isMounted() {
2875
+ return this.mounted && Boolean(this.iframe) && this.iframe?.isConnected === true;
2876
+ }
1550
2877
  createIframe() {
1551
2878
  if (!this.container) return;
1552
2879
  const iframe = document.createElement("iframe");
@@ -1561,14 +2888,21 @@ var CimplifyElement = class {
1561
2888
  iframe.style.overflow = "hidden";
1562
2889
  iframe.setAttribute("allowtransparency", "true");
1563
2890
  iframe.setAttribute("frameborder", "0");
1564
- iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-popups");
2891
+ iframe.setAttribute(
2892
+ "sandbox",
2893
+ "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
2894
+ );
1565
2895
  this.iframe = iframe;
1566
2896
  this.container.appendChild(iframe);
1567
2897
  iframe.onload = () => {
2898
+ const publicKey = this.parent.getPublicKey();
1568
2899
  this.sendMessage({
1569
2900
  type: MESSAGE_TYPES.INIT,
1570
2901
  businessId: this.businessId,
1571
- prefillEmail: this.options.prefillEmail
2902
+ publicKey,
2903
+ demoMode: publicKey.length === 0,
2904
+ prefillEmail: this.options.prefillEmail,
2905
+ appearance: this.parent.getAppearance()
1572
2906
  });
1573
2907
  const token = this.parent.getAccessToken();
1574
2908
  if (token && this.type !== ELEMENT_TYPES.AUTH) {
@@ -1580,8 +2914,8 @@ var CimplifyElement = class {
1580
2914
  if (!isAllowedOrigin(event.origin)) {
1581
2915
  return;
1582
2916
  }
1583
- const message = event.data;
1584
- if (!message?.type) return;
2917
+ const message = parseIframeMessage(event.data);
2918
+ if (!message) return;
1585
2919
  switch (message.type) {
1586
2920
  case MESSAGE_TYPES.READY:
1587
2921
  this.emit(EVENT_TYPES.READY, { height: message.height });
@@ -1590,10 +2924,16 @@ var CimplifyElement = class {
1590
2924
  if (this.iframe) this.iframe.style.height = `${message.height}px`;
1591
2925
  break;
1592
2926
  case MESSAGE_TYPES.AUTHENTICATED:
2927
+ const customer = message.customer ?? {
2928
+ name: "",
2929
+ email: null,
2930
+ phone: null
2931
+ };
1593
2932
  this.emit(EVENT_TYPES.AUTHENTICATED, {
1594
2933
  accountId: message.accountId,
1595
2934
  customerId: message.customerId,
1596
- token: message.token
2935
+ token: message.token,
2936
+ customer
1597
2937
  });
1598
2938
  break;
1599
2939
  case MESSAGE_TYPES.REQUIRES_OTP:
@@ -1619,7 +2959,10 @@ var CimplifyElement = class {
1619
2959
  this.eventHandlers.get(event)?.forEach((handler) => handler(data));
1620
2960
  }
1621
2961
  resolveData(data) {
1622
- this.resolvers.forEach((resolve) => resolve(data));
2962
+ this.resolvers.forEach((entry) => {
2963
+ clearTimeout(entry.timeoutId);
2964
+ entry.resolve(data);
2965
+ });
1623
2966
  this.resolvers.clear();
1624
2967
  }
1625
2968
  };
@@ -1728,6 +3071,9 @@ var CimplifyClient = class {
1728
3071
  getAccessToken() {
1729
3072
  return this.accessToken;
1730
3073
  }
3074
+ getPublicKey() {
3075
+ return this.publicKey;
3076
+ }
1731
3077
  setAccessToken(token) {
1732
3078
  const previous = this.accessToken;
1733
3079
  this.accessToken = token;
@@ -2034,463 +3380,119 @@ var CimplifyClient = class {
2034
3380
  }
2035
3381
  get business() {
2036
3382
  if (!this._business) {
2037
- this._business = new BusinessService(this);
2038
- }
2039
- return this._business;
2040
- }
2041
- get inventory() {
2042
- if (!this._inventory) {
2043
- this._inventory = new InventoryService(this);
2044
- }
2045
- return this._inventory;
2046
- }
2047
- get scheduling() {
2048
- if (!this._scheduling) {
2049
- this._scheduling = new SchedulingService(this);
2050
- }
2051
- return this._scheduling;
2052
- }
2053
- get lite() {
2054
- if (!this._lite) {
2055
- this._lite = new LiteService(this);
2056
- }
2057
- return this._lite;
2058
- }
2059
- get fx() {
2060
- if (!this._fx) {
2061
- this._fx = new FxService(this);
2062
- }
2063
- return this._fx;
2064
- }
2065
- /**
2066
- * Create a CimplifyElements instance for embedding checkout components.
2067
- * Like Stripe's stripe.elements().
2068
- *
2069
- * @param businessId - The business ID for checkout context
2070
- * @param options - Optional configuration for elements
2071
- *
2072
- * @example
2073
- * ```ts
2074
- * const elements = client.elements('bus_xxx');
2075
- * const authElement = elements.create('auth');
2076
- * authElement.mount('#auth-container');
2077
- * ```
2078
- */
2079
- elements(businessId, options) {
2080
- return createElements(this, businessId, options);
2081
- }
2082
- };
2083
- function createCimplifyClient(config = {}) {
2084
- return new CimplifyClient(config);
2085
- }
2086
-
2087
- // src/query/builder.ts
2088
- var QueryBuilder = class {
2089
- constructor(entity) {
2090
- this.filters = [];
2091
- this.modifiers = [];
2092
- this.pathSegments = [];
2093
- this.entity = entity;
2094
- }
2095
- path(segment) {
2096
- this.pathSegments.push(segment);
2097
- return this;
2098
- }
2099
- where(field, op, value) {
2100
- const v = typeof value === "string" ? `'${value}'` : value;
2101
- if (op === "contains" || op === "startsWith") {
2102
- this.filters.push(`@.${field} ${op} ${v}`);
2103
- } else {
2104
- this.filters.push(`@.${field}${op}${v}`);
2105
- }
2106
- return this;
2107
- }
2108
- and(field, op, value) {
2109
- return this.where(field, op, value);
2110
- }
2111
- sort(field, order = "asc") {
2112
- this.modifiers.push(`sort(${field},${order})`);
2113
- return this;
2114
- }
2115
- limit(n) {
2116
- this.modifiers.push(`limit(${n})`);
2117
- return this;
2118
- }
2119
- offset(n) {
2120
- this.modifiers.push(`offset(${n})`);
2121
- return this;
2122
- }
2123
- count() {
2124
- this.modifiers.push("count");
2125
- return this;
2126
- }
2127
- enriched() {
2128
- this.modifiers.push("enriched");
2129
- return this;
2130
- }
2131
- build() {
2132
- let query2 = this.entity;
2133
- if (this.pathSegments.length > 0) {
2134
- query2 += "." + this.pathSegments.join(".");
2135
- }
2136
- if (this.filters.length > 0) {
2137
- query2 += `[?(${this.filters.join(" && ")})]`;
2138
- }
2139
- for (const mod of this.modifiers) {
2140
- query2 += `#${mod}`;
2141
- }
2142
- return query2;
2143
- }
2144
- toString() {
2145
- return this.build();
2146
- }
2147
- };
2148
- function query(entity) {
2149
- return new QueryBuilder(entity);
2150
- }
2151
-
2152
- // src/utils/price.ts
2153
- var CURRENCY_SYMBOLS = {
2154
- // Major world currencies
2155
- USD: "$",
2156
- EUR: "\u20AC",
2157
- GBP: "\xA3",
2158
- JPY: "\xA5",
2159
- CNY: "\xA5",
2160
- CHF: "CHF",
2161
- CAD: "C$",
2162
- AUD: "A$",
2163
- NZD: "NZ$",
2164
- HKD: "HK$",
2165
- SGD: "S$",
2166
- INR: "\u20B9",
2167
- BRL: "R$",
2168
- MXN: "MX$",
2169
- KRW: "\u20A9",
2170
- RUB: "\u20BD",
2171
- TRY: "\u20BA",
2172
- THB: "\u0E3F",
2173
- PLN: "z\u0142",
2174
- SEK: "kr",
2175
- NOK: "kr",
2176
- DKK: "kr",
2177
- CZK: "K\u010D",
2178
- HUF: "Ft",
2179
- ILS: "\u20AA",
2180
- AED: "\u062F.\u0625",
2181
- SAR: "\uFDFC",
2182
- MYR: "RM",
2183
- PHP: "\u20B1",
2184
- IDR: "Rp",
2185
- VND: "\u20AB",
2186
- TWD: "NT$",
2187
- // African currencies
2188
- GHS: "GH\u20B5",
2189
- NGN: "\u20A6",
2190
- KES: "KSh",
2191
- ZAR: "R",
2192
- XOF: "CFA",
2193
- XAF: "FCFA",
2194
- EGP: "E\xA3",
2195
- MAD: "MAD",
2196
- TZS: "TSh",
2197
- UGX: "USh",
2198
- RWF: "FRw",
2199
- ETB: "Br",
2200
- ZMW: "ZK",
2201
- BWP: "P",
2202
- MUR: "\u20A8",
2203
- SCR: "\u20A8",
2204
- NAD: "N$",
2205
- SZL: "E",
2206
- LSL: "L",
2207
- MWK: "MK",
2208
- AOA: "Kz",
2209
- CDF: "FC",
2210
- GMD: "D",
2211
- GNF: "FG",
2212
- LRD: "L$",
2213
- SLL: "Le",
2214
- MZN: "MT",
2215
- SDG: "SDG",
2216
- SSP: "SSP",
2217
- SOS: "Sh.So.",
2218
- DJF: "Fdj",
2219
- ERN: "Nfk",
2220
- CVE: "$",
2221
- STN: "Db",
2222
- KMF: "CF",
2223
- BIF: "FBu"
2224
- };
2225
- function getCurrencySymbol(currencyCode) {
2226
- return CURRENCY_SYMBOLS[currencyCode.toUpperCase()] || currencyCode;
2227
- }
2228
- function formatNumberCompact(value, decimals = 1) {
2229
- const absValue = Math.abs(value);
2230
- const sign = value < 0 ? "-" : "";
2231
- if (absValue >= 1e9) {
2232
- return `${sign}${(absValue / 1e9).toFixed(decimals)}B`;
2233
- }
2234
- if (absValue >= 1e6) {
2235
- return `${sign}${(absValue / 1e6).toFixed(decimals)}M`;
2236
- }
2237
- if (absValue >= 1e3) {
2238
- return `${sign}${(absValue / 1e3).toFixed(decimals)}K`;
2239
- }
2240
- return `${sign}${absValue.toFixed(decimals)}`;
2241
- }
2242
- function formatPrice(amount, currency = "GHS", locale = "en-US") {
2243
- const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
2244
- if (isNaN(numAmount)) {
2245
- return `${getCurrencySymbol(currency)}0.00`;
2246
- }
2247
- try {
2248
- return new Intl.NumberFormat(locale, {
2249
- style: "currency",
2250
- currency: currency.toUpperCase(),
2251
- minimumFractionDigits: 2,
2252
- maximumFractionDigits: 2
2253
- }).format(numAmount);
2254
- } catch {
2255
- return `${getCurrencySymbol(currency)}${numAmount.toFixed(2)}`;
2256
- }
2257
- }
2258
- function formatPriceAdjustment(amount, currency = "GHS", locale = "en-US") {
2259
- const formatted = formatPrice(Math.abs(amount), currency, locale);
2260
- if (amount > 0) {
2261
- return `+${formatted}`;
2262
- } else if (amount < 0) {
2263
- return `-${formatted}`;
2264
- }
2265
- return formatted;
2266
- }
2267
- function formatPriceCompact(amount, currency = "GHS", decimals = 1) {
2268
- const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
2269
- if (isNaN(numAmount)) {
2270
- return `${getCurrencySymbol(currency)}0`;
2271
- }
2272
- const symbol = getCurrencySymbol(currency);
2273
- if (Math.abs(numAmount) < 1e3) {
2274
- return `${symbol}${numAmount.toFixed(2)}`;
2275
- }
2276
- return `${symbol}${formatNumberCompact(numAmount, decimals)}`;
2277
- }
2278
- function formatMoney(amount, currency = "GHS") {
2279
- const symbol = getCurrencySymbol(currency);
2280
- const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
2281
- if (isNaN(numAmount)) {
2282
- return `${symbol}0.00`;
2283
- }
2284
- return `${symbol}${numAmount.toFixed(2)}`;
2285
- }
2286
- function parsePrice(value) {
2287
- if (value === void 0 || value === null) {
2288
- return 0;
2289
- }
2290
- if (typeof value === "number") {
2291
- return isNaN(value) ? 0 : value;
2292
- }
2293
- const cleaned = value.replace(/[^\d.-]/g, "");
2294
- const parsed = parseFloat(cleaned);
2295
- return isNaN(parsed) ? 0 : parsed;
2296
- }
2297
- function getDisplayPrice(product) {
2298
- if (product.price_info) {
2299
- return parsePrice(product.price_info.final_price);
2300
- }
2301
- if (product.final_price !== void 0 && product.final_price !== null) {
2302
- return parsePrice(product.final_price);
3383
+ this._business = new BusinessService(this);
3384
+ }
3385
+ return this._business;
2303
3386
  }
2304
- if (product.default_price !== void 0 && product.default_price !== null) {
2305
- return parsePrice(product.default_price);
3387
+ get inventory() {
3388
+ if (!this._inventory) {
3389
+ this._inventory = new InventoryService(this);
3390
+ }
3391
+ return this._inventory;
2306
3392
  }
2307
- return 0;
2308
- }
2309
- function getBasePrice(product) {
2310
- if (product.price_info) {
2311
- return parsePrice(product.price_info.base_price);
3393
+ get scheduling() {
3394
+ if (!this._scheduling) {
3395
+ this._scheduling = new SchedulingService(this);
3396
+ }
3397
+ return this._scheduling;
2312
3398
  }
2313
- if (product.base_price !== void 0 && product.base_price !== null) {
2314
- return parsePrice(product.base_price);
3399
+ get lite() {
3400
+ if (!this._lite) {
3401
+ this._lite = new LiteService(this);
3402
+ }
3403
+ return this._lite;
2315
3404
  }
2316
- if (product.default_price !== void 0 && product.default_price !== null) {
2317
- return parsePrice(product.default_price);
3405
+ get fx() {
3406
+ if (!this._fx) {
3407
+ this._fx = new FxService(this);
3408
+ }
3409
+ return this._fx;
2318
3410
  }
2319
- return 0;
2320
- }
2321
- function isOnSale(product) {
2322
- const basePrice = getBasePrice(product);
2323
- const finalPrice = getDisplayPrice(product);
2324
- return basePrice > finalPrice && basePrice > 0;
2325
- }
2326
- function getDiscountPercentage(product) {
2327
- const basePrice = getBasePrice(product);
2328
- const finalPrice = getDisplayPrice(product);
2329
- if (basePrice > finalPrice && basePrice > 0) {
2330
- return Math.round((basePrice - finalPrice) / basePrice * 100);
3411
+ /**
3412
+ * Create a CimplifyElements instance for embedding checkout components.
3413
+ * Like Stripe's stripe.elements().
3414
+ *
3415
+ * @param businessId - The business ID for checkout context
3416
+ * @param options - Optional configuration for elements
3417
+ *
3418
+ * @example
3419
+ * ```ts
3420
+ * const elements = client.elements('bus_xxx');
3421
+ * const authElement = elements.create('auth');
3422
+ * authElement.mount('#auth-container');
3423
+ * ```
3424
+ */
3425
+ elements(businessId, options) {
3426
+ return createElements(this, businessId, options);
2331
3427
  }
2332
- return 0;
3428
+ };
3429
+ function createCimplifyClient(config = {}) {
3430
+ return new CimplifyClient(config);
2333
3431
  }
2334
- function getMarkupPercentage(product) {
2335
- const basePrice = getBasePrice(product);
2336
- const finalPrice = getDisplayPrice(product);
2337
- if (finalPrice > basePrice && basePrice > 0) {
2338
- return Math.round((finalPrice - basePrice) / basePrice * 100);
3432
+
3433
+ // src/query/builder.ts
3434
+ var QueryBuilder = class {
3435
+ constructor(entity) {
3436
+ this.filters = [];
3437
+ this.modifiers = [];
3438
+ this.pathSegments = [];
3439
+ this.entity = entity;
2339
3440
  }
2340
- return 0;
2341
- }
2342
- function getProductCurrency(product) {
2343
- if (product.price_info?.currency) {
2344
- return product.price_info.currency;
3441
+ path(segment) {
3442
+ this.pathSegments.push(segment);
3443
+ return this;
2345
3444
  }
2346
- if (product.currency) {
2347
- return product.currency;
3445
+ where(field, op, value) {
3446
+ const v = typeof value === "string" ? `'${value}'` : value;
3447
+ if (op === "contains" || op === "startsWith") {
3448
+ this.filters.push(`@.${field} ${op} ${v}`);
3449
+ } else {
3450
+ this.filters.push(`@.${field}${op}${v}`);
3451
+ }
3452
+ return this;
2348
3453
  }
2349
- return "GHS";
2350
- }
2351
- function formatProductPrice(product, locale = "en-US") {
2352
- const price = getDisplayPrice(product);
2353
- const currency = getProductCurrency(product);
2354
- return formatPrice(price, currency, locale);
2355
- }
2356
-
2357
- // src/utils/payment.ts
2358
- function categorizePaymentError(error, errorCode) {
2359
- let message = "An unexpected error occurred during payment processing. Please try again or contact support.";
2360
- let recoverable = true;
2361
- let code = errorCode || "PAYMENT_ERROR";
2362
- const technical = error.stack;
2363
- const errorMessage = error.message?.toLowerCase() || "";
2364
- if (errorCode === "INSUFFICIENT_FUNDS" || errorMessage.includes("insufficient") || errorMessage.includes("funds")) {
2365
- code = "INSUFFICIENT_FUNDS";
2366
- message = "Your payment method has insufficient funds. Please try another payment method.";
2367
- } else if (errorCode === "CARD_DECLINED" || errorMessage.includes("declined")) {
2368
- code = "CARD_DECLINED";
2369
- message = "Your card was declined. Please try another card or payment method.";
2370
- } else if (errorMessage.includes("cancelled") || errorMessage.includes("canceled") || errorCode === "PAYMENT_CANCELLED") {
2371
- code = "PAYMENT_CANCELLED";
2372
- message = "Payment was cancelled. You can try again when ready.";
2373
- } else if (errorMessage.includes("network") || errorMessage.includes("connection") || errorCode === "NETWORK_ERROR") {
2374
- code = "NETWORK_ERROR";
2375
- message = "Network connection issue. Please check your internet connection and try again.";
2376
- } else if (errorMessage.includes("timeout") || errorCode === "TIMEOUT") {
2377
- code = "TIMEOUT";
2378
- message = "Payment processing timed out. Please try again.";
2379
- } else if (errorCode === "PAYMENT_ACTION_NOT_COMPLETED") {
2380
- code = "PAYMENT_ACTION_NOT_COMPLETED";
2381
- message = "Payment action was not completed. Please try again.";
2382
- } else if (errorCode === "AUTHORIZATION_FAILED") {
2383
- code = "AUTHORIZATION_FAILED";
2384
- message = "Authorization failed. Please check your code and try again.";
2385
- } else if (errorCode === "INVALID_OTP") {
2386
- code = "INVALID_OTP";
2387
- message = "Invalid verification code. Please check and try again.";
3454
+ and(field, op, value) {
3455
+ return this.where(field, op, value);
2388
3456
  }
2389
- return { code, message, recoverable, technical };
2390
- }
2391
- function isQuickPaymentResponse(response) {
2392
- return response !== null && typeof response === "object" && "payment" in response && typeof response.payment === "object";
2393
- }
2394
- function isWebPaymentResponse(response) {
2395
- return response !== null && typeof response === "object" && "transaction" in response && typeof response.transaction === "object";
2396
- }
2397
- function normalizePaymentResponse(response) {
2398
- if (!response) {
2399
- return {
2400
- method: "unknown",
2401
- provider: "unknown",
2402
- requires_action: false,
2403
- metadata: {}
2404
- };
3457
+ sort(field, order = "asc") {
3458
+ this.modifiers.push(`sort(${field},${order})`);
3459
+ return this;
2405
3460
  }
2406
- if (isQuickPaymentResponse(response)) {
2407
- return {
2408
- method: response.payment.type?.toLowerCase() || "unknown",
2409
- provider: response.payment.provider?.toLowerCase() || "unknown",
2410
- requires_action: !!response.payment.redirect_url || !!response.payment.access_code,
2411
- public_key: response.payment.public_key,
2412
- client_secret: response.payment.access_code,
2413
- access_code: response.payment.access_code,
2414
- redirect_url: response.payment.redirect_url,
2415
- transaction_id: response.payment.reference,
2416
- order_id: response.order_id,
2417
- reference: response.payment.reference,
2418
- instructions: response.payment.instructions,
2419
- metadata: response.payment.metadata
2420
- };
3461
+ limit(n) {
3462
+ this.modifiers.push(`limit(${n})`);
3463
+ return this;
2421
3464
  }
2422
- if (isWebPaymentResponse(response)) {
2423
- const authType = response.authorization_type?.toLowerCase();
2424
- const validAuthTypes = ["otp", "pin", "phone", "birthday", "address"];
2425
- const safeAuthType = authType && validAuthTypes.includes(authType) ? authType : void 0;
2426
- return {
2427
- provider: response.transaction.provider_type?.toLowerCase() || "unknown",
2428
- requires_action: response.requires_action || false,
2429
- public_key: response.public_key,
2430
- client_secret: response.client_secret,
2431
- redirect_url: response.authorization_url,
2432
- transaction_id: response.transaction.id,
2433
- order_id: response.transaction.order_id,
2434
- reference: response.transaction.provider_reference,
2435
- metadata: response.transaction.metadata,
2436
- method: response.transaction.payment_method?.toLowerCase() || "unknown",
2437
- instructions: response.display_text,
2438
- display_text: response.display_text,
2439
- requires_authorization: response.requires_authorization,
2440
- authorization_type: safeAuthType,
2441
- provider_payment_id: response.provider_payment_id || response.transaction.provider_reference
2442
- };
3465
+ offset(n) {
3466
+ this.modifiers.push(`offset(${n})`);
3467
+ return this;
2443
3468
  }
2444
- return {
2445
- method: "unknown",
2446
- provider: "unknown",
2447
- requires_action: false,
2448
- metadata: {}
2449
- };
2450
- }
2451
- function normalizeStatusResponse(response) {
2452
- if (!response || typeof response !== "object") {
2453
- return {
2454
- status: "pending",
2455
- paid: false,
2456
- message: "No status available"
2457
- };
3469
+ count() {
3470
+ this.modifiers.push("count");
3471
+ return this;
2458
3472
  }
2459
- const res = response;
2460
- const status = res.status?.toLowerCase() || "";
2461
- let standardStatus;
2462
- if (status === "success" || status === "completed" || res.paid === true) {
2463
- standardStatus = "success";
2464
- } else if (status === "failed" || status === "declined" || status === "cancelled") {
2465
- standardStatus = "failed";
2466
- } else if (status === "processing" || status === "pending_confirmation") {
2467
- standardStatus = "processing";
2468
- } else {
2469
- standardStatus = "pending";
3473
+ enriched() {
3474
+ this.modifiers.push("enriched");
3475
+ return this;
2470
3476
  }
2471
- return {
2472
- status: standardStatus,
2473
- paid: res.paid || standardStatus === "success",
2474
- amount: res.amount,
2475
- currency: res.currency,
2476
- reference: res.reference,
2477
- message: res.message || ""
2478
- };
2479
- }
2480
- var MOBILE_MONEY_PROVIDERS = {
2481
- mtn: { name: "MTN Mobile Money", prefix: ["024", "054", "055", "059"] },
2482
- vodafone: { name: "Vodafone Cash", prefix: ["020", "050"] },
2483
- airtel: { name: "AirtelTigo Money", prefix: ["027", "057", "026", "056"] }
2484
- };
2485
- function detectMobileMoneyProvider(phoneNumber) {
2486
- const cleaned = phoneNumber.replace(/\D/g, "");
2487
- const prefix = cleaned.slice(-9, -6);
2488
- for (const [provider, info] of Object.entries(MOBILE_MONEY_PROVIDERS)) {
2489
- if (info.prefix.some((p) => prefix.startsWith(p.slice(1)))) {
2490
- return provider;
3477
+ build() {
3478
+ let query2 = this.entity;
3479
+ if (this.pathSegments.length > 0) {
3480
+ query2 += "." + this.pathSegments.join(".");
3481
+ }
3482
+ if (this.filters.length > 0) {
3483
+ query2 += `[?(${this.filters.join(" && ")})]`;
3484
+ }
3485
+ for (const mod of this.modifiers) {
3486
+ query2 += `#${mod}`;
2491
3487
  }
3488
+ return query2;
2492
3489
  }
2493
- return null;
3490
+ toString() {
3491
+ return this.build();
3492
+ }
3493
+ };
3494
+ function query(entity) {
3495
+ return new QueryBuilder(entity);
2494
3496
  }
2495
3497
 
2496
- export { AUTHORIZATION_TYPE, AUTH_MUTATION, AuthService, BusinessService, CHECKOUT_MODE, CHECKOUT_MUTATION, CHECKOUT_STEP, CONTACT_TYPE, CURRENCY_SYMBOLS, CartOperations, CatalogueQueries, CheckoutService as CheckoutOperations, CheckoutService, CimplifyClient, CimplifyElement, CimplifyElements, CimplifyError, DEFAULT_COUNTRY, DEFAULT_CURRENCY, DEVICE_TYPE, ELEMENT_TYPES, EVENT_TYPES, ErrorCode, FxService, InventoryService, LINK_MUTATION, LINK_QUERY, LinkService, LiteService, MESSAGE_TYPES, MOBILE_MONEY_PROVIDER, MOBILE_MONEY_PROVIDERS, ORDER_MUTATION, ORDER_TYPE, OrderQueries, PAYMENT_METHOD, PAYMENT_MUTATION, PAYMENT_STATE, PICKUP_TIME_TYPE, QueryBuilder, SchedulingService, categorizePaymentError, combine, combineObject, createCimplifyClient, createElements, detectMobileMoneyProvider, err, flatMap, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatProductPrice, fromPromise, generateIdempotencyKey, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getOrElse, getProductCurrency, isCimplifyError, isErr, isOk, isOnSale, isRetryableError, mapError, mapResult, normalizePaymentResponse, normalizeStatusResponse, ok, parsePrice, query, toNullable, tryCatch, unwrap };
3498
+ export { AUTHORIZATION_TYPE, AUTH_MUTATION, AuthService, BusinessService, CHECKOUT_MODE, CHECKOUT_MUTATION, CHECKOUT_STEP, CONTACT_TYPE, CURRENCY_SYMBOLS, CartOperations, CatalogueQueries, CheckoutService as CheckoutOperations, CheckoutService, CimplifyClient, CimplifyElement, CimplifyElements, CimplifyError, DEFAULT_COUNTRY, DEFAULT_CURRENCY, DEVICE_TYPE, ELEMENT_TYPES, EVENT_TYPES, ErrorCode, FxService, InventoryService, LINK_MUTATION, LINK_QUERY, LinkService, LiteService, MESSAGE_TYPES, MOBILE_MONEY_PROVIDER, MOBILE_MONEY_PROVIDERS, ORDER_MUTATION, ORDER_TYPE, OrderQueries, PAYMENT_METHOD, PAYMENT_MUTATION, PAYMENT_STATE, PICKUP_TIME_TYPE, QueryBuilder, SchedulingService, categorizePaymentError, combine, combineObject, createCimplifyClient, createElements, detectMobileMoneyProvider, err, flatMap, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatProductPrice, fromPromise, generateIdempotencyKey, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getOrElse, getProductCurrency, isCimplifyError, isErr, isOk, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isRetryableError, mapError, mapResult, normalizePaymentResponse, normalizeStatusResponse, ok, parsePrice, query, toNullable, tryCatch, unwrap };