@cimplify/sdk 0.102.0 → 0.103.0

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.
Files changed (68) hide show
  1. package/dist/advanced.d.mts +3 -3
  2. package/dist/advanced.d.ts +3 -3
  3. package/dist/advanced.js +23 -22
  4. package/dist/advanced.mjs +4 -3
  5. package/dist/{chunk-DCMXUKDQ.mjs → chunk-C2JSNJUC.mjs} +404 -95
  6. package/dist/chunk-CNN7Z2IP.js +125 -0
  7. package/dist/{chunk-DKLHCZ5M.mjs → chunk-GERXZQSX.mjs} +487 -11
  8. package/dist/chunk-GOHSXBKW.mjs +105 -0
  9. package/dist/{chunk-4KNFBPV2.mjs → chunk-H7ACMHM3.mjs} +32 -14
  10. package/dist/{chunk-Z3K2D3TE.mjs → chunk-J6PNHDHL.mjs} +453 -483
  11. package/dist/{chunk-NIOUSKQQ.js → chunk-JG5CQNCB.js} +5 -0
  12. package/dist/{chunk-YER7DIOM.js → chunk-JSWJ4XDE.js} +471 -519
  13. package/dist/{chunk-DLW3RWRK.mjs → chunk-M4URVKHE.mjs} +46 -12
  14. package/dist/{chunk-WMEC5JWN.js → chunk-NNG6IFFX.js} +50 -13
  15. package/dist/{chunk-LOWSMGRT.js → chunk-QOWPGS7Z.js} +34 -16
  16. package/dist/{chunk-YDA46OZ6.js → chunk-R6OLQ33I.js} +560 -80
  17. package/dist/{chunk-EAZGA7LD.js → chunk-VIL6IQCG.js} +406 -97
  18. package/dist/{chunk-O3YUHLZL.mjs → chunk-VNPEYLB2.mjs} +5 -0
  19. package/dist/{client-B40KFazg.d.ts → client-BfpNjJ_a.d.mts} +90 -299
  20. package/dist/{client-BizGfqB7.d.mts → client-DHBwvh7U.d.ts} +90 -299
  21. package/dist/{client-BdErclfU.d.mts → client-DpqRobY9.d.mts} +2 -2
  22. package/dist/{client-nsiMwuC1.d.ts → client-Dqg2mc-7.d.ts} +2 -2
  23. package/dist/{index-DpmnI__D.d.mts → index-C8uDqLwJ.d.ts} +14 -4
  24. package/dist/{index-nEyXWIyR.d.ts → index-e2y6AK70.d.mts} +14 -4
  25. package/dist/index.d.mts +81 -8
  26. package/dist/index.d.ts +81 -8
  27. package/dist/index.js +202 -169
  28. package/dist/index.mjs +6 -5
  29. package/dist/link-cuNVYc2s.d.mts +297 -0
  30. package/dist/link-cuNVYc2s.d.ts +297 -0
  31. package/dist/mock/cli.mjs +410 -94
  32. package/dist/mock/library.js +410 -94
  33. package/dist/mock/library.mjs +410 -94
  34. package/dist/mock/msw.js +410 -94
  35. package/dist/mock/msw.mjs +410 -94
  36. package/dist/{product-Byf0unSy.d.mts → payment-H5LoIvXU.d.mts} +189 -7
  37. package/dist/{product-Byf0unSy.d.ts → payment-H5LoIvXU.d.ts} +189 -7
  38. package/dist/{price-CS4E5zLS.d.mts → price-BvePgDXe.d.mts} +1 -1
  39. package/dist/{price-BstChV9a.d.ts → price-C2RqscMY.d.ts} +1 -1
  40. package/dist/react.d.mts +4 -4
  41. package/dist/react.d.ts +4 -4
  42. package/dist/react.js +141 -140
  43. package/dist/react.mjs +5 -4
  44. package/dist/{server-DqbZh8Bk.d.mts → server-C7FyLdrz.d.mts} +11 -9
  45. package/dist/{server-Cj40R2ZD.d.ts → server-DoXIpSd7.d.ts} +11 -9
  46. package/dist/server.d.mts +4 -4
  47. package/dist/server.d.ts +4 -4
  48. package/dist/server.js +11 -10
  49. package/dist/server.mjs +5 -4
  50. package/dist/testing/msw.d.mts +2 -2
  51. package/dist/testing/msw.d.ts +2 -2
  52. package/dist/testing/msw.js +4 -3
  53. package/dist/testing/msw.mjs +3 -2
  54. package/dist/testing/suite.d.mts +6 -6
  55. package/dist/testing/suite.d.ts +6 -6
  56. package/dist/testing/suite.js +26 -25
  57. package/dist/testing/suite.mjs +7 -6
  58. package/dist/testing.d.mts +154 -55
  59. package/dist/testing.d.ts +154 -55
  60. package/dist/testing.js +83 -82
  61. package/dist/testing.mjs +8 -7
  62. package/dist/utils.d.mts +4 -4
  63. package/dist/utils.d.ts +4 -4
  64. package/dist/utils.js +37 -29
  65. package/dist/utils.mjs +2 -2
  66. package/package.json +7 -1
  67. package/dist/payment-l0rkieEQ.d.mts +0 -113
  68. package/dist/payment-vzNnIi1n.d.ts +0 -113
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+
3
+ // src/constants.ts
4
+ var CHECKOUT_MODE = {
5
+ LINK: "link",
6
+ GUEST: "guest"
7
+ };
8
+ var ORDER_TYPE = {
9
+ DELIVERY: "delivery",
10
+ PICKUP: "pickup",
11
+ DINE_IN: "dine-in",
12
+ WALK_IN: "walk-in"
13
+ };
14
+ var PAYMENT_METHOD = {
15
+ MOBILE_MONEY: "mobile_money",
16
+ CARD: "card"
17
+ };
18
+ var CHECKOUT_STEP = {
19
+ AUTHENTICATION: "authentication",
20
+ ORDER_DETAILS: "order_details",
21
+ PAYMENT_METHOD: "payment_method",
22
+ PAYMENT: "payment",
23
+ CONFIRMATION: "confirmation"
24
+ };
25
+ var PAYMENT_STATE = {
26
+ INITIAL: "initial",
27
+ PREPARING: "preparing",
28
+ PROCESSING: "processing",
29
+ VERIFYING: "verifying",
30
+ AWAITING_AUTHORIZATION: "awaiting_authorization",
31
+ SUCCESS: "success",
32
+ ERROR: "error",
33
+ TIMEOUT: "timeout"
34
+ };
35
+ var PICKUP_TIME_TYPE = {
36
+ ASAP: "asap",
37
+ SCHEDULED: "scheduled"
38
+ };
39
+ var MOBILE_MONEY_PROVIDER = {
40
+ MTN: "mtn",
41
+ VODAFONE: "vodafone",
42
+ TELECEL: "telecel",
43
+ AIRTEL: "airtel",
44
+ AIRTELTIGO: "airteltigo",
45
+ MPESA: "mpesa"
46
+ };
47
+ var AUTHORIZATION_TYPE = {
48
+ OTP: "otp",
49
+ PIN: "pin",
50
+ PHONE: "phone",
51
+ BIRTHDAY: "birthday",
52
+ ADDRESS: "address"
53
+ };
54
+ var MAX_PAYMENT_AUTHORIZATION_STEPS = 8;
55
+ var DEVICE_TYPE = {
56
+ MOBILE: "mobile",
57
+ DESKTOP: "desktop",
58
+ TABLET: "tablet",
59
+ UNKNOWN: "unknown"
60
+ };
61
+ var CONTACT_TYPE = {
62
+ PHONE: "phone",
63
+ EMAIL: "email"
64
+ };
65
+ var LINK_QUERY = {
66
+ DATA: "link.data",
67
+ ADDRESSES: "link.addresses",
68
+ MOBILE_MONEY: "link.mobile_money",
69
+ PREFERENCES: "link.preferences",
70
+ SESSIONS: "link.sessions"
71
+ };
72
+ var LINK_MUTATION = {
73
+ CHECK_STATUS: "link.check_status",
74
+ ENROLL: "link.enroll",
75
+ ENROLL_AND_LINK_ORDER: "link.enroll_and_link_order",
76
+ UPDATE_PREFERENCES: "link.update_preferences",
77
+ CREATE_ADDRESS: "link.create_address",
78
+ UPDATE_ADDRESS: "link.update_address",
79
+ DELETE_ADDRESS: "link.delete_address",
80
+ SET_DEFAULT_ADDRESS: "link.set_default_address",
81
+ TRACK_ADDRESS_USAGE: "link.track_address_usage",
82
+ CREATE_MOBILE_MONEY: "link.create_mobile_money",
83
+ DELETE_MOBILE_MONEY: "link.delete_mobile_money",
84
+ SET_DEFAULT_MOBILE_MONEY: "link.set_default_mobile_money",
85
+ TRACK_MOBILE_MONEY_USAGE: "link.track_mobile_money_usage",
86
+ VERIFY_MOBILE_MONEY: "link.verify_mobile_money",
87
+ REVOKE_SESSION: "link.revoke_session",
88
+ REVOKE_ALL_SESSIONS: "link.revoke_all_sessions"
89
+ };
90
+ var AUTH_MUTATION = {
91
+ REQUEST_OTP: "auth.request_otp",
92
+ VERIFY_OTP: "auth.verify_otp"
93
+ };
94
+ var CHECKOUT_MUTATION = {
95
+ PROCESS: "checkout.process"
96
+ };
97
+ var PAYMENT_MUTATION = {
98
+ SUBMIT_AUTHORIZATION: "payment.submit_authorization",
99
+ CHECK_STATUS: "order.poll_payment_status"
100
+ };
101
+ var ORDER_MUTATION = {
102
+ UPDATE_CUSTOMER: "order.update_order_customer"
103
+ };
104
+ var DEFAULT_CURRENCY = "GHS";
105
+ var DEFAULT_COUNTRY = "GHA";
106
+
107
+ exports.AUTHORIZATION_TYPE = AUTHORIZATION_TYPE;
108
+ exports.AUTH_MUTATION = AUTH_MUTATION;
109
+ exports.CHECKOUT_MODE = CHECKOUT_MODE;
110
+ exports.CHECKOUT_MUTATION = CHECKOUT_MUTATION;
111
+ exports.CHECKOUT_STEP = CHECKOUT_STEP;
112
+ exports.CONTACT_TYPE = CONTACT_TYPE;
113
+ exports.DEFAULT_COUNTRY = DEFAULT_COUNTRY;
114
+ exports.DEFAULT_CURRENCY = DEFAULT_CURRENCY;
115
+ exports.DEVICE_TYPE = DEVICE_TYPE;
116
+ exports.LINK_MUTATION = LINK_MUTATION;
117
+ exports.LINK_QUERY = LINK_QUERY;
118
+ exports.MAX_PAYMENT_AUTHORIZATION_STEPS = MAX_PAYMENT_AUTHORIZATION_STEPS;
119
+ exports.MOBILE_MONEY_PROVIDER = MOBILE_MONEY_PROVIDER;
120
+ exports.ORDER_MUTATION = ORDER_MUTATION;
121
+ exports.ORDER_TYPE = ORDER_TYPE;
122
+ exports.PAYMENT_METHOD = PAYMENT_METHOD;
123
+ exports.PAYMENT_MUTATION = PAYMENT_MUTATION;
124
+ exports.PAYMENT_STATE = PAYMENT_STATE;
125
+ exports.PICKUP_TIME_TYPE = PICKUP_TIME_TYPE;
@@ -1,5 +1,5 @@
1
- import { safe, generateIdempotencyKey, err, ok, CatalogueQueries, CartOperations, CheckoutService, OrderQueries, LinkService, AuthService, BusinessService, InventoryService, SchedulingService, LiteService, FxService, createElements } from './chunk-Z3K2D3TE.mjs';
2
- import { CimplifyError, ErrorCode, IdempotencyMismatchError, enrichError } from './chunk-O3YUHLZL.mjs';
1
+ import { safe, generateIdempotencyKey, err, ok, CatalogueQueries, CartOperations, CheckoutService, OrderQueries, LinkService, AuthService, BusinessService, InventoryService, SchedulingService, LiteService, FxService, createElements } from './chunk-J6PNHDHL.mjs';
2
+ import { CimplifyError, ErrorCode, IdempotencyMismatchError, enrichError } from './chunk-VNPEYLB2.mjs';
3
3
 
