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