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