4
4
  // src/store-credit.ts
5
5
  var StoreCreditQueries = class {
@@ -721,6 +721,453 @@ var SupportService = class {
721
721
  }
722
722
  };
723
723
 
724
+ // src/payment-authorization/providers/paystack.ts
725
+ async function openPaystackPopup(options, signal) {
726
+ if (typeof window === "undefined") {
727
+ return { success: false, error: "PROVIDER_UNAVAILABLE" };
728
+ }
729
+ let PaystackPop;
730
+ try {
731
+ const imported = await import('@paystack/inline-js');
732
+ PaystackPop = imported.default;
733
+ } catch {
734
+ return { success: false, error: "PROVIDER_UNAVAILABLE" };
735
+ }
736
+ return new Promise((resolve) => {
737
+ let settled = false;
738
+ let providerStarted = false;
739
+ const resolveOnce = (result) => {
740
+ if (settled) {
741
+ return;
742
+ }
743
+ settled = true;
744
+ if (signal) {
745
+ signal.removeEventListener("abort", onAbort);
746
+ }
747
+ resolve(result);
748
+ };
749
+ const onAbort = () => {
750
+ resolveOnce({
751
+ success: false,
752
+ // Once Paystack owns the transaction, cancellation is ambiguous. The
753
+ // adapter converts this into submitted so Cimplify polling decides.
754
+ error: providerStarted ? "SUBMISSION_UNKNOWN" : "CANCELLED"
755
+ });
756
+ };
757
+ if (signal?.aborted) {
758
+ resolveOnce({ success: false, error: "CANCELLED" });
759
+ return;
760
+ }
761
+ if (signal) {
762
+ signal.addEventListener("abort", onAbort, { once: true });
763
+ }
764
+ try {
765
+ const popup = new PaystackPop();
766
+ popup.newTransaction({
767
+ key: options.key,
768
+ email: options.email,
769
+ amount: options.amount,
770
+ currency: options.currency,
771
+ reference: options.reference,
772
+ accessCode: options.accessCode,
773
+ onSuccess: (transaction) => {
774
+ resolveOnce({
775
+ success: true,
776
+ reference: transaction.reference ?? options.reference
777
+ });
778
+ },
779
+ onCancel: () => {
780
+ resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
781
+ },
782
+ onError: (error) => {
783
+ resolveOnce({
784
+ success: false,
785
+ error: error?.message || "PAYMENT_FAILED"
786
+ });
787
+ }
788
+ });
789
+ providerStarted = true;
790
+ } catch {
791
+ resolveOnce({ success: false, error: "POPUP_BLOCKED" });
792
+ }
793
+ });
794
+ }
795
+ var PaystackAuthorizationAdapter = class {
796
+ constructor(openPopup = openPaystackPopup) {
797
+ this.openPopup = openPopup;
798
+ this.provider = "paystack";
799
+ }
800
+ async authorize(context) {
801
+ const result = await this.openPopup(
802
+ {
803
+ key: context.action.public_key,
804
+ email: context.email,
805
+ accessCode: context.action.client_secret,
806
+ reference: context.reference || context.action.client_secret,
807
+ currency: context.currency
808
+ },
809
+ context.signal
810
+ );
811
+ if (result.success) {
812
+ return { status: "submitted", reference: result.reference || context.reference };
813
+ }
814
+ if (result.error === "SUBMISSION_UNKNOWN") {
815
+ return { status: "submitted", reference: context.reference };
816
+ }
817
+ if (!result.error || result.error === "CANCELLED" || result.error === "PAYMENT_CANCELLED") {
818
+ return { status: "cancelled" };
819
+ }
820
+ if (result.error === "POPUP_BLOCKED") {
821
+ return {
822
+ status: "failed",
823
+ code: "POPUP_BLOCKED",
824
+ message: "Unable to open the Paystack payment popup. Please allow popups and try again.",
825
+ recoverable: true
826
+ };
827
+ }
828
+ if (result.error === "PROVIDER_UNAVAILABLE") {
829
+ return {
830
+ status: "failed",
831
+ code: "PROVIDER_UNAVAILABLE",
832
+ message: "Paystack is unavailable in this environment.",
833
+ recoverable: false
834
+ };
835
+ }
836
+ return {
837
+ status: "failed",
838
+ code: "PAYMENT_FAILED",
839
+ message: result.error || "Paystack payment could not be submitted.",
840
+ recoverable: true
841
+ };
842
+ }
843
+ };
844
+
845
+ // src/payment-authorization/providers/stripe.ts
846
+ async function loadStripeFromPeer(publicKey) {
847
+ try {
848
+ const imported = await import('@stripe/stripe-js');
849
+ return imported.loadStripe ? imported.loadStripe(publicKey) : null;
850
+ } catch {
851
+ return null;
852
+ }
853
+ }
854
+ function createStripeModal() {
855
+ if (typeof document === "undefined" || !document.body) return null;
856
+ const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
857
+ const root = document.createElement("div");
858
+ root.setAttribute("role", "dialog");
859
+ root.setAttribute("aria-modal", "true");
860
+ root.setAttribute("aria-label", "Secure card payment");
861
+ Object.assign(root.style, {
862
+ position: "fixed",
863
+ inset: "0",
864
+ zIndex: "2147483647",
865
+ display: "grid",
866
+ placeItems: "center",
867
+ padding: "16px",
868
+ background: "rgba(15, 23, 42, 0.58)"
869
+ });
870
+ const form = document.createElement("form");
871
+ Object.assign(form.style, {
872
+ width: "min(100%, 480px)",
873
+ maxHeight: "calc(100vh - 32px)",
874
+ overflow: "auto",
875
+ borderRadius: "12px",
876
+ padding: "24px",
877
+ background: "#fff",
878
+ boxShadow: "0 24px 80px rgba(15, 23, 42, 0.32)"
879
+ });
880
+ const title = document.createElement("h2");
881
+ title.textContent = "Complete card payment";
882
+ Object.assign(title.style, { margin: "0 0 18px", font: "600 18px/1.4 system-ui" });
883
+ const paymentMount = document.createElement("div");
884
+ paymentMount.setAttribute("data-cimplify-stripe-payment-element", "");
885
+ const error = document.createElement("p");
886
+ error.setAttribute("role", "alert");
887
+ Object.assign(error.style, {
888
+ minHeight: "20px",
889
+ margin: "14px 0 0",
890
+ color: "#b91c1c",
891
+ font: "14px/1.4 system-ui"
892
+ });
893
+ const actions = document.createElement("div");
894
+ Object.assign(actions.style, {
895
+ display: "flex",
896
+ justifyContent: "flex-end",
897
+ gap: "10px",
898
+ marginTop: "18px"
899
+ });
900
+ const cancelButton = document.createElement("button");
901
+ cancelButton.type = "button";
902
+ cancelButton.textContent = "Cancel";
903
+ Object.assign(cancelButton.style, {
904
+ border: "1px solid #cbd5e1",
905
+ borderRadius: "8px",
906
+ padding: "10px 16px",
907
+ background: "#fff",
908
+ color: "#0f172a",
909
+ cursor: "pointer"
910
+ });
911
+ const submitButton = document.createElement("button");
912
+ submitButton.type = "submit";
913
+ submitButton.textContent = "Pay securely";
914
+ Object.assign(submitButton.style, {
915
+ border: "0",
916
+ borderRadius: "8px",
917
+ padding: "10px 16px",
918
+ background: "#0f172a",
919
+ color: "#fff",
920
+ cursor: "pointer"
921
+ });
922
+ actions.append(cancelButton, submitButton);
923
+ form.append(title, paymentMount, error, actions);
924
+ root.append(form);
925
+ document.body.append(root);
926
+ cancelButton.focus();
927
+ return { root, form, paymentMount, submitButton, cancelButton, error, previousFocus };
928
+ }
929
+ function stripeFailure(error) {
930
+ const message = error?.message || "Stripe could not authorize this payment.";
931
+ if (error?.code === "payment_intent_authentication_failure" || error?.type === "invalid_request_error") {
932
+ return {
933
+ status: "failed",
934
+ code: "AUTHORIZATION_FAILED",
935
+ message,
936
+ recoverable: true
937
+ };
938
+ }
939
+ return { status: "failed", code: "PAYMENT_FAILED", message, recoverable: true };
940
+ }
941
+ var StripeAuthorizationAdapter = class {
942
+ constructor(loadStripe = loadStripeFromPeer) {
943
+ this.loadStripe = loadStripe;
944
+ this.provider = "stripe";
945
+ }
946
+ async authorize(context) {
947
+ if (typeof window === "undefined" || typeof document === "undefined" || context.signal?.aborted) {
948
+ return context.signal?.aborted ? { status: "cancelled" } : {
949
+ status: "failed",
950
+ code: "PROVIDER_UNAVAILABLE",
951
+ message: "Stripe Payment Element is unavailable in this environment.",
952
+ recoverable: false
953
+ };
954
+ }
955
+ let stripe;
956
+ try {
957
+ stripe = await this.loadStripe(context.action.public_key);
958
+ } catch (error) {
959
+ return {
960
+ status: "failed",
961
+ code: "PROVIDER_UNAVAILABLE",
962
+ message: error instanceof Error ? error.message : "Stripe.js could not be loaded.",
963
+ recoverable: true
964
+ };
965
+ }
966
+ if (context.signal?.aborted) {
967
+ return { status: "cancelled" };
968
+ }
969
+ if (!stripe) {
970
+ return {
971
+ status: "failed",
972
+ code: "PROVIDER_UNAVAILABLE",
973
+ message: "Stripe.js could not be loaded.",
974
+ recoverable: false
975
+ };
976
+ }
977
+ const modal = createStripeModal();
978
+ if (!modal) {
979
+ return {
980
+ status: "failed",
981
+ code: "PROVIDER_UNAVAILABLE",
982
+ message: "Stripe Payment Element could not be mounted.",
983
+ recoverable: false
984
+ };
985
+ }
986
+ let paymentElement;
987
+ let elements;
988
+ try {
989
+ elements = stripe.elements({ clientSecret: context.action.client_secret });
990
+ paymentElement = elements.create("payment");
991
+ paymentElement.mount(modal.paymentMount);
992
+ } catch {
993
+ modal.root.remove();
994
+ modal.previousFocus?.focus();
995
+ return {
996
+ status: "failed",
997
+ code: "PROVIDER_UNAVAILABLE",
998
+ message: "Stripe Payment Element could not be initialized.",
999
+ recoverable: false
1000
+ };
1001
+ }
1002
+ if (context.signal?.aborted) {
1003
+ if (paymentElement.destroy) paymentElement.destroy();
1004
+ else paymentElement.unmount?.();
1005
+ modal.root.remove();
1006
+ modal.previousFocus?.focus();
1007
+ return { status: "cancelled" };
1008
+ }
1009
+ return new Promise((resolve) => {
1010
+ let settled = false;
1011
+ let submitting = false;
1012
+ let confirmationStarted = false;
1013
+ const cleanup = () => {
1014
+ context.signal?.removeEventListener("abort", onAbort);
1015
+ modal.form.removeEventListener("submit", onSubmit);
1016
+ modal.cancelButton.removeEventListener("click", onCancel);
1017
+ if (paymentElement.destroy) paymentElement.destroy();
1018
+ else paymentElement.unmount?.();
1019
+ modal.root.remove();
1020
+ modal.previousFocus?.focus();
1021
+ };
1022
+ const settle = (outcome) => {
1023
+ if (settled) return;
1024
+ settled = true;
1025
+ cleanup();
1026
+ resolve(outcome);
1027
+ };
1028
+ const onAbort = () => {
1029
+ if (confirmationStarted) {
1030
+ settle({ status: "submitted", reference: context.reference });
1031
+ return;
1032
+ }
1033
+ settle({ status: "cancelled" });
1034
+ };
1035
+ const onCancel = () => {
1036
+ if (!confirmationStarted) settle({ status: "cancelled" });
1037
+ };
1038
+ const onSubmit = async (event) => {
1039
+ event.preventDefault();
1040
+ if (submitting || settled) return;
1041
+ submitting = true;
1042
+ modal.submitButton.disabled = true;
1043
+ modal.error.textContent = "";
1044
+ try {
1045
+ const validation = elements.submit ? await elements.submit() : {};
1046
+ if (validation.error) {
1047
+ modal.error.textContent = validation.error.message || "Check your payment details.";
1048
+ submitting = false;
1049
+ modal.submitButton.disabled = false;
1050
+ return;
1051
+ }
1052
+ if (settled || context.signal?.aborted) {
1053
+ settle({ status: "cancelled" });
1054
+ return;
1055
+ }
1056
+ confirmationStarted = true;
1057
+ modal.cancelButton.disabled = true;
1058
+ const confirmation = await stripe.confirmPayment({
1059
+ elements,
1060
+ confirmParams: { return_url: context.returnUrl || window.location.href },
1061
+ redirect: "if_required"
1062
+ });
1063
+ if (settled) return;
1064
+ if (confirmation.error) {
1065
+ settle(stripeFailure(confirmation.error));
1066
+ return;
1067
+ }
1068
+ settle({
1069
+ status: "submitted",
1070
+ reference: confirmation.paymentIntent?.id || context.reference
1071
+ });
1072
+ } catch (error) {
1073
+ if (confirmationStarted) {
1074
+ settle({ status: "submitted", reference: context.reference });
1075
+ return;
1076
+ }
1077
+ settle(
1078
+ stripeFailure({
1079
+ message: error instanceof Error ? error.message : "Stripe confirmation failed."
1080
+ })
1081
+ );
1082
+ }
1083
+ };
1084
+ modal.form.addEventListener("submit", onSubmit);
1085
+ modal.cancelButton.addEventListener("click", onCancel);
1086
+ context.signal?.addEventListener("abort", onAbort, { once: true });
1087
+ paymentElement.on?.("escape", onCancel);
1088
+ paymentElement.on?.("loaderror", (event) => {
1089
+ if (confirmationStarted) return;
1090
+ settle({
1091
+ status: "failed",
1092
+ code: "PROVIDER_UNAVAILABLE",
1093
+ message: event?.error?.message || "Stripe Payment Element failed to load.",
1094
+ recoverable: true
1095
+ });
1096
+ });
1097
+ });
1098
+ }
1099
+ };
1100
+
1101
+ // src/payment-authorization/registry.ts
1102
+ function normalizeProvider(provider) {
1103
+ return typeof provider === "string" ? provider.trim().toLowerCase() : "";
1104
+ }
1105
+ var PaymentAuthorizationAdapterRegistry = class {
1106
+ constructor(adapters = []) {
1107
+ this.adapters = /* @__PURE__ */ new Map();
1108
+ for (const adapter of adapters) {
1109
+ this.register(adapter);
1110
+ }
1111
+ }
1112
+ register(adapter) {
1113
+ const provider = normalizeProvider(adapter.provider);
1114
+ if (!provider) {
1115
+ throw new TypeError("Payment authorization adapter provider cannot be empty.");
1116
+ }
1117
+ this.adapters.set(provider, adapter);
1118
+ return this;
1119
+ }
1120
+ unregister(provider) {
1121
+ return this.adapters.delete(normalizeProvider(provider));
1122
+ }
1123
+ get(provider) {
1124
+ return this.adapters.get(normalizeProvider(provider));
1125
+ }
1126
+ supports(provider) {
1127
+ return this.adapters.has(normalizeProvider(provider));
1128
+ }
1129
+ providers() {
1130
+ return [...this.adapters.keys()];
1131
+ }
1132
+ async authorize(action, options) {
1133
+ const provider = normalizeProvider(action.provider);
1134
+ if (!provider) {
1135
+ return {
1136
+ status: "failed",
1137
+ code: "PROVIDER_UNAVAILABLE",
1138
+ message: "Card authorization response did not identify a payment provider.",
1139
+ recoverable: false
1140
+ };
1141
+ }
1142
+ const adapter = this.adapters.get(provider);
1143
+ if (!adapter) {
1144
+ return {
1145
+ status: "failed",
1146
+ code: "PROVIDER_UNAVAILABLE",
1147
+ message: `Card authorization provider '${provider}' is not supported by this SDK client.`,
1148
+ recoverable: false
1149
+ };
1150
+ }
1151
+ try {
1152
+ const normalizedAction = action.provider === provider ? action : { ...action, provider };
1153
+ return await adapter.authorize({ ...options, action: normalizedAction });
1154
+ } catch (error) {
1155
+ return {
1156
+ status: "failed",
1157
+ code: "PAYMENT_FAILED",
1158
+ message: error instanceof Error ? error.message : `Card authorization provider '${provider}' failed unexpectedly.`,
1159
+ recoverable: true
1160
+ };
1161
+ }
1162
+ }
1163
+ };
1164
+ function createDefaultPaymentAuthorizationAdapterRegistry() {
1165
+ return new PaymentAuthorizationAdapterRegistry([
1166
+ new PaystackAuthorizationAdapter(),
1167
+ new StripeAuthorizationAdapter()
1168
+ ]);
1169
+ }
1170
+
724
1171
  // src/client.ts
