@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
@@ -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 chunkNNG6IFFX_js = require('./chunk-NNG6IFFX.js');
5
+ var chunkJG5CQNCB_js = require('./chunk-JG5CQNCB.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 chunkJG5CQNCB_js.CimplifyError) return chunkJG5CQNCB_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 chunkJG5CQNCB_js.enrichError(new chunkJG5CQNCB_js.CimplifyError(chunkJG5CQNCB_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 chunkJG5CQNCB_js.enrichError(new chunkJG5CQNCB_js.CimplifyError(chunkJG5CQNCB_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 chunkJG5CQNCB_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 = chunkNNG6IFFX_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
921
  }
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
- }
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") {
1045
+ case NEXT_ACTION_REDIRECT: {
1046
+ const limitFailure = this.consumeAuthorizationStep(resolutionState);
1047
+ if (limitFailure) return limitFailure;
1048
+ const authorizationUrl = chunkNNG6IFFX_js.paymentAuthorizationUrl(action.authorization_url);
1049
+ if (!authorizationUrl) {
1218
1050
  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") {
1225
- 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 = chunkNNG6IFFX_js.normalizeStatusResponse(statusResult.value);
1173
+ if (normalized.paid || chunkNNG6IFFX_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 (chunkNNG6IFFX_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 (chunkNNG6IFFX_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 chunkJG5CQNCB_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 chunkJG5CQNCB_js.isSupportedCurrency(currency2) ? currency2 : void 0;
1547
1421
  }
1548
- return "GHS";
1422
+ const currency = this.cartCurrency.trim().toUpperCase();
1423
+ return chunkJG5CQNCB_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 = chunkNNG6IFFX_js.normalizeStatusResponse({ status, paid: false });
1455
+ return normalized.paid || chunkNNG6IFFX_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 = chunkNNG6IFFX_js.normalizeStatusResponse({ status, paid: false });
1459
+ return chunkNNG6IFFX_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 chunkJG5CQNCB_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 chunkJG5CQNCB_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 (!chunkJG5CQNCB_js.isSupportedCurrency(normalized)) {
1570
+ return err(
1571
+ new chunkJG5CQNCB_js.CimplifyError(
1572
+ chunkJG5CQNCB_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 chunkJG5CQNCB_js.CimplifyError(
1587
+ chunkJG5CQNCB_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 !== chunkJG5CQNCB_js.ErrorCode.NETWORK_ERROR && result.error.code !== chunkJG5CQNCB_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 chunkJG5CQNCB_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 (!chunkJG5CQNCB_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 && !chunkJG5CQNCB_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 && chunkJG5CQNCB_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
  });
@@ -2120,7 +2090,7 @@ var BusinessService = class {
2120
2090
  if (!result.ok) return err(result.error);
2121
2091
  const location = result.value.find((item) => item.id === locationId);
2122
2092
  if (!location) {
2123
- return err(new chunkNIOUSKQQ_js.CimplifyError(chunkNIOUSKQQ_js.ErrorCode.NOT_FOUND, `Location not found: ${locationId}`, false));
2093
+ return err(new chunkJG5CQNCB_js.CimplifyError(chunkJG5CQNCB_js.ErrorCode.NOT_FOUND, `Location not found: ${locationId}`, false));
2124
2094
  }
2125
2095
  return ok(location);
2126
2096
  }
@@ -2740,7 +2710,7 @@ function startRedirectSignIn(opts, authorizeEndpoint, pkce, state) {
2740
2710
 
2741
2711
  // src/elements.ts
2742
2712
  function toCheckoutError(code, message, recoverable) {
2743
- const hint = chunkNIOUSKQQ_js.getErrorHint(code);
2713
+ const hint = chunkJG5CQNCB_js.getErrorHint(code);
2744
2714
  return {
2745
2715
  success: false,
2746
2716
  error: {
@@ -3645,23 +3615,14 @@ function createElements(client, businessId, options) {
3645
3615
  return new CimplifyElements(client, businessId, options);
3646
3616
  }
3647
3617
 
3648
- exports.AUTHORIZATION_TYPE = AUTHORIZATION_TYPE;
3649
- exports.AUTH_MUTATION = AUTH_MUTATION;
3650
3618
  exports.AuthService = AuthService;
3651
3619
  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
3620
  exports.CartOperations = CartOperations;
3657
3621
  exports.CatalogueQueries = CatalogueQueries;
3658
3622
  exports.CheckoutService = CheckoutService;
3659
3623
  exports.CimplifyElement = CimplifyElement;
3660
3624
  exports.CimplifyElements = CimplifyElements;
3661
- exports.DEFAULT_COUNTRY = DEFAULT_COUNTRY;
3662
- exports.DEFAULT_CURRENCY = DEFAULT_CURRENCY;
3663
3625
  exports.DEFAULT_ISSUER = DEFAULT_ISSUER;
3664
- exports.DEVICE_TYPE = DEVICE_TYPE;
3665
3626
  exports.ELEMENTS_AUTH_CONTACT_TYPES = ELEMENTS_AUTH_CONTACT_TYPES;
3666
3627
  exports.ELEMENTS_AUTH_MODES = ELEMENTS_AUTH_MODES;
3667
3628
  exports.ELEMENTS_AUTH_PROMPTS = ELEMENTS_AUTH_PROMPTS;
@@ -3669,19 +3630,10 @@ exports.ELEMENT_TYPES = ELEMENT_TYPES;
3669
3630
  exports.EVENT_TYPES = EVENT_TYPES;
3670
3631
  exports.FxService = FxService;
3671
3632
  exports.InventoryService = InventoryService;
3672
- exports.LINK_MUTATION = LINK_MUTATION;
3673
- exports.LINK_QUERY = LINK_QUERY;
3674
3633
  exports.LinkService = LinkService;
3675
3634
  exports.LiteService = LiteService;
3676
3635
  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
3636
  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
3637
  exports.PKCE_COOKIE = PKCE_COOKIE;
3686
3638
  exports.PKCE_COOKIE_PATH = PKCE_COOKIE_PATH;
3687
3639
  exports.SchedulingService = SchedulingService;