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