@cimplify/sdk 0.102.0 → 0.104.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 (69) 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-2WPVUIHX.mjs} +455 -105
  6. package/dist/{chunk-NIOUSKQQ.js → chunk-4BKOKHE3.js} +9 -0
  7. package/dist/{chunk-YDA46OZ6.js → chunk-5BDYDZ4U.js} +722 -90
  8. package/dist/{chunk-4KNFBPV2.mjs → chunk-5RT4HYMP.mjs} +61 -15
  9. package/dist/{chunk-WMEC5JWN.js → chunk-7PDEX54K.js} +50 -13
  10. package/dist/chunk-CNN7Z2IP.js +125 -0
  11. package/dist/{chunk-DLW3RWRK.mjs → chunk-DYIAX3WI.mjs} +46 -12
  12. package/dist/{chunk-LOWSMGRT.js → chunk-E4K2VQDD.js} +64 -16
  13. package/dist/chunk-GOHSXBKW.mjs +105 -0
  14. package/dist/{chunk-O3YUHLZL.mjs → chunk-H3RATH4B.mjs} +9 -0
  15. package/dist/{chunk-YER7DIOM.js → chunk-IQFFSKE2.js} +753 -594
  16. package/dist/{chunk-Z3K2D3TE.mjs → chunk-OCCNG6MO.mjs} +735 -558
  17. package/dist/{chunk-DKLHCZ5M.mjs → chunk-OKADBZAI.mjs} +652 -25
  18. package/dist/{chunk-EAZGA7LD.js → chunk-YVAEY3VC.js} +457 -107
  19. package/dist/{client-nsiMwuC1.d.ts → client-BVNWjk6W.d.ts} +2 -2
  20. package/dist/{client-B40KFazg.d.ts → client-BuLcOWLv.d.ts} +202 -319
  21. package/dist/{client-BizGfqB7.d.mts → client-D5AoobFc.d.mts} +202 -319
  22. package/dist/{client-BdErclfU.d.mts → client-fM716k7G.d.mts} +2 -2
  23. package/dist/{index-DpmnI__D.d.mts → index-BtcoqrY1.d.ts} +14 -4
  24. package/dist/{index-nEyXWIyR.d.ts → index-D7Tf3v7c.d.mts} +14 -4
  25. package/dist/index.d.mts +83 -8
  26. package/dist/index.d.ts +83 -8
  27. package/dist/index.js +203 -169
  28. package/dist/index.mjs +7 -5
  29. package/dist/link-D9iSVxt5.d.mts +308 -0
  30. package/dist/link-D9iSVxt5.d.ts +308 -0
  31. package/dist/mock/cli.mjs +463 -104
  32. package/dist/mock/library.js +463 -104
  33. package/dist/mock/library.mjs +463 -104
  34. package/dist/mock/msw.js +463 -104
  35. package/dist/mock/msw.mjs +463 -104
  36. package/dist/{product-Byf0unSy.d.mts → payment-g0HFkEDU.d.mts} +708 -61
  37. package/dist/{product-Byf0unSy.d.ts → payment-g0HFkEDU.d.ts} +708 -61
  38. package/dist/{price-BstChV9a.d.ts → price-BLIeAb8K.d.ts} +1 -1
  39. package/dist/{price-CS4E5zLS.d.mts → price-Czq_cTC_.d.mts} +1 -1
  40. package/dist/react.d.mts +4 -4
  41. package/dist/react.d.ts +4 -4
  42. package/dist/react.js +194 -169
  43. package/dist/react.mjs +55 -30
  44. package/dist/{server-DqbZh8Bk.d.mts → server-BkVhOIwn.d.mts} +14 -10
  45. package/dist/{server-Cj40R2ZD.d.ts → server-DKK_D-PP.d.ts} +14 -10
  46. package/dist/server.d.mts +4 -4
  47. package/dist/server.d.ts +4 -4
  48. package/dist/server.js +12 -10
  49. package/dist/server.mjs +6 -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 +195 -56
  59. package/dist/testing.d.ts +195 -56
  60. package/dist/testing.js +95 -82
  61. package/dist/testing.mjs +14 -9
  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/registry/chat-widget.json +1 -1
  68. package/dist/payment-l0rkieEQ.d.mts +0 -113
  69. package/dist/payment-vzNnIi1n.d.ts +0 -113
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkWMEC5JWN_js = require('./chunk-WMEC5JWN.js');
4
- var chunkNIOUSKQQ_js = require('./chunk-NIOUSKQQ.js');
3
+ var chunkCNN7Z2IP_js = require('./chunk-CNN7Z2IP.js');
4
+ var chunk7PDEX54K_js = require('./chunk-7PDEX54K.js');
5
+ var chunk4BKOKHE3_js = require('./chunk-4BKOKHE3.js');
5
6
 
6
7
  // src/types/result.ts