725
1172
  var SESSION_TOKEN_STORAGE_KEY = "cimplify_session_token";
726
1173
  var SESSION_SYNC_STORAGE_KEY = "cimplify_session_sync";
@@ -845,6 +1292,7 @@ var CimplifyClient = class {
845
1292
  this.context = {};
846
1293
  this.businessId = null;
847
1294
  this.businessIdResolvePromise = null;
1295
+ this.publicKeyEpoch = 0;
848
1296
  this.inflightRequests = /* @__PURE__ */ new Map();
849
1297
  /**
850
1298
  * App-level event channel. Subscribe to surface mutation rollbacks, outbox
@@ -863,6 +1311,7 @@ var CimplifyClient = class {
863
1311
  this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
864
1312
  this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
865
1313
  this.hooks = config.hooks ?? {};
1314
+ this.authorizationAdapters = config.authorizationAdapterRegistry ?? createDefaultPaymentAuthorizationAdapterRegistry();
866
1315
  this.fetchImpl = (config.fetch ?? globalThis.fetch).bind(globalThis);
867
1316
  this.sessionToken = this.loadSessionToken() ?? this.mintSessionToken();
868
1317
  this.watchCrossTabSession();
@@ -1079,10 +1528,18 @@ var CimplifyClient = class {
1079
1528
  // X-API-Key and the business can't be resolved.
1080
1529
  setPublicKey(publicKey) {
1081
1530
  const normalized = publicKey.trim();
1082
- if (!normalized) {
1083
- return;
1084
- }
1531
+ if (normalized === this.publicKey) return;
1085
1532
  this.publicKey = normalized;
1533
+ this.publicKeyEpoch += 1;
1534
+ this.businessId = null;
1535
+ this.businessIdResolvePromise = null;
1536
+ this.inflightRequests.clear();
1537
+ this._activity?.resetSession();
1538
+ delete this.context.location_id;
1539
+ if (this.accessToken !== null) {
1540
+ this.setAccessToken(null);
1541
+ }
1542
+ this.events.emit(SESSION_CLEARED, {});
1086
1543
  }
1087
1544
  getBusinessId() {
1088
1545
  return this.businessId;
@@ -1094,7 +1551,8 @@ var CimplifyClient = class {
1094
1551
  if (this.businessIdResolvePromise) {
1095
1552
  return this.businessIdResolvePromise;
1096
1553
  }
1097
- this.businessIdResolvePromise = (async () => {
1554
+ const publicKeyEpoch = this.publicKeyEpoch;
1555
+ const resolution = (async () => {
1098
1556
  const result = await this.business.getInfo();
1099
1557
  if (!result.ok || !result.value?.id) {
1100
1558
  throw new CimplifyError(
@@ -1103,13 +1561,23 @@ var CimplifyClient = class {
1103
1561
  true
1104
1562
  );
1105
1563
  }
1564
+ if (this.publicKeyEpoch !== publicKeyEpoch) {
1565
+ throw new CimplifyError(
1566
+ ErrorCode.TENANT_CONTEXT_CHANGED,
1567
+ "The storefront tenant changed while resolving its business identity.",
1568
+ true
1569
+ );
1570
+ }
1106
1571
  this.businessId = result.value.id;
1107
1572
  return result.value.id;
1108
1573
  })();
1574
+ this.businessIdResolvePromise = resolution;
1109
1575
  try {
1110
- return await this.businessIdResolvePromise;
1576
+ return await resolution;
1111
1577
  } finally {
1112
- this.businessIdResolvePromise = null;
1578
+ if (this.businessIdResolvePromise === resolution) {
1579
+ this.businessIdResolvePromise = null;
1580
+ }
1113
1581
  }
1114
1582
  }
1115
1583
  async authedFetch(url, options, timeoutMs) {
@@ -1312,7 +1780,13 @@ var CimplifyClient = class {
1312
1780
  }
1313
1781
  getDedupeKey(type, payload, cacheOptions) {
1314
1782
  const cacheKey = cacheOptions ? `|${JSON.stringify(cacheOptions)}` : "";
1315
- return `${type}:${JSON.stringify(payload)}${cacheKey}`;
1783
+ const identityKey = JSON.stringify([
1784
+ this.publicKey.trim(),
1785
+ this.publicKeyEpoch,
1786
+ this.sessionEpoch,
1787
+ this.context.location_id ?? null
1788
+ ]);
1789
+ return `${type}:${identityKey}:${JSON.stringify(payload)}${cacheKey}`;
1316
1790
  }
1317
1791
  async deduplicatedRequest(key, requestFn) {
1318
1792
  const existing = this.inflightRequests.get(key);
@@ -1320,7 +1794,9 @@ var CimplifyClient = class {
1320
1794
  return existing;
1321
1795
  }
1322
1796
  const request = requestFn().finally(() => {
1323
- this.inflightRequests.delete(key);
1797
+ if (this.inflightRequests.get(key) === request) {
1798
+ this.inflightRequests.delete(key);
1799
+ }
1324
1800
  });
1325
1801
  this.inflightRequests.set(key, request);
1326
1802
  return request;
@@ -1572,4 +2048,4 @@ function createCimplifyClient(config = {}) {
1572
2048
  return new CimplifyClient(config);
1573
2049
  }
1574
2050
 
1575
- export { AUTH_TOKEN_CHANGED, ActivityService, CimplifyClient, DeliveryService, MUTATION_ERROR, PlacesService, SESSION_CLAIMED, SESSION_CLEARED, StoreCreditQueries, SubscriptionService, SupportService, UploadService, createCimplifyClient };
2051
+ export { AUTH_TOKEN_CHANGED, ActivityService, CimplifyClient, DeliveryService, MUTATION_ERROR, PaymentAuthorizationAdapterRegistry, PaystackAuthorizationAdapter, PlacesService, SESSION_CLAIMED, SESSION_CLEARED, StoreCreditQueries, StripeAuthorizationAdapter, SubscriptionService, SupportService, UploadService, createCimplifyClient, createDefaultPaymentAuthorizationAdapterRegistry };