7
8
  function ok(value) {
@@ -116,11 +117,11 @@ function clearIntentIdempotencyKey(scope, storage = sessionStorageOrNull()) {
116
117
 
117
118
  // src/internal/safe.ts
118
119
  function toCimplifyError(error) {
119
- if (error instanceof chunkNIOUSKQQ_js.CimplifyError) return chunkNIOUSKQQ_js.enrichError(error);
120
+ if (error instanceof chunk4BKOKHE3_js.CimplifyError) return chunk4BKOKHE3_js.enrichError(error);
120
121
  if (error instanceof Error) {
121
- return chunkNIOUSKQQ_js.enrichError(new chunkNIOUSKQQ_js.CimplifyError(chunkNIOUSKQQ_js.ErrorCode.UNKNOWN_ERROR, error.message, false));
122
+ return chunk4BKOKHE3_js.enrichError(new chunk4BKOKHE3_js.CimplifyError(chunk4BKOKHE3_js.ErrorCode.UNKNOWN_ERROR, error.message, false));
122
123
  }
123
- return chunkNIOUSKQQ_js.enrichError(new chunkNIOUSKQQ_js.CimplifyError(chunkNIOUSKQQ_js.ErrorCode.UNKNOWN_ERROR, String(error), false));
124
+ return chunk4BKOKHE3_js.enrichError(new chunk4BKOKHE3_js.CimplifyError(chunk4BKOKHE3_js.ErrorCode.UNKNOWN_ERROR, String(error), false));
124
125
  }
125
126
  async function safe(promise) {
126
127
  try {
@@ -695,7 +696,7 @@ var CartOperations = class {
695
696
  if (typeof updates.quantity !== "number") {
696
697
  return {
697
698
  ok: false,
698
- error: new chunkNIOUSKQQ_js.CimplifyError(
699
+ error: new chunk4BKOKHE3_js.CimplifyError(
699
700
  "VALIDATION_ERROR",
700
701
  "updateItem requires a quantity; richer mid-cart edits are not supported"
701
702
  )
@@ -786,223 +787,16 @@ var CartOperations = class {
786
787
  }
787
788
  };
788
789
 
789
- // src/constants.ts
790
- var CHECKOUT_MODE = {
791
- LINK: "link",
792
- GUEST: "guest"
793
- };
794
- var ORDER_TYPE = {
795
- DELIVERY: "delivery",
796
- PICKUP: "pickup",
797
- DINE_IN: "dine-in",
798
- WALK_IN: "walk-in"
799
- };
800
- var PAYMENT_METHOD = {
801
- MOBILE_MONEY: "mobile_money",
802
- CARD: "card"
803
- };
804
- var CHECKOUT_STEP = {
805
- AUTHENTICATION: "authentication",
806
- ORDER_DETAILS: "order_details",
807
- PAYMENT_METHOD: "payment_method",
808
- PAYMENT: "payment",
809
- CONFIRMATION: "confirmation"
810
- };
811
- var PAYMENT_STATE = {
812
- INITIAL: "initial",
813
- PREPARING: "preparing",
814
- PROCESSING: "processing",
815
- VERIFYING: "verifying",
816
- AWAITING_AUTHORIZATION: "awaiting_authorization",
817
- SUCCESS: "success",
818
- ERROR: "error",
819
- TIMEOUT: "timeout"
820
- };
821
- var PICKUP_TIME_TYPE = {
822
- ASAP: "asap",
823
- SCHEDULED: "scheduled"
824
- };
825
- var MOBILE_MONEY_PROVIDER = {
826
- MTN: "mtn",
827
- VODAFONE: "vodafone",
828
- TELECEL: "telecel",
829
- AIRTEL: "airtel",
830
- AIRTELTIGO: "airteltigo",
831
- MPESA: "mpesa"
832
- };
833
- var AUTHORIZATION_TYPE = {
834
- OTP: "otp",
835
- PIN: "pin",
836
- PHONE: "phone",
837
- BIRTHDAY: "birthday",
838
- ADDRESS: "address"
839
- };
840
- var DEVICE_TYPE = {
841
- MOBILE: "mobile",
842
- DESKTOP: "desktop",
843
- TABLET: "tablet",
844
- UNKNOWN: "unknown"
845
- };
846
- var CONTACT_TYPE = {
847
- PHONE: "phone",
848
- EMAIL: "email"
849
- };
850
- var LINK_QUERY = {
851
- DATA: "link.data",
852
- ADDRESSES: "link.addresses",
853
- MOBILE_MONEY: "link.mobile_money",
854
- PREFERENCES: "link.preferences",
855
- SESSIONS: "link.sessions"
856
- };
857
- var LINK_MUTATION = {
858
- CHECK_STATUS: "link.check_status",
859
- ENROLL: "link.enroll",
860
- ENROLL_AND_LINK_ORDER: "link.enroll_and_link_order",
861
- UPDATE_PREFERENCES: "link.update_preferences",
862
- CREATE_ADDRESS: "link.create_address",
863
- UPDATE_ADDRESS: "link.update_address",
864
- DELETE_ADDRESS: "link.delete_address",
865
- SET_DEFAULT_ADDRESS: "link.set_default_address",
866
- TRACK_ADDRESS_USAGE: "link.track_address_usage",
867
- CREATE_MOBILE_MONEY: "link.create_mobile_money",
868
- DELETE_MOBILE_MONEY: "link.delete_mobile_money",
869
- SET_DEFAULT_MOBILE_MONEY: "link.set_default_mobile_money",
870
- TRACK_MOBILE_MONEY_USAGE: "link.track_mobile_money_usage",
871
- VERIFY_MOBILE_MONEY: "link.verify_mobile_money",
872
- REVOKE_SESSION: "link.revoke_session",
873
- REVOKE_ALL_SESSIONS: "link.revoke_all_sessions"
874
- };
875
- var AUTH_MUTATION = {
876
- REQUEST_OTP: "auth.request_otp",
877
- VERIFY_OTP: "auth.verify_otp"
878
- };
879
- var CHECKOUT_MUTATION = {
880
- PROCESS: "checkout.process"
881
- };
882
- var PAYMENT_MUTATION = {
883
- SUBMIT_AUTHORIZATION: "payment.submit_authorization",
884
- CHECK_STATUS: "order.poll_payment_status"
885
- };
886
- var ORDER_MUTATION = {
887
- UPDATE_CUSTOMER: "order.update_order_customer"
888
- };
889
- var DEFAULT_CURRENCY = "GHS";
890
- var DEFAULT_COUNTRY = "GHA";
891
-
892
- // src/utils/paystack.ts
893
- async function openPaystackPopup(options, signal) {
894
- if (typeof window === "undefined") {
895
- return { success: false, error: "PROVIDER_UNAVAILABLE" };
896
- }
897
- let PaystackPop;
898
- try {
899
- const specifier = "@paystack/inline-js";
900
- const imported = await import(
901
- /* webpackIgnore: true */
902
- /* @vite-ignore */
903
- specifier
904
- );
905
- PaystackPop = imported.default;
906
- } catch {
907
- return { success: false, error: "PROVIDER_UNAVAILABLE" };
908
- }
909
- return new Promise((resolve) => {
910
- let settled = false;
911
- const resolveOnce = (result) => {
912
- if (settled) {
913
- return;
914
- }
915
- settled = true;
916
- if (signal) {
917
- signal.removeEventListener("abort", onAbort);
918
- }
919
- resolve(result);
920
- };
921
- const onAbort = () => {
922
- resolveOnce({ success: false, error: "CANCELLED" });
923
- };
924
- if (signal?.aborted) {
925
- resolveOnce({ success: false, error: "CANCELLED" });
926
- return;
927
- }
928
- if (signal) {
929
- signal.addEventListener("abort", onAbort, { once: true });
930
- }
931
- try {
932
- const popup = new PaystackPop();
933
- popup.newTransaction({
934
- key: options.key,
935
- email: options.email,
936
- amount: options.amount,
937
- currency: options.currency,
938
- reference: options.reference,
939
- accessCode: options.accessCode,
940
- onSuccess: (transaction) => {
941
- resolveOnce({
942
- success: true,
943
- reference: transaction.reference ?? options.reference
944
- });
945
- },
946
- onCancel: () => {
947
- resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
948
- },
949
- onError: (error) => {
950
- resolveOnce({
951
- success: false,
952
- error: error?.message || "PAYMENT_FAILED"
953
- });
954
- }
955
- });
956
- } catch {
957
- resolveOnce({ success: false, error: "POPUP_BLOCKED" });
958
- }
959
- });
960
- }
961
-
962
790
  // src/utils/checkout-resolver.ts
963
791
  var DEFAULT_POLL_INTERVAL_MS = 3e3;
964
792
  var DEFAULT_MAX_POLL_ATTEMPTS = 60;
965
793
  var MAX_CONSECUTIVE_NETWORK_ERRORS = 5;
966
- var CARD_PROVIDER_PAYSTACK = "paystack";
967
794
  var NEXT_ACTION_NONE = "none";
968
795
  var NEXT_ACTION_CARD_POPUP = "card_popup";
969
796
  var NEXT_ACTION_REDIRECT = "redirect";
970
797
  var NEXT_ACTION_AUTHORIZATION = "authorization";
971
- function normalizeCardPopupError(error) {
972
- if (!error || error === "PAYMENT_CANCELLED" || error === "CANCELLED") {
973
- return "PAYMENT_CANCELLED";
974
- }
975
- if (error === "PROVIDER_UNAVAILABLE") {
976
- return "PROVIDER_UNAVAILABLE";
977
- }
978
- if (error === "POPUP_BLOCKED") {
979
- return "POPUP_BLOCKED";
980
- }
981
- return "PAYMENT_FAILED";
982
- }
983
- function normalizeCardProvider(value) {
984
- const normalized = value?.trim().toLowerCase();
985
- return normalized && normalized.length > 0 ? normalized : CARD_PROVIDER_PAYSTACK;
986
- }
987
- async function openCardPopup(provider, checkoutResult, email, currency, signal) {
988
- if (typeof window === "undefined") {
989
- return { success: false, error: "PROVIDER_UNAVAILABLE" };
990
- }
991
- if (provider === CARD_PROVIDER_PAYSTACK) {
992
- return openPaystackPopup(
993
- {
994
- key: checkoutResult.public_key || "",
995
- email,
996
- accessCode: checkoutResult.client_secret,
997
- reference: checkoutResult.payment_reference || checkoutResult.client_secret,
998
- currency
999
- },
1000
- signal
1001
- );
1002
- }
1003
- return { success: false, error: "PROVIDER_UNAVAILABLE" };
1004
- }
1005
- var VALID_MOBILE_MONEY_PROVIDERS = new Set(Object.values(MOBILE_MONEY_PROVIDER));
798
+ var NEXT_ACTION_POLL = "poll";
799
+ var VALID_MOBILE_MONEY_PROVIDERS = new Set(Object.values(chunkCNN7Z2IP_js.MOBILE_MONEY_PROVIDER));
1006
800
  function isValidMobileMoneyProvider(value) {
1007
801
  return VALID_MOBILE_MONEY_PROVIDERS.has(value);
1008
802
  }
@@ -1018,6 +812,32 @@ function normalizeAuthorizationType(value) {
1018
812
  }
1019
813
  return void 0;
1020
814
  }
815
+ function normalizeAuthorizationSubmission(expectedType, submission) {
816
+ if (!submission || submission.type !== expectedType) {
817
+ throw new Error(`Expected ${expectedType} authorization details.`);
818
+ }
819
+ if (submission.type === "address") {
820
+ const address = submission.address.trim();
821
+ const city = submission.city.trim();
822
+ const state = submission.state.trim();
823
+ const zipCode = submission.zip_code.trim();
824
+ if (!address || !city || !state || !zipCode) {
825
+ throw new Error("Complete all authorization address fields.");
826
+ }
827
+ return {
828
+ type: "address",
829
+ address,
830
+ city,
831
+ state,
832
+ zip_code: zipCode
833
+ };
834
+ }
835
+ const value = submission.value.trim();
836
+ if (!value) {
837
+ throw new Error("Authorization value is required.");
838
+ }
839
+ return { type: submission.type, value };
840
+ }
1021
841
  function formatMoney(value) {
1022
842
  if (typeof value === "number" && Number.isFinite(value)) {
1023
843
  return value.toFixed(2);
@@ -1038,6 +858,27 @@ function isAbortError(error) {
1038
858
  }
1039
859
  return error instanceof Error && (error.name === "AbortError" || error.message === "CANCELLED");
1040
860
  }
861
+ function waitForAbortable(promise, signal) {
862
+ if (!signal) return promise;
863
+ if (signal.aborted) return Promise.reject(createAbortError());
864
+ return new Promise((resolve, reject) => {
865
+ const onAbort = () => {
866
+ signal.removeEventListener("abort", onAbort);
867
+ reject(createAbortError());
868
+ };
869
+ signal.addEventListener("abort", onAbort, { once: true });
870
+ promise.then(
871
+ (value) => {
872
+ signal.removeEventListener("abort", onAbort);
873
+ resolve(value);
874
+ },
875
+ (error) => {
876
+ signal.removeEventListener("abort", onAbort);
877
+ reject(error);
878
+ }
879
+ );
880
+ });
881
+ }
1041
882
  var CheckoutResolver = class {
1042
883
  constructor(options) {
1043
884
  this.client = options.client;
@@ -1045,6 +886,7 @@ var CheckoutResolver = class {
1045
886
  this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
1046
887
  this.maxPollAttempts = options.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;
1047
888
  this.onStatusChange = options.onStatusChange;
889
+ this.onAuthorizationUiChange = options.onAuthorizationUiChange;
1048
890
  this.onAuthorizationRequired = options.onAuthorizationRequired;
1049
891
  this.signal = options.signal;
1050
892
  this.returnUrl = options.returnUrl;
@@ -1063,116 +905,22 @@ var CheckoutResolver = class {
1063
905
  if (!checkoutResult.order_id) {
1064
906
  return this.fail("CHECKOUT_FAILED", "Checkout did not return an order ID.", false);
1065
907
  }
1066
- if (checkoutResult.next_action) {
1067
- return this.resolveNextAction(checkoutResult);
1068
- }
1069
- let latestCheckoutResult = checkoutResult;
1070
- let authorizationType = normalizeAuthorizationType(checkoutResult.authorization_type);
1071
- let paymentReference = checkoutResult.payment_reference;
1072
908
  if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
1073
909
  return this.finalizeSuccess(checkoutResult);
1074
910
  }
1075
- if (checkoutResult.client_secret && checkoutResult.public_key) {
1076
- const provider = normalizeCardProvider(checkoutResult.provider);
1077
- this.emit("awaiting_authorization", {
1078
- display_text: "Complete payment in the card authorization popup.",
1079
- order_id: checkoutResult.order_id,
1080
- order_number: checkoutResult.order_number
911
+ if (this.isFailureStatus(checkoutResult.payment_status)) {
912
+ const normalized = chunk7PDEX54K_js.normalizeStatusResponse({
913
+ status: checkoutResult.payment_status,
914
+ paid: false
1081
915
  });
1082
- if (!this.allowPopups) {
1083
- return this.fail(
1084
- "PROVIDER_UNAVAILABLE",
1085
- "Card payment popup is unavailable in this environment.",
1086
- false
1087
- );
1088
- }
1089
- const popupResult = await openCardPopup(
1090
- provider,
1091
- checkoutResult,
1092
- this.checkoutData.customer.email || "customer@cimplify.io",
1093
- this.getOrderCurrency(checkoutResult),
1094
- this.signal
916
+ return this.fail(
917
+ normalized.status.toUpperCase(),
918
+ checkoutResult.display_text || "Payment failed before confirmation.",
919
+ false
1095
920
  );
1096
- if (!popupResult.success) {
1097
- const popupError = normalizeCardPopupError(popupResult.error);
1098
- if (popupError === "POPUP_BLOCKED") {
1099
- return this.fail(
1100
- "POPUP_BLOCKED",
1101
- "Unable to open card payment popup. Please allow popups and try again.",
1102
- true
1103
- );
1104
- }
1105
- if (popupError === "PROVIDER_UNAVAILABLE") {
1106
- return this.fail(
1107
- "PROVIDER_UNAVAILABLE",
1108
- "Card payment provider is unavailable.",
1109
- false
1110
- );
1111
- }
1112
- return this.fail(
1113
- "PAYMENT_CANCELLED",
1114
- "Card payment was cancelled before completion.",
1115
- true
1116
- );
1117
- }
1118
- paymentReference = popupResult.reference || paymentReference;
1119
- }
1120
- if (checkoutResult.authorization_url) {
1121
- if (typeof window !== "undefined" && this.returnUrl) {
1122
- window.location.assign(checkoutResult.authorization_url);
1123
- return this.fail(
1124
- "REDIRECT_REQUIRED",
1125
- "Redirecting to complete payment authorization.",
1126
- true
1127
- );
1128
- }
1129
- if (typeof window !== "undefined") {
1130
- const popup = window.open(
1131
- checkoutResult.authorization_url,
1132
- "cimplify-auth",
1133
- "width=520,height=760,noopener,noreferrer"
1134
- );
1135
- if (!popup) {
1136
- return this.fail(
1137
- "POPUP_BLOCKED",
1138
- "Authorization popup was blocked. Please allow popups and retry.",
1139
- true
1140
- );
1141
- }
1142
- }
1143
- }
1144
- if (checkoutResult.requires_authorization || chunkWMEC5JWN_js.isPaymentStatusRequiresAction(checkoutResult.payment_status)) {
1145
- const authorization = await this.handleAuthorization({
1146
- authorizationType,
1147
- paymentReference,
1148
- displayText: checkoutResult.display_text,
1149
- provider: checkoutResult.provider
1150
- });
1151
- if (!authorization.ok) {
1152
- return authorization.result;
1153
- }
1154
- if (authorization.value) {
1155
- latestCheckoutResult = authorization.value;
1156
- paymentReference = authorization.value.payment_reference || paymentReference;
1157
- authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
1158
- if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1159
- return this.finalizeSuccess(authorization.value);
1160
- }
1161
- if (this.isFailureStatus(authorization.value.payment_status)) {
1162
- return this.fail(
1163
- "AUTHORIZATION_FAILED",
1164
- authorization.value.display_text || "Payment authorization failed.",
1165
- false
1166
- );
1167
- }
1168
- }
1169
921
  }
1170
- return this.pollUntilTerminal({
1171
- orderId: checkoutResult.order_id,
1172
- latestCheckoutResult,
1173
- paymentReference,
1174
- authorizationType
1175
- });
922
+ const nextAction = checkoutResult.next_action ?? this.deriveLegacyNextAction(checkoutResult);
923
+ return this.resolveNextAction({ ...checkoutResult, next_action: nextAction });
1176
924
  } catch (error) {
1177
925
  if (isAbortError(error)) {
1178
926
  return this.fail("CANCELLED", "Checkout was cancelled.", true);
@@ -1181,99 +929,172 @@ var CheckoutResolver = class {
1181
929
  return this.fail("CHECKOUT_FAILED", message, true);
1182
930
  }
1183
931
  }
1184
- async resolveNextAction(checkoutResult) {
1185
- const action = checkoutResult.next_action;
932
+ deriveLegacyNextAction(checkoutResult) {
933
+ if (checkoutResult.client_secret && checkoutResult.public_key) {
934
+ return {
935
+ type: NEXT_ACTION_CARD_POPUP,
936
+ provider: checkoutResult.provider?.trim().toLowerCase() || "",
937
+ client_secret: checkoutResult.client_secret,
938
+ public_key: checkoutResult.public_key
939
+ };
940
+ }
941
+ if (checkoutResult.authorization_url) {
942
+ return {
943
+ type: NEXT_ACTION_REDIRECT,
944
+ authorization_url: checkoutResult.authorization_url
945
+ };
946
+ }
947
+ if (checkoutResult.requires_authorization && checkoutResult.authorization_type) {
948
+ return {
949
+ type: NEXT_ACTION_AUTHORIZATION,
950
+ authorization_type: checkoutResult.authorization_type,
951
+ display_text: checkoutResult.display_text
952
+ };
953
+ }
954
+ return { type: NEXT_ACTION_POLL };
955
+ }
956
+ async runCardPopup(action, checkoutResult) {
957
+ if (typeof action.provider !== "string" || !action.provider.trim() || typeof action.client_secret !== "string" || !action.client_secret.trim() || typeof action.public_key !== "string" || !action.public_key.trim()) {
958
+ return {
959
+ ok: false,
960
+ result: this.fail(
961
+ "INVALID_CARD_AUTHORIZATION",
962
+ "The payment provider returned an incomplete card authorization response.",
963
+ false
964
+ )
965
+ };
966
+ }
967
+ this.emit("awaiting_authorization", {
968
+ display_text: "Complete payment in the card authorization window.",
969
+ order_id: checkoutResult.order_id,
970
+ order_number: checkoutResult.order_number,
971
+ provider: action.provider
972
+ });
973
+ if (!this.allowPopups) {
974
+ return {
975
+ ok: false,
976
+ result: this.fail(
977
+ "PROVIDER_UNAVAILABLE",
978
+ "Card authorization UI is unavailable in this environment.",
979
+ false
980
+ )
981
+ };
982
+ }
983
+ const currency = this.getOrderCurrency(checkoutResult);
984
+ if (!currency) {
985
+ return {
986
+ ok: false,
987
+ result: this.fail("INVALID_CURRENCY", "Payment currency is missing or unsupported.", false)
988
+ };
989
+ }
990
+ let outcome;
991
+ try {
992
+ this.notifyAuthorizationUiChange(true);
993
+ outcome = await this.client.checkout.authorizeCardPopup(action, {
994
+ email: this.checkoutData.customer.email || "customer@cimplify.io",
995
+ currency,
996
+ reference: checkoutResult.payment_reference,
997
+ returnUrl: this.returnUrl,
998
+ signal: this.signal
999
+ });
1000
+ } finally {
1001
+ this.notifyAuthorizationUiChange(false);
1002
+ }
1003
+ if (outcome.status === "submitted") {
1004
+ return { ok: true, reference: outcome.reference };
1005
+ }
1006
+ if (outcome.status === "cancelled") {
1007
+ return {
1008
+ ok: false,
1009
+ result: this.fail(
1010
+ "PAYMENT_CANCELLED",
1011
+ "Card payment was cancelled before submission.",
1012
+ true
1013
+ )
1014
+ };
1015
+ }
1016
+ return {
1017
+ ok: false,
1018
+ result: this.fail(outcome.code, outcome.message, outcome.recoverable)
1019
+ };
1020
+ }
1021
+ async resolveNextAction(checkoutResult, resolutionState = {
1022
+ authorizationSteps: 0,
1023
+ pollAttempts: 0,
1024
+ consecutivePollErrors: 0
1025
+ }) {
1186
1026
  if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
1187
1027
  return this.finalizeSuccess(checkoutResult);
1188
1028
  }
1189
1029
  let latestCheckoutResult = checkoutResult;
1190
1030
  let paymentReference = checkoutResult.payment_reference;
1191
- switch (action.type) {
1192
- case NEXT_ACTION_NONE:
1193
- return this.finalizeSuccess(checkoutResult);
1194
- case NEXT_ACTION_CARD_POPUP: {
1195
- const provider = normalizeCardProvider(action.provider);
1196
- this.emit("awaiting_authorization", {
1197
- display_text: "Complete payment in the card authorization popup.",
1198
- order_id: checkoutResult.order_id,
1199
- order_number: checkoutResult.order_number
1200
- });
1201
- if (!this.allowPopups) {
1202
- return this.fail(
1203
- "PROVIDER_UNAVAILABLE",
1204
- "Card payment popup is unavailable in this environment.",
1205
- false
1206
- );
1031
+ let action = checkoutResult.next_action;
1032
+ actionLoop: while (true) {
1033
+ this.ensureNotAborted();
1034
+ switch (action.type) {
1035
+ case NEXT_ACTION_NONE:
1036
+ break actionLoop;
1037
+ case NEXT_ACTION_CARD_POPUP: {
1038
+ const limitFailure = this.consumeAuthorizationStep(resolutionState);
1039
+ if (limitFailure) return limitFailure;
1040
+ const popup = await this.runCardPopup(action, latestCheckoutResult);
1041
+ if (!popup.ok) return popup.result;
1042
+ paymentReference = popup.reference || paymentReference;
1043
+ break actionLoop;
1207
1044
  }
1208
- const popupResult = await openCardPopup(
1209
- provider,
1210
- checkoutResult,
1211
- this.checkoutData.customer.email || "customer@cimplify.io",
1212
- this.getOrderCurrency(checkoutResult),
1213
- this.signal
1214
- );
1215
- if (!popupResult.success) {
1216
- const popupError = normalizeCardPopupError(popupResult.error);
1217
- if (popupError === "POPUP_BLOCKED") {
1218
- return this.fail(
1219
- "POPUP_BLOCKED",
1220
- "Unable to open card payment popup. Please allow popups and try again.",
1221
- true
1222
- );
1223
- }
1224
- if (popupError === "PROVIDER_UNAVAILABLE") {
1045
+ case NEXT_ACTION_REDIRECT: {
1046
+ const limitFailure = this.consumeAuthorizationStep(resolutionState);
1047
+ if (limitFailure) return limitFailure;
1048
+ const authorizationUrl = chunk7PDEX54K_js.paymentAuthorizationUrl(action.authorization_url);
1049
+ if (!authorizationUrl) {
1225
1050
  return this.fail(
1226
- "PROVIDER_UNAVAILABLE",
1227
- "Card payment provider is unavailable.",
1051
+ "INVALID_REDIRECT_AUTHORIZATION",
1052
+ "The payment provider returned an invalid authorization URL.",
1228
1053
  false
1229
1054
  );
1230
1055
  }
1231
- return this.fail(
1232
- "PAYMENT_CANCELLED",
1233
- "Card payment was cancelled before completion.",
1234
- true
1235
- );
1236
- }
1237
- paymentReference = popupResult.reference || paymentReference;
1238
- break;
1239
- }
1240
- case NEXT_ACTION_REDIRECT: {
1241
- if (typeof window !== "undefined" && this.returnUrl) {
1242
- window.location.assign(action.authorization_url);
1243
- return this.fail(
1244
- "REDIRECT_REQUIRED",
1245
- "Redirecting to complete payment authorization.",
1246
- true
1247
- );
1248
- }
1249
- if (typeof window !== "undefined") {
1250
- const popup = window.open(
1251
- action.authorization_url,
1252
- "cimplify-auth",
1253
- "width=520,height=760,noopener,noreferrer"
1254
- );
1255
- if (!popup) {
1056
+ if (typeof window !== "undefined" && this.returnUrl) {
1057
+ window.location.assign(authorizationUrl);
1256
1058
  return this.fail(
1257
- "POPUP_BLOCKED",
1258
- "Authorization popup was blocked. Please allow popups and retry.",
1059
+ "REDIRECT_REQUIRED",
1060
+ "Redirecting to complete payment authorization.",
1259
1061
  true
1260
1062
  );
1261
1063
  }
1064
+ if (typeof window !== "undefined") {
1065
+ const popup = window.open(
1066
+ authorizationUrl,
1067
+ "cimplify-auth",
1068
+ "width=520,height=760,noopener,noreferrer"
1069
+ );
1070
+ if (!popup) {
1071
+ return this.fail(
1072
+ "POPUP_BLOCKED",
1073
+ "Authorization popup was blocked. Please allow popups and retry.",
1074
+ true
1075
+ );
1076
+ }
1077
+ }
1078
+ break actionLoop;
1262
1079
  }
1263
- break;
1264
- }
1265
- case NEXT_ACTION_AUTHORIZATION: {
1266
- const authType = normalizeAuthorizationType(action.authorization_type);
1267
- const authorization = await this.handleAuthorization({
1268
- authorizationType: authType,
1269
- paymentReference,
1270
- displayText: action.display_text,
1271
- provider: checkoutResult.provider
1272
- });
1273
- if (!authorization.ok) {
1274
- return authorization.result;
1275
- }
1276
- if (authorization.value) {
1080
+ case NEXT_ACTION_AUTHORIZATION: {
1081
+ const limitFailure = this.consumeAuthorizationStep(resolutionState);
1082
+ if (limitFailure) return limitFailure;
1083
+ const authType = normalizeAuthorizationType(action.authorization_type);
1084
+ const authorization = await this.handleAuthorization({
1085
+ authorizationType: authType,
1086
+ paymentId: latestCheckoutResult.payment_id,
1087
+ displayText: action.display_text,
1088
+ provider: latestCheckoutResult.provider,
1089
+ orderId: latestCheckoutResult.order_id,
1090
+ orderNumber: latestCheckoutResult.order_number
1091
+ });
1092
+ if (!authorization.ok) {
1093
+ return authorization.result;
1094
+ }
1095
+ if (!authorization.value) {
1096
+ break actionLoop;
1097
+ }
1277
1098
  latestCheckoutResult = authorization.value;
1278
1099
  paymentReference = authorization.value.payment_reference || paymentReference;
1279
1100
  if (this.isSuccessfulStatus(authorization.value.payment_status)) {
@@ -1286,24 +1107,45 @@ var CheckoutResolver = class {
1286
1107
  false
1287
1108
  );
1288
1109
  }
1110
+ action = authorization.value.next_action ?? this.deriveLegacyNextAction(authorization.value);
1111
+ continue actionLoop;
1289
1112
  }
1290
- break;
1113
+ case NEXT_ACTION_POLL:
1114
+ break actionLoop;
1115
+ default:
1116
+ return this.fail(
1117
+ "UNSUPPORTED_NEXT_ACTION",
1118
+ "The payment provider requested an unsupported authorization action.",
1119
+ false
1120
+ );
1291
1121
  }
1292
1122
  }
1293
1123
  return this.pollUntilTerminal({
1294
- orderId: checkoutResult.order_id,
1124
+ orderId: latestCheckoutResult.order_id,
1295
1125
  latestCheckoutResult,
1296
1126
  paymentReference,
1297
- authorizationType: normalizeAuthorizationType(checkoutResult.authorization_type)
1127
+ resolutionState
1298
1128
  });
1299
1129
  }
1130
+ consumeAuthorizationStep(resolutionState) {
1131
+ if (resolutionState.authorizationSteps >= chunkCNN7Z2IP_js.MAX_PAYMENT_AUTHORIZATION_STEPS) {
1132
+ return this.fail(
1133
+ "AUTHORIZATION_LIMIT_EXCEEDED",
1134
+ "The payment provider requested too many authorization steps.",
1135
+ false
1136
+ );
1137
+ }
1138
+ resolutionState.authorizationSteps += 1;
1139
+ return void 0;
1140
+ }
1300
1141
  async pollUntilTerminal(input) {
1301
- let consecutiveErrors = 0;
1302
- let latestCheckoutResult = input.latestCheckoutResult;
1142
+ const latestCheckoutResult = input.latestCheckoutResult;
1303
1143
  let paymentReference = input.paymentReference;
1304
- let authorizationType = input.authorizationType;
1305
- for (let attempt = 0; attempt < this.maxPollAttempts; attempt++) {
1144
+ const { resolutionState } = input;
1145
+ while (resolutionState.pollAttempts < this.maxPollAttempts) {
1306
1146
  this.ensureNotAborted();
1147
+ const attempt = resolutionState.pollAttempts;
1148
+ resolutionState.pollAttempts += 1;
1307
1149
  this.emit("polling", {
1308
1150
  poll_attempt: attempt,
1309
1151
  max_poll_attempts: this.maxPollAttempts,
@@ -1312,8 +1154,8 @@ var CheckoutResolver = class {
1312
1154
  });
1313
1155
  const statusResult = await this.client.checkout.pollPaymentStatus(input.orderId);
1314
1156
  if (!statusResult.ok) {
1315
- consecutiveErrors += 1;
1316
- if (consecutiveErrors >= MAX_CONSECUTIVE_NETWORK_ERRORS) {
1157
+ resolutionState.consecutivePollErrors += 1;
1158
+ if (resolutionState.consecutivePollErrors >= MAX_CONSECUTIVE_NETWORK_ERRORS) {
1317
1159
  return this.fail(
1318
1160
  "NETWORK_ERROR",
1319
1161
  "Unable to confirm payment due to repeated network errors.",
@@ -1323,59 +1165,52 @@ var CheckoutResolver = class {
1323
1165
  await this.wait(this.pollIntervalMs);
1324
1166
  continue;
1325
1167
  }
1326
- consecutiveErrors = 0;
1168
+ resolutionState.consecutivePollErrors = 0;
1327
1169
  if (statusResult.value.reference) {
1328
1170
  paymentReference = statusResult.value.reference;
1329
1171
  }
1330
- const normalized = chunkWMEC5JWN_js.normalizeStatusResponse(statusResult.value);
1331
- if (normalized.paid || chunkWMEC5JWN_js.isPaymentStatusSuccess(normalized.status)) {
1332
- return this.finalizeSuccess(latestCheckoutResult);
1172
+ const normalized = chunk7PDEX54K_js.normalizeStatusResponse(statusResult.value);
1173
+ if (normalized.paid || chunk7PDEX54K_js.isPaymentStatusSuccess(normalized.status)) {
1174
+ return this.finalizeSuccess({
1175
+ ...latestCheckoutResult,
1176
+ payment_status: normalized.status,
1177
+ payment_reference: paymentReference || latestCheckoutResult.payment_reference
1178
+ });
1333
1179
  }
1334
- if (chunkWMEC5JWN_js.isPaymentStatusFailure(normalized.status)) {
1180
+ if (chunk7PDEX54K_js.isPaymentStatusFailure(normalized.status)) {
1335
1181
  return this.fail(
1336
1182
  normalized.status ? normalized.status.toUpperCase() : "PAYMENT_FAILED",
1337
1183
  normalized.message || "Payment failed during confirmation.",
1338
1184
  false
1339
1185
  );
1340
1186
  }
1341
- if (chunkWMEC5JWN_js.isPaymentStatusRequiresAction(normalized.status)) {
1342
- const authorization = await this.handleAuthorization({
1343
- authorizationType,
1344
- paymentReference,
1345
- displayText: normalized.message || latestCheckoutResult.display_text,
1346
- provider: latestCheckoutResult.provider
1347
- });
1348
- if (!authorization.ok) {
1349
- return authorization.result;
1350
- }
1351
- if (authorization.value) {
1352
- latestCheckoutResult = authorization.value;
1353
- paymentReference = authorization.value.payment_reference || paymentReference;
1354
- authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
1355
- if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1356
- return this.finalizeSuccess(authorization.value);
1357
- }
1358
- if (this.isFailureStatus(authorization.value.payment_status)) {
1359
- return this.fail(
1360
- "AUTHORIZATION_FAILED",
1361
- authorization.value.display_text || "Payment authorization failed.",
1362
- false
1363
- );
1364
- }
1187
+ if (chunk7PDEX54K_js.isPaymentStatusRequiresAction(normalized.status)) {
1188
+ const authorizationType = latestCheckoutResult.authorization_type;
1189
+ const paymentId = latestCheckoutResult.payment_id;
1190
+ if (!authorizationType || !normalizeAuthorizationType(authorizationType) || !paymentId) {
1191
+ return this.fail(
1192
+ "INVALID_AUTHORIZATION_RESPONSE",
1193
+ "The payment provider returned an incomplete authorization response.",
1194
+ false
1195
+ );
1365
1196
  }
1197
+ return this.resolveNextAction(
1198
+ {
1199
+ ...latestCheckoutResult,
1200
+ payment_status: normalized.status,
1201
+ payment_reference: paymentReference,
1202
+ display_text: normalized.message || latestCheckoutResult.display_text,
1203
+ next_action: {
1204
+ type: NEXT_ACTION_AUTHORIZATION,
1205
+ authorization_type: authorizationType,
1206
+ display_text: normalized.message || latestCheckoutResult.display_text
1207
+ }
1208
+ },
1209
+ resolutionState
1210
+ );
1366
1211
  }
1367
1212
  await this.wait(this.pollIntervalMs);
1368
1213
  }
1369
- try {
1370
- const finalResult = await this.client.checkout.pollPaymentStatus(input.orderId);
1371
- if (finalResult.ok) {
1372
- const normalized = chunkWMEC5JWN_js.normalizeStatusResponse(finalResult.value);
1373
- if (normalized.paid || chunkWMEC5JWN_js.isPaymentStatusSuccess(normalized.status)) {
1374
- return this.finalizeSuccess(latestCheckoutResult);
1375
- }
1376
- }
1377
- } catch {
1378
- }
1379
1214
  return this.fail(
1380
1215
  "PAYMENT_TIMEOUT",
1381
1216
  "Payment confirmation timed out. Please retry checkout.",
@@ -1384,35 +1219,61 @@ var CheckoutResolver = class {
1384
1219
  }
1385
1220
  async handleAuthorization(input) {
1386
1221
  const authorizationType = input.authorizationType;
1387
- const paymentReference = input.paymentReference;
1388
- if (!authorizationType || !paymentReference) {
1389
- return { ok: true };
1222
+ const paymentId = input.paymentId;
1223
+ if (!authorizationType || !paymentId) {
1224
+ return {
1225
+ ok: false,
1226
+ result: this.fail(
1227
+ "INVALID_AUTHORIZATION_RESPONSE",
1228
+ "The payment provider returned an incomplete authorization response.",
1229
+ false
1230
+ )
1231
+ };
1390
1232
  }
1391
1233
  this.emit("awaiting_authorization", {
1392
1234
  authorization_type: authorizationType,
1393
1235
  display_text: input.displayText,
1394
- provider: input.provider
1236
+ provider: input.provider,
1237
+ order_id: input.orderId,
1238
+ order_number: input.orderNumber
1395
1239
  });
1396
- let authorizationResult;
1397
- const submit = async (code) => {
1240
+ let authorizationRequest;
1241
+ const submit = (authorization) => {
1398
1242
  this.ensureNotAborted();
1399
- const trimmedCode = code.trim();
1400
- if (!trimmedCode) {
1401
- throw new Error("Authorization code is required.");
1243
+ let normalizedAuthorization;
1244
+ try {
1245
+ normalizedAuthorization = normalizeAuthorizationSubmission(
1246
+ authorizationType,
1247
+ authorization
1248
+ );
1249
+ } catch (error) {
1250
+ return Promise.reject(error);
1402
1251
  }
1403
- const submitResult = await this.client.checkout.submitAuthorization({
1404
- reference: paymentReference,
1405
- auth_type: authorizationType,
1406
- value: trimmedCode
1407
- });
1408
- if (!submitResult.ok) {
1409
- throw new Error(submitResult.error.message || "Authorization failed.");
1252
+ if (!authorizationRequest) {
1253
+ const request = this.client.checkout.submitAuthorization({
1254
+ payment_id: paymentId,
1255
+ authorization: normalizedAuthorization
1256
+ }).then((submitResult) => {
1257
+ if (!submitResult.ok) {
1258
+ throw new Error(submitResult.error.message || "Authorization failed.");
1259
+ }
1260
+ return submitResult.value;
1261
+ });
1262
+ authorizationRequest = request;
1263
+ void request.catch(() => {
1264
+ if (authorizationRequest === request) {
1265
+ authorizationRequest = void 0;
1266
+ }
1267
+ });
1410
1268
  }
1411
- authorizationResult = submitResult.value;
1269
+ return authorizationRequest.then(() => void 0);
1412
1270
  };
1413
1271
  try {
1414
1272
  if (this.onAuthorizationRequired) {
1415
- await this.onAuthorizationRequired(authorizationType, submit);
1273
+ await waitForAbortable(
1274
+ Promise.resolve(this.onAuthorizationRequired(authorizationType, submit)),
1275
+ this.signal
1276
+ );
1416
1277
  } else {
1417
1278
  return {
1418
1279
  ok: false,
@@ -1423,6 +1284,20 @@ var CheckoutResolver = class {
1423
1284
  )
1424
1285
  };
1425
1286
  }
1287
+ if (!authorizationRequest) {
1288
+ return {
1289
+ ok: false,
1290
+ result: this.fail(
1291
+ "AUTHORIZATION_NOT_SUBMITTED",
1292
+ "The authorization callback returned without submitting a value.",
1293
+ true
1294
+ )
1295
+ };
1296
+ }
1297
+ return {
1298
+ ok: true,
1299
+ value: await waitForAbortable(authorizationRequest, this.signal)
1300
+ };
1426
1301
  } catch (error) {
1427
1302
  if (isAbortError(error)) {
1428
1303
  return {
@@ -1436,12 +1311,12 @@ var CheckoutResolver = class {
1436
1311
  result: this.fail("AUTHORIZATION_FAILED", message, true)
1437
1312
  };
1438
1313
  }
1439
- return {
1440
- ok: true,
1441
- value: authorizationResult
1442
- };
1443
1314
  }
1444
1315
  async finalizeSuccess(checkoutResult) {
1316
+ const currency = this.getOrderCurrency(checkoutResult);
1317
+ if (!currency) {
1318
+ return this.fail("INVALID_CURRENCY", "Payment currency is missing or unsupported.", false);
1319
+ }
1445
1320
  this.emit("finalizing", {
1446
1321
  order_id: checkoutResult.order_id,
1447
1322
  order_number: checkoutResult.order_number
@@ -1458,7 +1333,7 @@ var CheckoutResolver = class {
1458
1333
  order_number: checkoutResult.order_number || checkoutResult.order_id,
1459
1334
  status: checkoutResult.payment_status || "paid",
1460
1335
  total: this.getOrderTotal(checkoutResult),
1461
- currency: this.getOrderCurrency(checkoutResult)
1336
+ currency
1462
1337
  },
1463
1338
  enrolled_in_link: enrolledInLink
1464
1339
  };
@@ -1510,7 +1385,7 @@ var CheckoutResolver = class {
1510
1385
  };
1511
1386
  }
1512
1387
  getEnrollmentMobileMoney() {
1513
- if (this.paymentData?.type === "mobile_money" && this.paymentData.phone_number && this.paymentData.provider && isValidMobileMoneyProvider(this.paymentData.provider)) {
1388
+ if (this.paymentData?.type === chunkCNN7Z2IP_js.PAYMENT_METHOD.MOBILE_MONEY && this.paymentData.phone_number && this.paymentData.provider && isValidMobileMoneyProvider(this.paymentData.provider)) {
1514
1389
  return {
1515
1390
  phone_number: this.paymentData.phone_number,
1516
1391
  provider: this.paymentData.provider,
@@ -1537,15 +1412,15 @@ var CheckoutResolver = class {
1537
1412
  }
1538
1413
  getOrderCurrency(checkoutResult) {
1539
1414
  if (checkoutResult.fx?.pay_currency) {
1540
- return checkoutResult.fx.pay_currency;
1541
- }
1542
- if (this.cartCurrency) {
1543
- return this.cartCurrency;
1415
+ const currency2 = checkoutResult.fx.pay_currency.trim().toUpperCase();
1416
+ return chunk4BKOKHE3_js.isSupportedCurrency(currency2) ? currency2 : void 0;
1544
1417
  }
1545
1418
  if (this.checkoutData.pay_currency) {
1546
- return this.checkoutData.pay_currency;
1419
+ const currency2 = this.checkoutData.pay_currency.trim().toUpperCase();
1420
+ return chunk4BKOKHE3_js.isSupportedCurrency(currency2) ? currency2 : void 0;
1547
1421
  }
1548
- return "GHS";
1422
+ const currency = this.cartCurrency.trim().toUpperCase();
1423
+ return chunk4BKOKHE3_js.isSupportedCurrency(currency) ? currency : void 0;
1549
1424
  }
1550
1425
  emit(status, context = {}) {
1551
1426
  if (!this.onStatusChange) {
@@ -1556,6 +1431,12 @@ var CheckoutResolver = class {
1556
1431
  } catch {
1557
1432
  }
1558
1433
  }
1434
+ notifyAuthorizationUiChange(open) {
1435
+ try {
1436
+ this.onAuthorizationUiChange?.(open);
1437
+ } catch {
1438
+ }
1439
+ }
1559
1440
  fail(code, message, recoverable) {
1560
1441
  this.emit("failed", {
1561
1442
  display_text: message
@@ -1570,12 +1451,12 @@ var CheckoutResolver = class {
1570
1451
  };
1571
1452
  }
1572
1453
  isSuccessfulStatus(status) {
1573
- const normalized = chunkWMEC5JWN_js.normalizeStatusResponse({ status, paid: false });
1574
- return normalized.paid || chunkWMEC5JWN_js.isPaymentStatusSuccess(normalized.status);
1454
+ const normalized = chunk7PDEX54K_js.normalizeStatusResponse({ status, paid: false });
1455
+ return normalized.paid || chunk7PDEX54K_js.isPaymentStatusSuccess(normalized.status);
1575
1456
  }
1576
1457
  isFailureStatus(status) {
1577
- const normalized = chunkWMEC5JWN_js.normalizeStatusResponse({ status, paid: false });
1578
- return chunkWMEC5JWN_js.isPaymentStatusFailure(normalized.status);
1458
+ const normalized = chunk7PDEX54K_js.normalizeStatusResponse({ status, paid: false });
1459
+ return chunk7PDEX54K_js.isPaymentStatusFailure(normalized.status);
1579
1460
  }
1580
1461
  ensureNotAborted() {
1581
1462
  if (this.signal?.aborted) {
@@ -1620,20 +1501,57 @@ function isValidCustomerName(name) {
1620
1501
  function isOfflinePaymentMethod(method) {
1621
1502
  return method === "cash" || method === "offline";
1622
1503
  }
1504
+ function notifyCheckoutStatus(observer, status, context = {}) {
1505
+ try {
1506
+ observer?.(status, context);
1507
+ } catch {
1508
+ }
1509
+ }
1510
+ var MAX_STOREFRONT_COLLECTION_OPTIONS = 32;
1511
+ function normalizeCollectionOptions(value, expectedCurrency) {
1512
+ if (!value || typeof value !== "object") return null;
1513
+ const response = value;
1514
+ if (response.presentment_currency !== expectedCurrency || !Array.isArray(response.options)) {
1515
+ return null;
1516
+ }
1517
+ if (response.options.length > MAX_STOREFRONT_COLLECTION_OPTIONS) return null;
1518
+ const options = [];
1519
+ const seen = /* @__PURE__ */ new Set();
1520
+ for (const candidate of response.options) {
1521
+ if (!candidate || typeof candidate !== "object") continue;
1522
+ const option = candidate;
1523
+ if (option.method === chunkCNN7Z2IP_js.PAYMENT_METHOD.CARD && option.network == null) {
1524
+ if (!seen.has(chunkCNN7Z2IP_js.PAYMENT_METHOD.CARD)) {
1525
+ seen.add(chunkCNN7Z2IP_js.PAYMENT_METHOD.CARD);
1526
+ options.push({ method: chunkCNN7Z2IP_js.PAYMENT_METHOD.CARD, network: null });
1527
+ }
1528
+ continue;
1529
+ }
1530
+ if (option.method !== chunkCNN7Z2IP_js.PAYMENT_METHOD.MOBILE_MONEY || typeof option.network !== "string" || !option.network.trim()) {
1531
+ continue;
1532
+ }
1533
+ const network = option.network.trim().toLowerCase();
1534
+ const key = `${chunkCNN7Z2IP_js.PAYMENT_METHOD.MOBILE_MONEY}:${network}`;
1535
+ if (seen.has(key)) continue;
1536
+ seen.add(key);
1537
+ options.push({ method: chunkCNN7Z2IP_js.PAYMENT_METHOD.MOBILE_MONEY, network });
1538
+ }
1539
+ return { presentment_currency: expectedCurrency, options };
1540
+ }
1623
1541
  var CheckoutService = class {
1624
1542
  constructor(client) {
1625
1543
  this.client = client;
1626
1544
  }
1627
1545
  validateCheckoutData(data) {
1628
1546
  if (isOfflinePaymentMethod(data.payment_method)) {
1629
- return new chunkNIOUSKQQ_js.CimplifyError(
1547
+ return new chunk4BKOKHE3_js.CimplifyError(
1630
1548
  "INVALID_PAYMENT_METHOD",
1631
1549
  "Offline payment isn't available for online checkout. Please pay by card or mobile money.",
1632
1550
  false
1633
1551
  );
1634
1552
  }
1635
1553
  if (!isValidCustomerName(data.customer?.name)) {
1636
- return new chunkNIOUSKQQ_js.CimplifyError(
1554
+ return new chunk4BKOKHE3_js.CimplifyError(
1637
1555
  "INVALID_CUSTOMER_NAME",
1638
1556
  "Enter your name, not a phone number.",
1639
1557
  false
@@ -1645,6 +1563,40 @@ var CheckoutService = class {
1645
1563
  const token = this.client.getOrderToken(orderId);
1646
1564
  return token ? `?token=${encodeURIComponent(token)}` : "";
1647
1565
  }
1566
+ /** Available storefront collection methods for one presentment currency. */
1567
+ async collectionOptions(currency) {
1568
+ const normalized = currency.trim().toUpperCase();
1569
+ if (!chunk4BKOKHE3_js.isSupportedCurrency(normalized)) {
1570
+ return err(
1571
+ new chunk4BKOKHE3_js.CimplifyError(
1572
+ chunk4BKOKHE3_js.ErrorCode.UNSUPPORTED_CURRENCY,
1573
+ "No online payment method is available for this currency.",
1574
+ false
1575
+ )
1576
+ );
1577
+ }
1578
+ const response = await safe(
1579
+ this.client.get(
1580
+ `/api/v1/payments/collection-options?currency=${encodeURIComponent(normalized)}`
1581
+ )
1582
+ );
1583
+ if (!response.ok) return response;
1584
+ const options = normalizeCollectionOptions(response.value, normalized);
1585
+ return options ? ok(options) : err(
1586
+ new chunk4BKOKHE3_js.CimplifyError(
1587
+ chunk4BKOKHE3_js.ErrorCode.PAYMENT_CAPABILITY_MISMATCH,
1588
+ "Payment methods could not be verified for this currency.",
1589
+ false
1590
+ )
1591
+ );
1592
+ }
1593
+ /**
1594
+ * Run provider-owned UI for a backend `card_popup` action. A submitted
1595
+ * outcome is not proof of payment; callers must poll/verify with Cimplify.
1596
+ */
1597
+ authorizeCardPopup(action, options) {
1598
+ return this.client.authorizationAdapters.authorize(action, options);
1599
+ }
1648
1600
  async process(data) {
1649
1601
  const invalid = this.validateCheckoutData(data);
1650
1602
  if (invalid) {
@@ -1668,14 +1620,14 @@ var CheckoutService = class {
1668
1620
  if (result.value.bill_token) {
1669
1621
  this.client.setOrderToken(result.value.order_id, result.value.bill_token);
1670
1622
  }
1671
- } else if (result.error.code !== chunkNIOUSKQQ_js.ErrorCode.NETWORK_ERROR && result.error.code !== chunkNIOUSKQQ_js.ErrorCode.TIMEOUT) {
1623
+ } else if (result.error.code !== chunk4BKOKHE3_js.ErrorCode.NETWORK_ERROR && result.error.code !== chunk4BKOKHE3_js.ErrorCode.TIMEOUT) {
1672
1624
  clearIntentIdempotencyKey(intentScope);
1673
1625
  }
1674
1626
  return result;
1675
1627
  }
1676
1628
  async initializePayment(_orderId, _method) {
1677
1629
  return err(
1678
- new chunkNIOUSKQQ_js.CimplifyError(
1630
+ new chunk4BKOKHE3_js.CimplifyError(
1679
1631
  "NOT_IMPLEMENTED",
1680
1632
  "initializePayment is not available in REST mode.",
1681
1633
  false
@@ -1688,12 +1640,18 @@ var CheckoutService = class {
1688
1640
  async pollPaymentStatus(orderId) {
1689
1641
  const encodedId = encodeURIComponent(orderId);
1690
1642
  const tokenParam = this.orderTokenParam(orderId);
1691
- return safe(this.client.get(`/api/v1/orders/${encodedId}/payment-status${tokenParam}`));
1643
+ return safe(
1644
+ this.client.get(
1645
+ `/api/v1/orders/${encodedId}/payment-status${tokenParam}`
1646
+ )
1647
+ );
1692
1648
  }
1693
1649
  async updateOrderCustomer(orderId, customer) {
1694
1650
  const encodedId = encodeURIComponent(orderId);
1695
1651
  const tokenParam = this.orderTokenParam(orderId);
1696
- return safe(this.client.post(`/api/v1/orders/${encodedId}/customer${tokenParam}`, customer));
1652
+ return safe(
1653
+ this.client.post(`/api/v1/orders/${encodedId}/customer${tokenParam}`, customer)
1654
+ );
1697
1655
  }
1698
1656
  async verifyPayment(orderId) {
1699
1657
  const encodedId = encodeURIComponent(orderId);
@@ -1701,7 +1659,7 @@ var CheckoutService = class {
1701
1659
  return safe(this.client.post(`/api/v1/orders/${encodedId}/verify-payment${tokenParam}`));
1702
1660
  }
1703
1661
  async processAndResolve(data) {
1704
- data.on_status_change?.("preparing", {});
1662
+ notifyCheckoutStatus(data.on_status_change, "preparing");
1705
1663
  if (!data.cart_id) {
1706
1664
  return ok(
1707
1665
  toTerminalFailure(
@@ -1713,13 +1671,7 @@ var CheckoutService = class {
1713
1671
  }
1714
1672
  const cartResult = await this.client.cart.get();
1715
1673
  if (!cartResult.ok || !cartResult.value?.id) {
1716
- return ok(
1717
- toTerminalFailure(
1718
- "INVALID_CART",
1719
- "Unable to load cart for checkout.",
1720
- false
1721
- )
1722
- );
1674
+ return ok(toTerminalFailure("INVALID_CART", "Unable to load cart for checkout.", false));
1723
1675
  }
1724
1676
  const cart = cartResult.value;
1725
1677
  if (cart.id !== data.cart_id || cart.items.length === 0) {
@@ -1751,11 +1703,28 @@ var CheckoutService = class {
1751
1703
  voucher_code: data.voucher_code,
1752
1704
  pay_deposit: data.pay_deposit
1753
1705
  };
1754
- const baseCurrency = chunkNIOUSKQQ_js.currencyCode(
1755
- (cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase()
1756
- );
1706
+ const rawBaseCurrency = typeof cart.pricing.currency === "string" ? cart.pricing.currency.trim().toUpperCase() : "";
1707
+ if (!chunk4BKOKHE3_js.isSupportedCurrency(rawBaseCurrency)) {
1708
+ return ok(
1709
+ toTerminalFailure(
1710
+ "INVALID_CURRENCY",
1711
+ "Cart currency is missing or unsupported. Refresh the cart before checkout.",
1712
+ false
1713
+ )
1714
+ );
1715
+ }
1716
+ const baseCurrency = rawBaseCurrency;
1757
1717
  const payCurrency = data.pay_currency?.trim().toUpperCase();
1758
- const payCurrencyCode = payCurrency ? chunkNIOUSKQQ_js.currencyCode(payCurrency) : void 0;
1718
+ if (payCurrency && !chunk4BKOKHE3_js.isSupportedCurrency(payCurrency)) {
1719
+ return ok(
1720
+ toTerminalFailure(
1721
+ "INVALID_CURRENCY",
1722
+ "Payment currency is unsupported for checkout.",
1723
+ false
1724
+ )
1725
+ );
1726
+ }
1727
+ const payCurrencyCode = payCurrency && chunk4BKOKHE3_js.isSupportedCurrency(payCurrency) ? payCurrency : void 0;
1759
1728
  const cartTotalAmount = typeof cart.pricing.total_price === "number" ? cart.pricing.total_price : Number.parseFloat(cart.pricing.total_price || "0");
1760
1729
  if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
1761
1730
  const fxQuoteResult = await this.client.fx.lockQuote({
@@ -1775,7 +1744,7 @@ var CheckoutService = class {
1775
1744
  checkoutData.pay_currency = payCurrencyCode;
1776
1745
  checkoutData.fx_quote_id = fxQuoteResult.value.id;
1777
1746
  }
1778
- data.on_status_change?.("processing", {});
1747
+ notifyCheckoutStatus(data.on_status_change, "processing");
1779
1748
  const processResult = await this.process(checkoutData);
1780
1749
  if (!processResult.ok) {
1781
1750
  return ok(
@@ -1792,12 +1761,13 @@ var CheckoutService = class {
1792
1761
  pollIntervalMs: data.poll_interval_ms,
1793
1762
  maxPollAttempts: data.max_poll_attempts,
1794
1763
  onStatusChange: data.on_status_change,
1764
+ onAuthorizationUiChange: data.on_authorization_ui_change,
1795
1765
  onAuthorizationRequired: data.on_authorization_required,
1796
1766
  signal: data.signal,
1797
1767
  returnUrl: data.return_url,
1798
1768
  enrollInLink: data.enroll_in_link,
1799
1769
  cartTotal: cart.pricing.total_price,
1800
- cartCurrency: checkoutData.pay_currency || cart.pricing.currency || "GHS",
1770
+ cartCurrency: checkoutData.pay_currency || baseCurrency,
1801
1771
  orderType: checkoutData.order_type,
1802
1772
  allowPopups: data.allow_popups
1803
1773
  });
@@ -1865,43 +1835,86 @@ function encodePathSegment(value) {
1865
1835
  var LinkService = class {
1866
1836
  constructor(client) {
1867
1837
  this.client = client;
1838
+ this.activationGeneration = 0;
1839
+ }
1840
+ beginActivation() {
1841
+ return {
1842
+ generation: ++this.activationGeneration,
1843
+ sessionEpoch: this.client.getSessionEpoch(),
1844
+ publicKeyEpoch: this.client.getPublicKeyEpoch()
1845
+ };
1846
+ }
1847
+ isActivationFenceCurrent(fence) {
1848
+ return fence.generation === this.activationGeneration && fence.sessionEpoch === this.client.getSessionEpoch() && fence.publicKeyEpoch === this.client.getPublicKeyEpoch();
1849
+ }
1850
+ supersededActivation() {
1851
+ return {
1852
+ ok: false,
1853
+ error: new chunk4BKOKHE3_js.CimplifyError(
1854
+ "session_superseded",
1855
+ "A newer authentication session replaced this one"
1856
+ )
1857
+ };
1858
+ }
1859
+ async activateAuthenticatedSession(result, fence) {
1860
+ if (!result.ok || !result.value.session_token) return result;
1861
+ if (!this.isActivationFenceCurrent(fence)) return this.supersededActivation();
1862
+ const accessToken = result.value.session_token;
1863
+ this.client.setAccessToken(accessToken);
1864
+ const installedSessionEpoch = this.client.getSessionEpoch();
1865
+ if (!this.client.getPublicKey().trim()) {
1866
+ this.client.activity.refreshIdentity();
1867
+ return result;
1868
+ }
1869
+ const claim = await this.client.support.claimConversation();
1870
+ if (fence.generation !== this.activationGeneration || fence.publicKeyEpoch !== this.client.getPublicKeyEpoch() || this.client.getAccessToken() !== accessToken) {
1871
+ return this.supersededActivation();
1872
+ }
1873
+ if (!claim.ok && claim.error.code !== "already_claimed") {
1874
+ if (this.client.getSessionEpoch() !== installedSessionEpoch) {
1875
+ return this.supersededActivation();
1876
+ }
1877
+ if (this.client.hasClaimedSessionAuthority()) {
1878
+ this.client.clearSession();
1879
+ } else {
1880
+ this.client.setAccessToken(null);
1881
+ }
1882
+ return claim;
1883
+ }
1884
+ this.client.activity.refreshIdentity();
1885
+ return result;
1868
1886
  }
1869
1887
  async requestOtp(input, opts) {
1870
1888
  const idempotencyKey = opts?.idempotencyKey ?? generateIdempotencyKey();
1871
1889
  return safe(
1872
- this.client.linkPost("/v1/link/auth/request-otp", input, { idempotencyKey })
1890
+ this.client.linkPost("/v1/link/auth/request-otp", input, {
1891
+ idempotencyKey
1892
+ })
1873
1893
  );
1874
1894
  }
1875
1895
  async verifyOtp(input, opts) {
1876
1896
  const idempotencyKey = opts?.idempotencyKey ?? generateIdempotencyKey();
1897
+ const activationFence = opts?.activateSession === false ? null : this.beginActivation();
1877
1898
  const { business_id: _ignoredBusinessId, ...body } = input;
1878
1899
  const result = await safe(
1879
1900
  this.client.linkPost("/v1/link/auth/verify-otp", body, { idempotencyKey })
1880
1901
  );
1881
- if (result.ok && result.value.session_token) {
1882
- this.client.setAccessToken(result.value.session_token);
1883
- this.client.activity.refreshIdentity();
1884
- this.client.autoClaimChatIdentity();
1885
- }
1886
- return result;
1902
+ return activationFence ? this.activateAuthenticatedSession(result, activationFence) : result;
1887
1903
  }
1888
1904
  async refreshSession(sessionToken, opts) {
1889
1905
  const idempotencyKey = opts?.idempotencyKey ?? generateIdempotencyKey();
1906
+ const activationFence = opts?.activateSession === false ? null : this.beginActivation();
1890
1907
  const body = sessionToken ? { session_token: sessionToken } : {};
1891
1908
  const result = await safe(
1892
1909
  this.client.linkPost("/v1/link/auth/refresh", body, { idempotencyKey })
1893
1910
  );
1894
- if (result.ok && result.value.session_token) {
1895
- this.client.setAccessToken(result.value.session_token);
1896
- this.client.activity.refreshIdentity();
1897
- this.client.autoClaimChatIdentity();
1898
- }
1899
- return result;
1911
+ return activationFence ? this.activateAuthenticatedSession(result, activationFence) : result;
1900
1912
  }
1901
1913
  async logout() {
1902
- const result = await safe(this.client.linkPost("/v1/link/auth/logout"));
1914
+ this.activationGeneration += 1;
1915
+ const request = safe(this.client.linkPost("/v1/link/auth/logout"));
1903
1916
  this.client.clearSession();
1904
- return result;
1917
+ return request;
1905
1918
  }
1906
1919
  async checkStatus(contact) {
1907
1920
  return safe(this.client.linkPost("/v1/link/check-status", { contact }));
@@ -1947,11 +1960,9 @@ var LinkService = class {
1947
1960
  async enrollAndLinkOrder(data, opts) {
1948
1961
  const idempotencyKey = opts?.idempotencyKey ?? generateIdempotencyKey();
1949
1962
  return safe(
1950
- this.client.linkPost(
1951
- "/v1/link/enroll-and-link-order",
1952
- data,
1953
- { idempotencyKey }
1954
- )
1963
+ this.client.linkPost("/v1/link/enroll-and-link-order", data, {
1964
+ idempotencyKey
1965
+ })
1955
1966
  );
1956
1967
  }
1957
1968
  async updatePreferences(preferences) {
@@ -2120,7 +2131,7 @@ var BusinessService = class {
2120
2131
  if (!result.ok) return err(result.error);
2121
2132
  const location = result.value.find((item) => item.id === locationId);
2122
2133
  if (!location) {
2123
- return err(new chunkNIOUSKQQ_js.CimplifyError(chunkNIOUSKQQ_js.ErrorCode.NOT_FOUND, `Location not found: ${locationId}`, false));
2134
+ return err(new chunk4BKOKHE3_js.CimplifyError(chunk4BKOKHE3_js.ErrorCode.NOT_FOUND, `Location not found: ${locationId}`, false));
2124
2135
  }
2125
2136
  return ok(location);
2126
2137
  }
@@ -2374,6 +2385,7 @@ var MESSAGE_TYPES = {
2374
2385
  INIT: "init",
2375
2386
  SET_TOKEN: "set_token",
2376
2387
  SET_SESSION: "set_session",
2388
+ SESSION_CLEARED: "session_cleared",
2377
2389
  SET_CART: "set_cart",
2378
2390
  GET_DATA: "get_data",
2379
2391
  NAVIGATE: "navigate",
@@ -2390,6 +2402,8 @@ var MESSAGE_TYPES = {
2390
2402
  LOGOUT_COMPLETE: "logout_complete",
2391
2403
  AUTH_REQUESTED: "auth_requested",
2392
2404
  SESSION_ESTABLISHED: "session_established",
2405
+ SESSION_ESTABLISHMENT_FAILED: "session_establishment_failed",
2406
+ CONTINUE_WITHOUT_SAVED_DETAILS: "continue_without_saved_details",
2393
2407
  CONTACT_PROVIDED: "contact_provided",
2394
2408
  CHECKOUT_STATUS: "checkout_status",
2395
2409
  CHECKOUT_COMPLETE: "checkout_complete",
@@ -2740,7 +2754,7 @@ function startRedirectSignIn(opts, authorizeEndpoint, pkce, state) {
2740
2754
 
2741
2755
  // src/elements.ts
2742
2756
  function toCheckoutError(code, message, recoverable) {
2743
- const hint = chunkNIOUSKQQ_js.getErrorHint(code);
2757
+ const hint = chunk4BKOKHE3_js.getErrorHint(code);
2744
2758
  return {
2745
2759
  success: false,
2746
2760
  error: {
@@ -2753,6 +2767,7 @@ function toCheckoutError(code, message, recoverable) {
2753
2767
  };
2754
2768
  }
2755
2769
  var DEFAULT_LINK_URL = "https://link.cimplify.io";
2770
+ var AUTH_ESTABLISHMENT_WAIT_MS = 8e3;
2756
2771
  var AUTH_CONTACT_TYPES = new Set(Object.values(ELEMENTS_AUTH_CONTACT_TYPES));
2757
2772
  var AUTH_MODES = new Set(Object.values(ELEMENTS_AUTH_MODES));
2758
2773
  var AUTH_PROMPTS = new Set(Object.values(ELEMENTS_AUTH_PROMPTS));
@@ -2803,6 +2818,7 @@ var KNOWN_IFRAME_TYPES = /* @__PURE__ */ new Set([
2803
2818
  MESSAGE_TYPES.HEIGHT_CHANGE,
2804
2819
  MESSAGE_TYPES.AUTH_REQUESTED,
2805
2820
  MESSAGE_TYPES.SESSION_ESTABLISHED,
2821
+ MESSAGE_TYPES.CONTINUE_WITHOUT_SAVED_DETAILS,
2806
2822
  MESSAGE_TYPES.ERROR,
2807
2823
  MESSAGE_TYPES.ADDRESS_CHANGED,
2808
2824
  MESSAGE_TYPES.ADDRESS_SELECTED,
@@ -2847,6 +2863,7 @@ function parseIframeMessage(data) {
2847
2863
  if (!record.method || typeof record.method !== "object") return null;
2848
2864
  break;
2849
2865
  case MESSAGE_TYPES.CONTACT_PROVIDED:
2866
+ case MESSAGE_TYPES.CONTINUE_WITHOUT_SAVED_DETAILS:
2850
2867
  if (typeof record.contact !== "string" || typeof record.contactType !== "string") return null;
2851
2868
  break;
2852
2869
  case MESSAGE_TYPES.ERROR:
@@ -2856,7 +2873,8 @@ function parseIframeMessage(data) {
2856
2873
  if (typeof record.success !== "boolean") return null;
2857
2874
  break;
2858
2875
  case MESSAGE_TYPES.CHECKOUT_STATUS:
2859
- if (typeof record.status !== "string" || !record.context || typeof record.context !== "object") return null;
2876
+ if (typeof record.status !== "string" || !record.context || typeof record.context !== "object")
2877
+ return null;
2860
2878
  break;
2861
2879
  case MESSAGE_TYPES.READY:
2862
2880
  case MESSAGE_TYPES.HEIGHT_CHANGE:
@@ -2879,6 +2897,8 @@ var CimplifyElements = class {
2879
2897
  this.paymentData = null;
2880
2898
  this.checkoutInProgress = false;
2881
2899
  this.authInProgress = false;
2900
+ this.authEstablishmentInProgress = false;
2901
+ this.authGeneration = 0;
2882
2902
  this.activeCheckoutAbort = null;
2883
2903
  this.businessIdResolvePromise = null;
2884
2904
  this.client = client;
@@ -2913,6 +2933,12 @@ var CimplifyElements = class {
2913
2933
  }
2914
2934
  }
2915
2935
  destroy() {
2936
+ this.authGeneration += 1;
2937
+ if (this.authEstablishmentInProgress) {
2938
+ this.client.setAccessToken(null);
2939
+ }
2940
+ this.authInProgress = false;
2941
+ this.authEstablishmentInProgress = false;
2916
2942
  this.elements.forEach((element) => element.destroy());
2917
2943
  this.elements.clear();
2918
2944
  if (typeof window !== "undefined") {
@@ -2950,16 +2976,27 @@ var CimplifyElements = class {
2950
2976
  processCheckout(options) {
2951
2977
  let abortFn = null;
2952
2978
  const task = (async () => {
2953
- this.debug("[cimplify:checkout] processCheckout called", { cart_id: options.cart_id, order_type: options.order_type, location_id: options.location_id });
2979
+ this.debug("[cimplify:checkout] processCheckout called", {
2980
+ cart_id: options.cart_id,
2981
+ order_type: options.order_type,
2982
+ location_id: options.location_id
2983
+ });
2954
2984
  if (this.checkoutInProgress) {
2955
2985
  this.debug("[cimplify:checkout] BLOCKED: already processing");
2956
- return toCheckoutError(
2957
- "ALREADY_PROCESSING",
2958
- "Checkout is already in progress.",
2959
- false
2960
- );
2986
+ return toCheckoutError("ALREADY_PROCESSING", "Checkout is already in progress.", false);
2961
2987
  }
2962
2988
  this.checkoutInProgress = true;
2989
+ if (this.authEstablishmentInProgress) {
2990
+ await this.waitForAuthEstablishment(AUTH_ESTABLISHMENT_WAIT_MS);
2991
+ if (this.authEstablishmentInProgress) {
2992
+ this.checkoutInProgress = false;
2993
+ return toCheckoutError(
2994
+ "AUTH_INCOMPLETE",
2995
+ "Authentication must finish establishing the checkout session.",
2996
+ true
2997
+ );
2998
+ }
2999
+ }
2963
3000
  if (!options.cart_id) {
2964
3001
  this.debug("[cimplify:checkout] BLOCKED: no cart_id");
2965
3002
  this.checkoutInProgress = false;
@@ -3017,7 +3054,11 @@ var CimplifyElements = class {
3017
3054
  }
3018
3055
  await this.hydrateCustomerData();
3019
3056
  }
3020
- this.debug("[cimplify:checkout] customer data hydrated", { customerData: this.customerData, accountId: this.accountId, isUnifiedCheckout });
3057
+ this.debug("[cimplify:checkout] customer data hydrated", {
3058
+ customerData: this.customerData,
3059
+ accountId: this.accountId,
3060
+ isUnifiedCheckout
3061
+ });
3021
3062
  const processMessage = {
3022
3063
  type: MESSAGE_TYPES.PROCESS_CHECKOUT,
3023
3064
  cart_id: options.cart_id,
@@ -3081,7 +3122,10 @@ var CimplifyElements = class {
3081
3122
  return;
3082
3123
  }
3083
3124
  if (message.type === MESSAGE_TYPES.CHECKOUT_COMPLETE) {
3084
- this.debug("[cimplify:checkout] received checkout_complete", { success: message.success, error: message.error });
3125
+ this.debug("[cimplify:checkout] received checkout_complete", {
3126
+ success: message.success,
3127
+ error: message.error
3128
+ });
3085
3129
  settle({
3086
3130
  success: message.success,
3087
3131
  order: message.order,
@@ -3092,11 +3136,7 @@ var CimplifyElements = class {
3092
3136
  if (message.type === MESSAGE_TYPES.LOGOUT_COMPLETE) {
3093
3137
  checkoutElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
3094
3138
  settle(
3095
- toCheckoutError(
3096
- "AUTH_LOST",
3097
- "Authentication was cleared during checkout.",
3098
- true
3099
- )
3139
+ toCheckoutError("AUTH_LOST", "Authentication was cleared during checkout.", true)
3100
3140
  );
3101
3141
  }
3102
3142
  };
@@ -3105,16 +3145,16 @@ var CimplifyElements = class {
3105
3145
  }
3106
3146
  abortFn = () => {
3107
3147
  checkoutElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
3108
- settle(
3109
- toCheckoutError(
3110
- "CANCELLED",
3111
- "Checkout was cancelled.",
3112
- true
3113
- )
3114
- );
3148
+ settle(toCheckoutError("CANCELLED", "Checkout was cancelled.", true));
3115
3149
  };
3116
3150
  this.activeCheckoutAbort = abortFn;
3117
- this.debug("[cimplify:checkout] sending process_checkout to checkout iframe", { cart_id: processMessage.cart_id, order_type: processMessage.order_type, hasCustomer: !!processMessage.customer, hasAddress: !!processMessage.address, isUnifiedCheckout });
3151
+ this.debug("[cimplify:checkout] sending process_checkout to checkout iframe", {
3152
+ cart_id: processMessage.cart_id,
3153
+ order_type: processMessage.order_type,
3154
+ hasCustomer: !!processMessage.customer,
3155
+ hasAddress: !!processMessage.address,
3156
+ isUnifiedCheckout
3157
+ });
3118
3158
  checkoutElement.sendMessage(processMessage);
3119
3159
  this.debug("[cimplify:checkout] postMessage sent, waiting for checkout_complete...");
3120
3160
  });
@@ -3160,11 +3200,16 @@ var CimplifyElements = class {
3160
3200
  getAppearance() {
3161
3201
  return this.options.appearance;
3162
3202
  }
3163
- async hydrateCustomerData() {
3164
- if (!this.accessToken || this.customerData) return;
3203
+ isAuthGenerationCurrent(generation, token) {
3204
+ return this.authGeneration === generation && this.client.getAccessToken() === token;
3205
+ }
3206
+ async hydrateCustomerData(generation = this.authGeneration, token = this.accessToken, force = false) {
3207
+ if (!token || !this.isAuthGenerationCurrent(generation, token)) return false;
3208
+ if (this.customerData && !force) return true;
3165
3209
  const linkDataResult = await this.client.link.getLinkData();
3210
+ if (!this.isAuthGenerationCurrent(generation, token)) return false;
3166
3211
  if (!linkDataResult.ok || !linkDataResult.value?.customer) {
3167
- return;
3212
+ return true;
3168
3213
  }
3169
3214
  const customer = linkDataResult.value.customer;
3170
3215
  this.customerData = {
@@ -3173,6 +3218,7 @@ var CimplifyElements = class {
3173
3218
  phone: customer.phone || null
3174
3219
  };
3175
3220
  this.customerId = customer.id;
3221
+ return true;
3176
3222
  }
3177
3223
  sourceElementFor(event, message) {
3178
3224
  for (const element of this.elements.values()) {
@@ -3211,48 +3257,123 @@ var CimplifyElements = class {
3211
3257
  } catch {
3212
3258
  }
3213
3259
  }
3260
+ clearClaimedAuthorityAfterRestoreFailure() {
3261
+ if (!this.client.getAccessToken() && !this.client.hasClaimedSessionAuthority()) return;
3262
+ this.accessToken = null;
3263
+ this.accountId = null;
3264
+ this.customerId = null;
3265
+ this.customerData = null;
3266
+ this.addressData = null;
3267
+ this.paymentData = null;
3268
+ this.authEstablishmentInProgress = false;
3269
+ this.client.clearSession();
3270
+ this.broadcastClearedSession();
3271
+ }
3214
3272
  async restoreSession() {
3215
3273
  if (typeof window === "undefined" || this.accessToken) return;
3274
+ const generation = ++this.authGeneration;
3216
3275
  const storageKey = this.sessionStorageKey();
3217
3276
  if (!storageKey) return;
3218
3277
  let refreshToken = null;
3219
3278
  try {
3220
3279
  refreshToken = window.localStorage.getItem(storageKey);
3221
3280
  } catch {
3281
+ this.clearClaimedAuthorityAfterRestoreFailure();
3222
3282
  return;
3223
3283
  }
3224
- if (!refreshToken) return;
3225
- const result = await this.client.link.refreshSession(refreshToken);
3284
+ if (!refreshToken) {
3285
+ this.clearClaimedAuthorityAfterRestoreFailure();
3286
+ return;
3287
+ }
3288
+ const result = await this.client.link.refreshSession(refreshToken, {
3289
+ activateSession: false
3290
+ });
3291
+ if (this.authGeneration !== generation) return;
3226
3292
  if (!result.ok || !result.value.session_token) {
3227
3293
  this.clearPersistedSession();
3294
+ this.clearClaimedAuthorityAfterRestoreFailure();
3228
3295
  return;
3229
3296
  }
3230
3297
  this.persistRefreshToken(result.value.refresh_token ?? void 0);
3231
- const data = await this.applyAccessToken(result.value.session_token);
3232
- this.broadcastAccessToken(result.value.session_token);
3233
- this.elements.forEach((element) => element._emitAuthenticated(data));
3298
+ const application = await this.applyAccessToken(result.value.session_token, generation);
3299
+ if (application.kind === "claim_failed") {
3300
+ this.clearPersistedSession();
3301
+ this.broadcastClearedSession();
3302
+ return;
3303
+ }
3304
+ if (application.kind !== "authenticated" || !this.broadcastAccessToken(result.value.session_token, generation)) {
3305
+ return;
3306
+ }
3307
+ this.elements.forEach((element) => element._emitAuthenticated(application.data));
3234
3308
  }
3235
- async applyAccessToken(token) {
3309
+ async applyAccessToken(token, generation) {
3310
+ if (this.authGeneration !== generation) return { kind: "incomplete" };
3311
+ this.authEstablishmentInProgress = true;
3312
+ this.accessToken = null;
3313
+ this.accountId = null;
3314
+ this.customerId = null;
3315
+ this.customerData = null;
3316
+ this.addressData = null;
3317
+ this.paymentData = null;
3318
+ this.client.setAccessToken(token);
3319
+ const claim = await this.client.support.claimConversation();
3320
+ if (!this.isAuthGenerationCurrent(generation, token)) return { kind: "incomplete" };
3321
+ if (!claim.ok && claim.error.code !== "already_claimed") {
3322
+ if (this.client.hasClaimedSessionAuthority()) {
3323
+ this.client.clearSession();
3324
+ } else {
3325
+ this.client.setAccessToken(null);
3326
+ }
3327
+ this.authEstablishmentInProgress = false;
3328
+ return {
3329
+ kind: "claim_failed",
3330
+ code: claim.error.code,
3331
+ message: claim.error.userMessage
3332
+ };
3333
+ }
3236
3334
  this.accessToken = token;
3237
3335
  this.accountId = decodeJwtSub(token);
3238
- this.client.setAccessToken(token);
3239
- this.client.autoClaimChatIdentity();
3240
- await this.hydrateCustomerData();
3336
+ if (!await this.hydrateCustomerData(generation, token, true)) {
3337
+ return { kind: "incomplete" };
3338
+ }
3339
+ if (!this.isAuthGenerationCurrent(generation, token)) return { kind: "incomplete" };
3340
+ this.authEstablishmentInProgress = false;
3241
3341
  return {
3242
- accountId: this.accountId ?? "",
3243
- customerId: this.customerId ?? "",
3244
- token,
3245
- customer: this.customerData ?? { name: "", email: null, phone: null }
3342
+ kind: "authenticated",
3343
+ data: {
3344
+ accountId: this.accountId ?? "",
3345
+ customerId: this.customerId ?? "",
3346
+ token,
3347
+ customer: this.customerData ?? { name: "", email: null, phone: null }
3348
+ }
3246
3349
  };
3247
3350
  }
3248
- broadcastAccessToken(token) {
3351
+ async waitForAuthEstablishment(timeoutMs) {
3352
+ const deadline = Date.now() + timeoutMs;
3353
+ while (this.authEstablishmentInProgress && Date.now() < deadline) {
3354
+ await new Promise((resolve) => setTimeout(resolve, 100));
3355
+ }
3356
+ }
3357
+ broadcastAccessToken(token, generation) {
3358
+ if (!this.isAuthGenerationCurrent(generation, token) || this.accessToken !== token || this.authEstablishmentInProgress) {
3359
+ return false;
3360
+ }
3249
3361
  const sessionToken = this.client.getSessionToken();
3250
3362
  this.elements.forEach((element) => {
3251
- element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
3252
3363
  if (sessionToken) {
3253
3364
  element.sendMessage({ type: MESSAGE_TYPES.SET_SESSION, sessionToken });
3254
3365
  }
3366
+ element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
3367
+ });
3368
+ return true;
3369
+ }
3370
+ broadcastClearedSession() {
3371
+ const sessionToken = this.client.getSessionToken();
3372
+ if (!sessionToken) return null;
3373
+ this.elements.forEach((element) => {
3374
+ element.sendMessage({ type: MESSAGE_TYPES.SESSION_CLEARED, sessionToken });
3255
3375
  });
3376
+ return sessionToken;
3256
3377
  }
3257
3378
  startEmbeddedAuth(sourceElement, request) {
3258
3379
  if (this.authInProgress) return;
@@ -3266,6 +3387,7 @@ var CimplifyElements = class {
3266
3387
  });
3267
3388
  return;
3268
3389
  }
3390
+ const generation = ++this.authGeneration;
3269
3391
  this.authInProgress = true;
3270
3392
  void startSignIn({
3271
3393
  clientId,
@@ -3280,6 +3402,7 @@ var CimplifyElements = class {
3280
3402
  mode: "modal",
3281
3403
  silentFirst: request.mode === ELEMENTS_AUTH_MODES.CHECKOUT && request.prompt !== ELEMENTS_AUTH_PROMPTS.LOGIN,
3282
3404
  onSuccess: async ({ accessToken }) => {
3405
+ if (this.authGeneration !== generation) return;
3283
3406
  this.authInProgress = false;
3284
3407
  if (!accessToken) {
3285
3408
  sourceElement._emitError({
@@ -3288,11 +3411,18 @@ var CimplifyElements = class {
3288
3411
  });
3289
3412
  return;
3290
3413
  }
3291
- const data = await this.applyAccessToken(accessToken);
3292
- this.broadcastAccessToken(accessToken);
3293
- sourceElement._emitAuthenticated(data);
3414
+ const application = await this.applyAccessToken(accessToken, generation);
3415
+ if (application.kind === "authenticated" && this.broadcastAccessToken(accessToken, generation)) {
3416
+ sourceElement._emitAuthenticated(application.data);
3417
+ } else if (application.kind === "claim_failed") {
3418
+ sourceElement._emitError({
3419
+ code: application.code,
3420
+ message: application.message
3421
+ });
3422
+ }
3294
3423
  },
3295
3424
  onError: (err2) => {
3425
+ if (this.authGeneration !== generation) return;
3296
3426
  this.authInProgress = false;
3297
3427
  sourceElement._emitError({
3298
3428
  code: err2.error,
@@ -3300,6 +3430,7 @@ var CimplifyElements = class {
3300
3430
  });
3301
3431
  }
3302
3432
  }).catch((error) => {
3433
+ if (this.authGeneration !== generation) return;
3303
3434
  this.authInProgress = false;
3304
3435
  sourceElement._emitError({
3305
3436
  code: "AUTH_START_FAILED",
@@ -3320,11 +3451,29 @@ var CimplifyElements = class {
3320
3451
  this.startEmbeddedAuth(sourceElement, message);
3321
3452
  break;
3322
3453
  case MESSAGE_TYPES.SESSION_ESTABLISHED:
3323
- this.persistRefreshToken(message.refreshToken);
3324
- void this.applyAccessToken(message.accessToken).then((data) => {
3325
- this.broadcastAccessToken(message.accessToken);
3326
- sourceElement._emitAuthenticated(data);
3327
- });
3454
+ {
3455
+ this.authInProgress = false;
3456
+ const generation = ++this.authGeneration;
3457
+ void this.applyAccessToken(message.accessToken, generation).then((application) => {
3458
+ if (this.authGeneration !== generation) return;
3459
+ if (application.kind === "claim_failed") {
3460
+ this.clearPersistedSession();
3461
+ sourceElement.sendMessage({
3462
+ type: MESSAGE_TYPES.SESSION_ESTABLISHMENT_FAILED,
3463
+ accessToken: message.accessToken,
3464
+ code: application.code,
3465
+ message: application.message
3466
+ });
3467
+ this.broadcastClearedSession();
3468
+ return;
3469
+ }
3470
+ if (application.kind !== "authenticated" || !this.broadcastAccessToken(message.accessToken, generation)) {
3471
+ return;
3472
+ }
3473
+ this.persistRefreshToken(message.refreshToken);
3474
+ sourceElement._emitAuthenticated(application.data);
3475
+ });
3476
+ }
3328
3477
  break;
3329
3478
  case MESSAGE_TYPES.ADDRESS_CHANGED:
3330
3479
  case MESSAGE_TYPES.ADDRESS_SELECTED:
@@ -3343,7 +3492,35 @@ var CimplifyElements = class {
3343
3492
  this.customerData.phone = message.contact;
3344
3493
  }
3345
3494
  break;
3495
+ case MESSAGE_TYPES.CONTINUE_WITHOUT_SAVED_DETAILS: {
3496
+ this.authGeneration += 1;
3497
+ this.authInProgress = false;
3498
+ this.authEstablishmentInProgress = false;
3499
+ const carriedIdentityAuthority = Boolean(this.client.getAccessToken()) || this.client.hasClaimedSessionAuthority();
3500
+ this.accessToken = null;
3501
+ this.accountId = null;
3502
+ this.customerId = null;
3503
+ this.customerData = { name: "", email: null, phone: null };
3504
+ this.addressData = null;
3505
+ this.paymentData = null;
3506
+ this.clearPersistedSession();
3507
+ if (carriedIdentityAuthority) {
3508
+ this.client.clearSession();
3509
+ } else {
3510
+ this.client.setAccessToken(null);
3511
+ }
3512
+ this.broadcastClearedSession();
3513
+ if (message.contactType === "email") {
3514
+ this.customerData.email = message.contact;
3515
+ } else {
3516
+ this.customerData.phone = message.contact;
3517
+ }
3518
+ break;
3519
+ }
3346
3520
  case MESSAGE_TYPES.LOGOUT_COMPLETE:
3521
+ this.authGeneration += 1;
3522
+ this.authInProgress = false;
3523
+ this.authEstablishmentInProgress = false;
3347
3524
  if (this.checkoutInProgress && this.activeCheckoutAbort) {
3348
3525
  this.activeCheckoutAbort();
3349
3526
  }
@@ -3355,6 +3532,7 @@ var CimplifyElements = class {
3355
3532
  this.paymentData = null;
3356
3533
  this.clearPersistedSession();
3357
3534
  this.client.clearSession();
3535
+ this.broadcastClearedSession();
3358
3536
  break;
3359
3537
  }
3360
3538
  }
@@ -3488,10 +3666,9 @@ var CimplifyElement = class {
3488
3666
  const iframe = this.iframe;
3489
3667
  if (!iframe || !iframe.isConnected) {
3490
3668
  if (iframe && !iframe.isConnected) {
3491
- console.warn(
3492
- "[cimplify:checkout] sendMessage DROPPED \u2014 iframe detached",
3493
- { type: message.type }
3494
- );
3669
+ console.warn("[cimplify:checkout] sendMessage DROPPED \u2014 iframe detached", {
3670
+ type: message.type
3671
+ });
3495
3672
  }
3496
3673
  return;
3497
3674
  }
@@ -3590,13 +3767,13 @@ var CimplifyElement = class {
3590
3767
  submitLabel: this.options.submitLabel
3591
3768
  });
3592
3769
  const token = this.parent.getAccessToken();
3593
- if (token) {
3594
- this.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
3595
- }
3596
3770
  const sessionToken = this.parent.getSessionToken();
3597
3771
  if (sessionToken) {
3598
3772
  this.sendMessage({ type: MESSAGE_TYPES.SET_SESSION, sessionToken });
3599
3773
  }
3774
+ if (token) {
3775
+ this.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
3776
+ }
3600
3777
  this.pendingInit = null;
3601
3778
  }
3602
3779
  this.flushPending();
@@ -3645,23 +3822,14 @@ function createElements(client, businessId, options) {
3645
3822
  return new CimplifyElements(client, businessId, options);
3646
3823
  }
3647
3824
 
3648
- exports.AUTHORIZATION_TYPE = AUTHORIZATION_TYPE;
3649
- exports.AUTH_MUTATION = AUTH_MUTATION;
3650
3825
  exports.AuthService = AuthService;
3651
3826
  exports.BusinessService = BusinessService;
3652
- exports.CHECKOUT_MODE = CHECKOUT_MODE;
3653
- exports.CHECKOUT_MUTATION = CHECKOUT_MUTATION;
3654
- exports.CHECKOUT_STEP = CHECKOUT_STEP;
3655
- exports.CONTACT_TYPE = CONTACT_TYPE;
3656
3827
  exports.CartOperations = CartOperations;
3657
3828
  exports.CatalogueQueries = CatalogueQueries;
3658
3829
  exports.CheckoutService = CheckoutService;
3659
3830
  exports.CimplifyElement = CimplifyElement;
3660
3831
  exports.CimplifyElements = CimplifyElements;
3661
- exports.DEFAULT_COUNTRY = DEFAULT_COUNTRY;
3662
- exports.DEFAULT_CURRENCY = DEFAULT_CURRENCY;
3663
3832
  exports.DEFAULT_ISSUER = DEFAULT_ISSUER;
3664
- exports.DEVICE_TYPE = DEVICE_TYPE;
3665
3833
  exports.ELEMENTS_AUTH_CONTACT_TYPES = ELEMENTS_AUTH_CONTACT_TYPES;
3666
3834
  exports.ELEMENTS_AUTH_MODES = ELEMENTS_AUTH_MODES;
3667
3835
  exports.ELEMENTS_AUTH_PROMPTS = ELEMENTS_AUTH_PROMPTS;
@@ -3669,19 +3837,10 @@ exports.ELEMENT_TYPES = ELEMENT_TYPES;
3669
3837
  exports.EVENT_TYPES = EVENT_TYPES;
3670
3838
  exports.FxService = FxService;
3671
3839
  exports.InventoryService = InventoryService;
3672
- exports.LINK_MUTATION = LINK_MUTATION;
3673
- exports.LINK_QUERY = LINK_QUERY;
3674
3840
  exports.LinkService = LinkService;
3675
3841
  exports.LiteService = LiteService;
3676
3842
  exports.MESSAGE_TYPES = MESSAGE_TYPES;
3677
- exports.MOBILE_MONEY_PROVIDER = MOBILE_MONEY_PROVIDER;
3678
- exports.ORDER_MUTATION = ORDER_MUTATION;
3679
- exports.ORDER_TYPE = ORDER_TYPE;
3680
3843
  exports.OrderQueries = OrderQueries;
3681
- exports.PAYMENT_METHOD = PAYMENT_METHOD;
3682
- exports.PAYMENT_MUTATION = PAYMENT_MUTATION;
3683
- exports.PAYMENT_STATE = PAYMENT_STATE;
3684
- exports.PICKUP_TIME_TYPE = PICKUP_TIME_TYPE;
3685
3844
  exports.PKCE_COOKIE = PKCE_COOKIE;
3686
3845
  exports.PKCE_COOKIE_PATH = PKCE_COOKIE_PATH;
3687
3846
  exports.SchedulingService = SchedulingService;