@cimplify/sdk 0.103.0 → 0.104.1

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 (72) hide show
  1. package/dist/advanced.d.mts +3 -7
  2. package/dist/advanced.d.ts +3 -7
  3. package/dist/advanced.js +23 -23
  4. package/dist/advanced.mjs +4 -4
  5. package/dist/{client-DHBwvh7U.d.ts → catalogue-BlQ85Uma.d.mts} +492 -748
  6. package/dist/{client-BfpNjJ_a.d.mts → catalogue-C8TqOj9K.d.ts} +492 -748
  7. package/dist/{payment-H5LoIvXU.d.mts → checkout-BqK9XUz5.d.mts} +4338 -1495
  8. package/dist/{payment-H5LoIvXU.d.ts → checkout-BqK9XUz5.d.ts} +4338 -1495
  9. package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
  10. package/dist/{chunk-C2JSNJUC.mjs → chunk-6G3ANKQA.mjs} +1080 -369
  11. package/dist/{chunk-QOWPGS7Z.js → chunk-BWEIMKLF.js} +572 -17
  12. package/dist/{chunk-JSWJ4XDE.js → chunk-DQEXY542.js} +1630 -824
  13. package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
  14. package/dist/{chunk-VIL6IQCG.js → chunk-HYFA67A7.js} +1084 -373
  15. package/dist/{chunk-H7ACMHM3.mjs → chunk-L5SNEU2O.mjs} +553 -16
  16. package/dist/{chunk-JG5CQNCB.js → chunk-RBNBSQI7.mjs} +221 -19
  17. package/dist/{chunk-VNPEYLB2.mjs → chunk-SFUJMS5E.js} +265 -2
  18. package/dist/{chunk-M4URVKHE.mjs → chunk-U54CWUUT.mjs} +11 -2
  19. package/dist/{chunk-GERXZQSX.mjs → chunk-UFOWPN3D.mjs} +967 -582
  20. package/dist/{chunk-J6PNHDHL.mjs → chunk-V4457OKB.mjs} +1580 -785
  21. package/dist/{chunk-R6OLQ33I.js → chunk-WXREIBFW.js} +1041 -654
  22. package/dist/{chunk-NNG6IFFX.js → chunk-XAUSDMMZ.js} +13 -3
  23. package/dist/{client-DpqRobY9.d.mts → client-DhXeiUrf.d.ts} +2 -2
  24. package/dist/{client-Dqg2mc-7.d.ts → client-mXiUrV4X.d.mts} +2 -2
  25. package/dist/{index-C8uDqLwJ.d.ts → index-B0AdZvrM.d.mts} +9 -4
  26. package/dist/{index-e2y6AK70.d.mts → index-CK8Nk5uQ.d.ts} +9 -4
  27. package/dist/index-DPWI5XcU.d.ts +8 -0
  28. package/dist/index-DVfUh-ci.d.mts +8 -0
  29. package/dist/index.d.mts +34 -15
  30. package/dist/index.d.ts +34 -15
  31. package/dist/index.js +306 -133
  32. package/dist/index.mjs +7 -6
  33. package/dist/mock/cli.mjs +1494 -395
  34. package/dist/mock/library.js +1220 -361
  35. package/dist/mock/library.mjs +1220 -361
  36. package/dist/mock/msw.js +1220 -361
  37. package/dist/mock/msw.mjs +1220 -361
  38. package/dist/{price-C2RqscMY.d.ts → price-6hQIIac9.d.ts} +1 -1
  39. package/dist/{price-BvePgDXe.d.mts → price-BUDv1rI7.d.mts} +1 -1
  40. package/dist/react.d.mts +10 -5
  41. package/dist/react.d.ts +10 -5
  42. package/dist/react.js +279 -225
  43. package/dist/react.mjs +140 -86
  44. package/dist/{server-DoXIpSd7.d.ts → server-DJ0mSs9i.d.mts} +33 -57
  45. package/dist/{server-C7FyLdrz.d.mts → server-DSTX76Ki.d.ts} +33 -57
  46. package/dist/server.d.mts +3 -4
  47. package/dist/server.d.ts +3 -4
  48. package/dist/server.js +12 -11
  49. package/dist/server.mjs +6 -5
  50. package/dist/styles.css +2 -2
  51. package/dist/testing/msw.d.mts +2 -2
  52. package/dist/testing/msw.d.ts +2 -2
  53. package/dist/testing/msw.js +4 -4
  54. package/dist/testing/msw.mjs +3 -3
  55. package/dist/testing/suite.d.mts +5 -6
  56. package/dist/testing/suite.d.ts +5 -6
  57. package/dist/testing/suite.js +29 -28
  58. package/dist/testing/suite.mjs +10 -9
  59. package/dist/testing.d.mts +2217 -14
  60. package/dist/testing.d.ts +2217 -14
  61. package/dist/testing.js +173 -83
  62. package/dist/testing.mjs +28 -10
  63. package/dist/utils.d.mts +3 -4
  64. package/dist/utils.d.ts +3 -4
  65. package/dist/utils.js +35 -31
  66. package/dist/utils.mjs +2 -2
  67. package/package.json +9 -9
  68. package/registry/chat-widget.json +1 -1
  69. package/registry/order-history.json +1 -1
  70. package/registry/order-summary.json +1 -1
  71. package/dist/link-cuNVYc2s.d.mts +0 -297
  72. package/dist/link-cuNVYc2s.d.ts +0 -297
@@ -1,6 +1,6 @@
1
- import { MOBILE_MONEY_PROVIDER, PAYMENT_METHOD, MAX_PAYMENT_AUTHORIZATION_STEPS } from './chunk-GOHSXBKW.mjs';
2
- import { normalizeStatusResponse, paymentAuthorizationUrl, isPaymentStatusSuccess, isPaymentStatusFailure, isPaymentStatusRequiresAction } from './chunk-M4URVKHE.mjs';
3
- import { CimplifyError, isSupportedCurrency, ErrorCode, enrichError, getErrorHint } from './chunk-VNPEYLB2.mjs';
1
+ import { CHECKOUT_STATUS, PAYMENT_METHOD, CHECKOUT_NEXT_ACTION, CHECKOUT_ORDER_RESULT_STATUS, ORDER_TYPE, AUTHORIZATION_TYPE } from './chunk-33VCYWVL.mjs';
2
+ import { paymentAuthorizationUrl, purchaseIntentMobileMoneyNetwork } from './chunk-U54CWUUT.mjs';
3
+ import { ErrorCode, PURCHASE_INTENT_RESOLVE_STATUS, CimplifyError, PAYMENT_OPTION_STATUS, isSupportedCurrency, PURCHASE_INTENT_CONFIRM_STATUS, PURCHASE_INTENT_ACTION_DECISION, PURCHASE_INTENT_RESOLUTION_KIND, PURCHASE_INTENT_ERROR_CODE, enrichError, LEGACY_CHECKOUT_PAYMENT_METHOD_ALIAS, PURCHASE_INTENT_PAYMENT_METHOD, PURCHASE_INTENT_SOURCE_KIND, LEGACY_CHECKOUT_PAYMENT_STATUS, getErrorHint, MOBILE_MONEY_TENDER_AUTHORITY_KIND } from './chunk-RBNBSQI7.mjs';
4
4
 
5
5
  // src/types/result.ts
6
6
  function ok(value) {
@@ -785,703 +785,796 @@ var CartOperations = class {
785
785
  }
786
786
  };
787
787
 
788
- // src/utils/checkout-resolver.ts
789
- var DEFAULT_POLL_INTERVAL_MS = 3e3;
790
- var DEFAULT_MAX_POLL_ATTEMPTS = 60;
791
- var MAX_CONSECUTIVE_NETWORK_ERRORS = 5;
792
- var NEXT_ACTION_NONE = "none";
793
- var NEXT_ACTION_CARD_POPUP = "card_popup";
794
- var NEXT_ACTION_REDIRECT = "redirect";
795
- var NEXT_ACTION_AUTHORIZATION = "authorization";
796
- var NEXT_ACTION_POLL = "poll";
797
- var VALID_MOBILE_MONEY_PROVIDERS = new Set(Object.values(MOBILE_MONEY_PROVIDER));
798
- function isValidMobileMoneyProvider(value) {
799
- return VALID_MOBILE_MONEY_PROVIDERS.has(value);
800
- }
801
- function normalizeAuthorizationType(value) {
802
- if (value && typeof value === "object" && "address" in value) {
803
- return "address";
804
- }
805
- if (typeof value !== "string") return void 0;
806
- const normalized = value.trim().toLowerCase().replace(/[\s-]+/g, "_");
807
- if (normalized === "otp" || normalized === "pin") return normalized;
808
- if (normalized === "phone" || normalized === "birthday" || normalized === "address") {
809
- return normalized;
788
+ // src/payment-authorization/types.ts
789
+ var PAYMENT_AUTHORIZATION_OUTCOME_STATUS = {
790
+ SUBMITTED: "submitted",
791
+ CANCELLED: "cancelled",
792
+ FAILED: "failed"
793
+ };
794
+ var PAYMENT_AUTHORIZATION_PROVIDER = {
795
+ PAYSTACK: "paystack",
796
+ STRIPE: "stripe"
797
+ };
798
+
799
+ // src/payment-authorization/providers/paystack.ts
800
+ var PAYSTACK_POPUP_ERROR = {
801
+ PROVIDER_UNAVAILABLE: ErrorCode.PROVIDER_UNAVAILABLE,
802
+ SUBMISSION_UNKNOWN: "SUBMISSION_UNKNOWN",
803
+ CANCELLED: "CANCELLED",
804
+ PAYMENT_CANCELLED: ErrorCode.PAYMENT_CANCELLED,
805
+ PAYMENT_FAILED: ErrorCode.PAYMENT_FAILED,
806
+ POPUP_BLOCKED: ErrorCode.POPUP_BLOCKED
807
+ };
808
+ async function openPaystackPopup(options, signal) {
809
+ if (typeof window === "undefined") {
810
+ return { success: false, error: PAYSTACK_POPUP_ERROR.PROVIDER_UNAVAILABLE };
810
811
  }
811
- return void 0;
812
- }
813
- function normalizeAuthorizationSubmission(expectedType, submission) {
814
- if (!submission || submission.type !== expectedType) {
815
- throw new Error(`Expected ${expectedType} authorization details.`);
812
+ let PaystackPop;
813
+ try {
814
+ const imported = await import('@paystack/inline-js');
815
+ PaystackPop = imported.default;
816
+ } catch {
817
+ return { success: false, error: PAYSTACK_POPUP_ERROR.PROVIDER_UNAVAILABLE };
816
818
  }
817
- if (submission.type === "address") {
818
- const address = submission.address.trim();
819
- const city = submission.city.trim();
820
- const state = submission.state.trim();
821
- const zipCode = submission.zip_code.trim();
822
- if (!address || !city || !state || !zipCode) {
823
- throw new Error("Complete all authorization address fields.");
824
- }
825
- return {
826
- type: "address",
827
- address,
828
- city,
829
- state,
830
- zip_code: zipCode
819
+ return new Promise((resolve) => {
820
+ let settled = false;
821
+ let providerStarted = false;
822
+ const resolveOnce = (result) => {
823
+ if (settled) {
824
+ return;
825
+ }
826
+ settled = true;
827
+ if (signal) {
828
+ signal.removeEventListener("abort", onAbort);
829
+ }
830
+ resolve(result);
831
831
  };
832
- }
833
- const value = submission.value.trim();
834
- if (!value) {
835
- throw new Error("Authorization value is required.");
836
- }
837
- return { type: submission.type, value };
838
- }
839
- function formatMoney(value) {
840
- if (typeof value === "number" && Number.isFinite(value)) {
841
- return value.toFixed(2);
842
- }
843
- if (typeof value === "string" && value.trim().length > 0) {
844
- return value;
845
- }
846
- return "0.00";
847
- }
848
- function createAbortError() {
849
- const error = new Error("CANCELLED");
850
- error.name = "AbortError";
851
- return error;
852
- }
853
- function isAbortError(error) {
854
- if (error instanceof DOMException && error.name === "AbortError") {
855
- return true;
856
- }
857
- return error instanceof Error && (error.name === "AbortError" || error.message === "CANCELLED");
858
- }
859
- function waitForAbortable(promise, signal) {
860
- if (!signal) return promise;
861
- if (signal.aborted) return Promise.reject(createAbortError());
862
- return new Promise((resolve, reject) => {
863
832
  const onAbort = () => {
864
- signal.removeEventListener("abort", onAbort);
865
- reject(createAbortError());
833
+ resolveOnce({
834
+ success: false,
835
+ // Once Paystack owns the transaction, cancellation is ambiguous. The
836
+ // adapter converts this into submitted so Cimplify polling decides.
837
+ error: providerStarted ? PAYSTACK_POPUP_ERROR.SUBMISSION_UNKNOWN : PAYSTACK_POPUP_ERROR.CANCELLED
838
+ });
866
839
  };
867
- signal.addEventListener("abort", onAbort, { once: true });
868
- promise.then(
869
- (value) => {
870
- signal.removeEventListener("abort", onAbort);
871
- resolve(value);
872
- },
873
- (error) => {
874
- signal.removeEventListener("abort", onAbort);
875
- reject(error);
876
- }
877
- );
878
- });
879
- }
880
- var CheckoutResolver = class {
881
- constructor(options) {
882
- this.client = options.client;
883
- this.checkoutData = options.checkoutData;
884
- this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
885
- this.maxPollAttempts = options.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;
886
- this.onStatusChange = options.onStatusChange;
887
- this.onAuthorizationUiChange = options.onAuthorizationUiChange;
888
- this.onAuthorizationRequired = options.onAuthorizationRequired;
889
- this.signal = options.signal;
890
- this.returnUrl = options.returnUrl;
891
- this.enrollInLink = options.enrollInLink !== false;
892
- this.businessId = options.businessId;
893
- this.addressData = options.addressData;
894
- this.paymentData = options.paymentData;
895
- this.orderType = options.orderType;
896
- this.cartTotal = options.cartTotal;
897
- this.cartCurrency = options.cartCurrency;
898
- this.allowPopups = options.allowPopups ?? typeof window !== "undefined";
899
- }
900
- async resolve(checkoutResult) {
840
+ if (signal?.aborted) {
841
+ resolveOnce({ success: false, error: PAYSTACK_POPUP_ERROR.CANCELLED });
842
+ return;
843
+ }
844
+ if (signal) {
845
+ signal.addEventListener("abort", onAbort, { once: true });
846
+ }
901
847
  try {
902
- this.ensureNotAborted();
903
- if (!checkoutResult.order_id) {
904
- return this.fail("CHECKOUT_FAILED", "Checkout did not return an order ID.", false);
905
- }
906
- if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
907
- return this.finalizeSuccess(checkoutResult);
908
- }
909
- if (this.isFailureStatus(checkoutResult.payment_status)) {
910
- const normalized = normalizeStatusResponse({
911
- status: checkoutResult.payment_status,
912
- paid: false
913
- });
914
- return this.fail(
915
- normalized.status.toUpperCase(),
916
- checkoutResult.display_text || "Payment failed before confirmation.",
917
- false
918
- );
919
- }
920
- const nextAction = checkoutResult.next_action ?? this.deriveLegacyNextAction(checkoutResult);
921
- return this.resolveNextAction({ ...checkoutResult, next_action: nextAction });
922
- } catch (error) {
923
- if (isAbortError(error)) {
924
- return this.fail("CANCELLED", "Checkout was cancelled.", true);
925
- }
926
- const message = error instanceof Error ? error.message : "Checkout failed unexpectedly.";
927
- return this.fail("CHECKOUT_FAILED", message, true);
848
+ const popup = new PaystackPop();
849
+ popup.newTransaction({
850
+ key: options.key,
851
+ email: options.email,
852
+ amount: options.amount,
853
+ currency: options.currency,
854
+ reference: options.reference,
855
+ accessCode: options.accessCode,
856
+ onSuccess: (transaction) => {
857
+ resolveOnce({
858
+ success: true,
859
+ reference: transaction.reference ?? options.reference
860
+ });
861
+ },
862
+ onCancel: () => {
863
+ resolveOnce({ success: false, error: PAYSTACK_POPUP_ERROR.PAYMENT_CANCELLED });
864
+ },
865
+ onError: (error) => {
866
+ resolveOnce({
867
+ success: false,
868
+ error: error?.message || PAYSTACK_POPUP_ERROR.PAYMENT_FAILED
869
+ });
870
+ }
871
+ });
872
+ providerStarted = true;
873
+ } catch {
874
+ resolveOnce({ success: false, error: PAYSTACK_POPUP_ERROR.POPUP_BLOCKED });
928
875
  }
876
+ });
877
+ }
878
+ var PaystackAuthorizationAdapter = class {
879
+ constructor(openPopup = openPaystackPopup) {
880
+ this.openPopup = openPopup;
881
+ this.provider = PAYMENT_AUTHORIZATION_PROVIDER.PAYSTACK;
929
882
  }
930
- deriveLegacyNextAction(checkoutResult) {
931
- if (checkoutResult.client_secret && checkoutResult.public_key) {
883
+ async authorize(context) {
884
+ const result = await this.openPopup(
885
+ {
886
+ key: context.action.public_key,
887
+ email: context.email,
888
+ accessCode: context.action.client_secret,
889
+ reference: context.reference || context.action.client_secret,
890
+ currency: context.currency
891
+ },
892
+ context.signal
893
+ );
894
+ if (result.success) {
895
+ return {
896
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.SUBMITTED,
897
+ reference: result.reference || context.reference
898
+ };
899
+ }
900
+ if (result.error === PAYSTACK_POPUP_ERROR.SUBMISSION_UNKNOWN) {
932
901
  return {
933
- type: NEXT_ACTION_CARD_POPUP,
934
- provider: checkoutResult.provider?.trim().toLowerCase() || "",
935
- client_secret: checkoutResult.client_secret,
936
- public_key: checkoutResult.public_key
902
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.SUBMITTED,
903
+ reference: context.reference
937
904
  };
938
905
  }
939
- if (checkoutResult.authorization_url) {
906
+ if (!result.error || result.error === PAYSTACK_POPUP_ERROR.CANCELLED || result.error === PAYSTACK_POPUP_ERROR.PAYMENT_CANCELLED) {
907
+ return { status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED };
908
+ }
909
+ if (result.error === PAYSTACK_POPUP_ERROR.POPUP_BLOCKED) {
940
910
  return {
941
- type: NEXT_ACTION_REDIRECT,
942
- authorization_url: checkoutResult.authorization_url
911
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
912
+ code: ErrorCode.POPUP_BLOCKED,
913
+ message: "Unable to open the Paystack payment popup. Please allow popups and try again.",
914
+ recoverable: true
943
915
  };
944
916
  }
945
- if (checkoutResult.requires_authorization && checkoutResult.authorization_type) {
917
+ if (result.error === PAYSTACK_POPUP_ERROR.PROVIDER_UNAVAILABLE) {
946
918
  return {
947
- type: NEXT_ACTION_AUTHORIZATION,
948
- authorization_type: checkoutResult.authorization_type,
949
- display_text: checkoutResult.display_text
919
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
920
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
921
+ message: "Paystack is unavailable in this environment.",
922
+ recoverable: false
950
923
  };
951
924
  }
952
- return { type: NEXT_ACTION_POLL };
925
+ return {
926
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
927
+ code: ErrorCode.PAYMENT_FAILED,
928
+ message: result.error || "Paystack payment could not be submitted.",
929
+ recoverable: true
930
+ };
931
+ }
932
+ };
933
+
934
+ // src/payment-authorization/providers/stripe.ts
935
+ var STRIPE_REDIRECT_MODE = "if_required";
936
+ var STRIPE_PAYMENT_ELEMENT_TYPE = "payment";
937
+ async function loadStripeFromPeer(publicKey, options) {
938
+ try {
939
+ const imported = await import('@stripe/stripe-js');
940
+ return imported.loadStripe ? imported.loadStripe(publicKey, options) : null;
941
+ } catch {
942
+ return null;
943
+ }
944
+ }
945
+ function createStripeModal() {
946
+ if (typeof document === "undefined" || !document.body) return null;
947
+ const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
948
+ const root = document.createElement("div");
949
+ root.setAttribute("role", "dialog");
950
+ root.setAttribute("aria-modal", "true");
951
+ root.setAttribute("aria-label", "Secure card payment");
952
+ Object.assign(root.style, {
953
+ position: "fixed",
954
+ inset: "0",
955
+ zIndex: "2147483647",
956
+ display: "grid",
957
+ placeItems: "center",
958
+ padding: "16px",
959
+ background: "rgba(15, 23, 42, 0.58)"
960
+ });
961
+ const form = document.createElement("form");
962
+ Object.assign(form.style, {
963
+ width: "min(100%, 480px)",
964
+ maxHeight: "calc(100vh - 32px)",
965
+ overflow: "auto",
966
+ borderRadius: "12px",
967
+ padding: "24px",
968
+ background: "#fff",
969
+ boxShadow: "0 24px 80px rgba(15, 23, 42, 0.32)"
970
+ });
971
+ const title = document.createElement("h2");
972
+ title.textContent = "Complete card payment";
973
+ Object.assign(title.style, { margin: "0 0 18px", font: "600 18px/1.4 system-ui" });
974
+ const paymentMount = document.createElement("div");
975
+ paymentMount.setAttribute("data-cimplify-stripe-payment-element", "");
976
+ const error = document.createElement("p");
977
+ error.setAttribute("role", "alert");
978
+ Object.assign(error.style, {
979
+ minHeight: "20px",
980
+ margin: "14px 0 0",
981
+ color: "#b91c1c",
982
+ font: "14px/1.4 system-ui"
983
+ });
984
+ const actions = document.createElement("div");
985
+ Object.assign(actions.style, {
986
+ display: "flex",
987
+ justifyContent: "flex-end",
988
+ gap: "10px",
989
+ marginTop: "18px"
990
+ });
991
+ const cancelButton = document.createElement("button");
992
+ cancelButton.type = "button";
993
+ cancelButton.textContent = "Cancel";
994
+ Object.assign(cancelButton.style, {
995
+ border: "1px solid #cbd5e1",
996
+ borderRadius: "8px",
997
+ padding: "10px 16px",
998
+ background: "#fff",
999
+ color: "#0f172a",
1000
+ cursor: "pointer"
1001
+ });
1002
+ const submitButton = document.createElement("button");
1003
+ submitButton.type = "submit";
1004
+ submitButton.textContent = "Pay securely";
1005
+ Object.assign(submitButton.style, {
1006
+ border: "0",
1007
+ borderRadius: "8px",
1008
+ padding: "10px 16px",
1009
+ background: "#0f172a",
1010
+ color: "#fff",
1011
+ cursor: "pointer"
1012
+ });
1013
+ actions.append(cancelButton, submitButton);
1014
+ form.append(title, paymentMount, error, actions);
1015
+ root.append(form);
1016
+ document.body.append(root);
1017
+ cancelButton.focus();
1018
+ return { root, form, paymentMount, submitButton, cancelButton, error, previousFocus };
1019
+ }
1020
+ function stripeFailure(error) {
1021
+ const message = error?.message || "Stripe could not authorize this payment.";
1022
+ if (error?.code === "payment_intent_authentication_failure" || error?.type === "invalid_request_error") {
1023
+ return {
1024
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1025
+ code: ErrorCode.AUTHORIZATION_FAILED,
1026
+ message,
1027
+ recoverable: true
1028
+ };
953
1029
  }
954
- async runCardPopup(action, checkoutResult) {
955
- 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()) {
1030
+ return {
1031
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1032
+ code: ErrorCode.PAYMENT_FAILED,
1033
+ message,
1034
+ recoverable: true
1035
+ };
1036
+ }
1037
+ var StripeAuthorizationAdapter = class {
1038
+ constructor(loadStripe = loadStripeFromPeer) {
1039
+ this.loadStripe = loadStripe;
1040
+ this.provider = PAYMENT_AUTHORIZATION_PROVIDER.STRIPE;
1041
+ }
1042
+ async authorize(context) {
1043
+ if (typeof window === "undefined" || typeof document === "undefined" || context.signal?.aborted) {
1044
+ return context.signal?.aborted ? { status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED } : {
1045
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1046
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
1047
+ message: "Stripe Payment Element is unavailable in this environment.",
1048
+ recoverable: false
1049
+ };
1050
+ }
1051
+ let stripe;
1052
+ try {
1053
+ stripe = await this.loadStripe(
1054
+ context.action.public_key,
1055
+ context.action.provider_account_id ? { stripeAccount: context.action.provider_account_id } : void 0
1056
+ );
1057
+ } catch (error) {
956
1058
  return {
957
- ok: false,
958
- result: this.fail(
959
- "INVALID_CARD_AUTHORIZATION",
960
- "The payment provider returned an incomplete card authorization response.",
961
- false
962
- )
1059
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1060
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
1061
+ message: error instanceof Error ? error.message : "Stripe.js could not be loaded.",
1062
+ recoverable: true
963
1063
  };
964
1064
  }
965
- this.emit("awaiting_authorization", {
966
- display_text: "Complete payment in the card authorization window.",
967
- order_id: checkoutResult.order_id,
968
- order_number: checkoutResult.order_number,
969
- provider: action.provider
970
- });
971
- if (!this.allowPopups) {
1065
+ if (context.signal?.aborted) {
1066
+ return { status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED };
1067
+ }
1068
+ if (!stripe) {
972
1069
  return {
973
- ok: false,
974
- result: this.fail(
975
- "PROVIDER_UNAVAILABLE",
976
- "Card authorization UI is unavailable in this environment.",
977
- false
978
- )
1070
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1071
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
1072
+ message: "Stripe.js could not be loaded.",
1073
+ recoverable: false
979
1074
  };
980
1075
  }
981
- const currency = this.getOrderCurrency(checkoutResult);
982
- if (!currency) {
1076
+ const modal = createStripeModal();
1077
+ if (!modal) {
983
1078
  return {
984
- ok: false,
985
- result: this.fail("INVALID_CURRENCY", "Payment currency is missing or unsupported.", false)
1079
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1080
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
1081
+ message: "Stripe Payment Element could not be mounted.",
1082
+ recoverable: false
986
1083
  };
987
1084
  }
988
- let outcome;
1085
+ let paymentElement;
1086
+ let elements;
989
1087
  try {
990
- this.notifyAuthorizationUiChange(true);
991
- outcome = await this.client.checkout.authorizeCardPopup(action, {
992
- email: this.checkoutData.customer.email || "customer@cimplify.io",
993
- currency,
994
- reference: checkoutResult.payment_reference,
995
- returnUrl: this.returnUrl,
996
- signal: this.signal
997
- });
998
- } finally {
999
- this.notifyAuthorizationUiChange(false);
1000
- }
1001
- if (outcome.status === "submitted") {
1002
- return { ok: true, reference: outcome.reference };
1003
- }
1004
- if (outcome.status === "cancelled") {
1088
+ elements = stripe.elements({ clientSecret: context.action.client_secret });
1089
+ paymentElement = elements.create(STRIPE_PAYMENT_ELEMENT_TYPE);
1090
+ paymentElement.mount(modal.paymentMount);
1091
+ } catch {
1092
+ modal.root.remove();
1093
+ modal.previousFocus?.focus();
1005
1094
  return {
1006
- ok: false,
1007
- result: this.fail(
1008
- "PAYMENT_CANCELLED",
1009
- "Card payment was cancelled before submission.",
1010
- true
1011
- )
1095
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1096
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
1097
+ message: "Stripe Payment Element could not be initialized.",
1098
+ recoverable: false
1012
1099
  };
1013
1100
  }
1014
- return {
1015
- ok: false,
1016
- result: this.fail(outcome.code, outcome.message, outcome.recoverable)
1017
- };
1018
- }
1019
- async resolveNextAction(checkoutResult, resolutionState = {
1020
- authorizationSteps: 0,
1021
- pollAttempts: 0,
1022
- consecutivePollErrors: 0
1023
- }) {
1024
- if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
1025
- return this.finalizeSuccess(checkoutResult);
1026
- }
1027
- let latestCheckoutResult = checkoutResult;
1028
- let paymentReference = checkoutResult.payment_reference;
1029
- let action = checkoutResult.next_action;
1030
- actionLoop: while (true) {
1031
- this.ensureNotAborted();
1032
- switch (action.type) {
1033
- case NEXT_ACTION_NONE:
1034
- break actionLoop;
1035
- case NEXT_ACTION_CARD_POPUP: {
1036
- const limitFailure = this.consumeAuthorizationStep(resolutionState);
1037
- if (limitFailure) return limitFailure;
1038
- const popup = await this.runCardPopup(action, latestCheckoutResult);
1039
- if (!popup.ok) return popup.result;
1040
- paymentReference = popup.reference || paymentReference;
1041
- break actionLoop;
1101
+ if (context.signal?.aborted) {
1102
+ if (paymentElement.destroy) paymentElement.destroy();
1103
+ else paymentElement.unmount?.();
1104
+ modal.root.remove();
1105
+ modal.previousFocus?.focus();
1106
+ return { status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED };
1107
+ }
1108
+ return new Promise((resolve) => {
1109
+ let settled = false;
1110
+ let submitting = false;
1111
+ let confirmationStarted = false;
1112
+ const cleanup = () => {
1113
+ context.signal?.removeEventListener("abort", onAbort);
1114
+ modal.form.removeEventListener("submit", onSubmit);
1115
+ modal.cancelButton.removeEventListener("click", onCancel);
1116
+ if (paymentElement.destroy) paymentElement.destroy();
1117
+ else paymentElement.unmount?.();
1118
+ modal.root.remove();
1119
+ modal.previousFocus?.focus();
1120
+ };
1121
+ const settle = (outcome) => {
1122
+ if (settled) return;
1123
+ settled = true;
1124
+ cleanup();
1125
+ resolve(outcome);
1126
+ };
1127
+ const onAbort = () => {
1128
+ if (confirmationStarted) {
1129
+ settle({
1130
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.SUBMITTED,
1131
+ reference: context.reference
1132
+ });
1133
+ return;
1042
1134
  }
1043
- case NEXT_ACTION_REDIRECT: {
1044
- const limitFailure = this.consumeAuthorizationStep(resolutionState);
1045
- if (limitFailure) return limitFailure;
1046
- const authorizationUrl = paymentAuthorizationUrl(action.authorization_url);
1047
- if (!authorizationUrl) {
1048
- return this.fail(
1049
- "INVALID_REDIRECT_AUTHORIZATION",
1050
- "The payment provider returned an invalid authorization URL.",
1051
- false
1052
- );
1053
- }
1054
- if (typeof window !== "undefined" && this.returnUrl) {
1055
- window.location.assign(authorizationUrl);
1056
- return this.fail(
1057
- "REDIRECT_REQUIRED",
1058
- "Redirecting to complete payment authorization.",
1059
- true
1060
- );
1061
- }
1062
- if (typeof window !== "undefined") {
1063
- const popup = window.open(
1064
- authorizationUrl,
1065
- "cimplify-auth",
1066
- "width=520,height=760,noopener,noreferrer"
1067
- );
1068
- if (!popup) {
1069
- return this.fail(
1070
- "POPUP_BLOCKED",
1071
- "Authorization popup was blocked. Please allow popups and retry.",
1072
- true
1073
- );
1074
- }
1075
- }
1076
- break actionLoop;
1135
+ settle({ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED });
1136
+ };
1137
+ const onCancel = () => {
1138
+ if (!confirmationStarted) {
1139
+ settle({ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED });
1077
1140
  }
1078
- case NEXT_ACTION_AUTHORIZATION: {
1079
- const limitFailure = this.consumeAuthorizationStep(resolutionState);
1080
- if (limitFailure) return limitFailure;
1081
- const authType = normalizeAuthorizationType(action.authorization_type);
1082
- const authorization = await this.handleAuthorization({
1083
- authorizationType: authType,
1084
- paymentId: latestCheckoutResult.payment_id,
1085
- displayText: action.display_text,
1086
- provider: latestCheckoutResult.provider,
1087
- orderId: latestCheckoutResult.order_id,
1088
- orderNumber: latestCheckoutResult.order_number
1089
- });
1090
- if (!authorization.ok) {
1091
- return authorization.result;
1141
+ };
1142
+ const onSubmit = async (event) => {
1143
+ event.preventDefault();
1144
+ if (submitting || settled) return;
1145
+ submitting = true;
1146
+ modal.submitButton.disabled = true;
1147
+ modal.error.textContent = "";
1148
+ try {
1149
+ const validation = elements.submit ? await elements.submit() : {};
1150
+ if (validation.error) {
1151
+ modal.error.textContent = validation.error.message || "Check your payment details.";
1152
+ submitting = false;
1153
+ modal.submitButton.disabled = false;
1154
+ return;
1092
1155
  }
1093
- if (!authorization.value) {
1094
- break actionLoop;
1156
+ if (settled || context.signal?.aborted) {
1157
+ settle({ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED });
1158
+ return;
1095
1159
  }
1096
- latestCheckoutResult = authorization.value;
1097
- paymentReference = authorization.value.payment_reference || paymentReference;
1098
- if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1099
- return this.finalizeSuccess(authorization.value);
1160
+ confirmationStarted = true;
1161
+ modal.cancelButton.disabled = true;
1162
+ const confirmation = await stripe.confirmPayment({
1163
+ elements,
1164
+ confirmParams: { return_url: context.returnUrl || window.location.href },
1165
+ redirect: STRIPE_REDIRECT_MODE
1166
+ });
1167
+ if (settled) return;
1168
+ if (confirmation.error) {
1169
+ settle(stripeFailure(confirmation.error));
1170
+ return;
1100
1171
  }
1101
- if (this.isFailureStatus(authorization.value.payment_status)) {
1102
- return this.fail(
1103
- "AUTHORIZATION_FAILED",
1104
- authorization.value.display_text || "Payment authorization failed.",
1105
- false
1106
- );
1172
+ settle({
1173
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.SUBMITTED,
1174
+ reference: confirmation.paymentIntent?.id || context.reference
1175
+ });
1176
+ } catch (error) {
1177
+ if (confirmationStarted) {
1178
+ settle({
1179
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.SUBMITTED,
1180
+ reference: context.reference
1181
+ });
1182
+ return;
1107
1183
  }
1108
- action = authorization.value.next_action ?? this.deriveLegacyNextAction(authorization.value);
1109
- continue actionLoop;
1110
- }
1111
- case NEXT_ACTION_POLL:
1112
- break actionLoop;
1113
- default:
1114
- return this.fail(
1115
- "UNSUPPORTED_NEXT_ACTION",
1116
- "The payment provider requested an unsupported authorization action.",
1117
- false
1184
+ settle(
1185
+ stripeFailure({
1186
+ message: error instanceof Error ? error.message : "Stripe confirmation failed."
1187
+ })
1118
1188
  );
1119
- }
1120
- }
1121
- return this.pollUntilTerminal({
1122
- orderId: latestCheckoutResult.order_id,
1123
- latestCheckoutResult,
1124
- paymentReference,
1125
- resolutionState
1189
+ }
1190
+ };
1191
+ modal.form.addEventListener("submit", onSubmit);
1192
+ modal.cancelButton.addEventListener("click", onCancel);
1193
+ context.signal?.addEventListener("abort", onAbort, { once: true });
1194
+ paymentElement.on?.("escape", onCancel);
1195
+ paymentElement.on?.("loaderror", (event) => {
1196
+ if (confirmationStarted) return;
1197
+ settle({
1198
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1199
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
1200
+ message: event?.error?.message || "Stripe Payment Element failed to load.",
1201
+ recoverable: true
1202
+ });
1203
+ });
1126
1204
  });
1127
1205
  }
1128
- consumeAuthorizationStep(resolutionState) {
1129
- if (resolutionState.authorizationSteps >= MAX_PAYMENT_AUTHORIZATION_STEPS) {
1130
- return this.fail(
1131
- "AUTHORIZATION_LIMIT_EXCEEDED",
1132
- "The payment provider requested too many authorization steps.",
1133
- false
1134
- );
1206
+ };
1207
+
1208
+ // src/payment-authorization/registry.ts
1209
+ function normalizeProvider(provider) {
1210
+ return typeof provider === "string" ? provider.trim().toLowerCase() : "";
1211
+ }
1212
+ var PaymentAuthorizationAdapterRegistry = class {
1213
+ constructor(adapters = []) {
1214
+ this.adapters = /* @__PURE__ */ new Map();
1215
+ for (const adapter of adapters) {
1216
+ this.register(adapter);
1135
1217
  }
1136
- resolutionState.authorizationSteps += 1;
1137
- return void 0;
1138
1218
  }
1139
- async pollUntilTerminal(input) {
1140
- const latestCheckoutResult = input.latestCheckoutResult;
1141
- let paymentReference = input.paymentReference;
1142
- const { resolutionState } = input;
1143
- while (resolutionState.pollAttempts < this.maxPollAttempts) {
1144
- this.ensureNotAborted();
1145
- const attempt = resolutionState.pollAttempts;
1146
- resolutionState.pollAttempts += 1;
1147
- this.emit("polling", {
1148
- poll_attempt: attempt,
1149
- max_poll_attempts: this.maxPollAttempts,
1150
- order_id: input.orderId,
1151
- order_number: latestCheckoutResult.order_number
1152
- });
1153
- const statusResult = await this.client.checkout.pollPaymentStatus(input.orderId);
1154
- if (!statusResult.ok) {
1155
- resolutionState.consecutivePollErrors += 1;
1156
- if (resolutionState.consecutivePollErrors >= MAX_CONSECUTIVE_NETWORK_ERRORS) {
1157
- return this.fail(
1158
- "NETWORK_ERROR",
1159
- "Unable to confirm payment due to repeated network errors.",
1160
- true
1161
- );
1162
- }
1163
- await this.wait(this.pollIntervalMs);
1164
- continue;
1165
- }
1166
- resolutionState.consecutivePollErrors = 0;
1167
- if (statusResult.value.reference) {
1168
- paymentReference = statusResult.value.reference;
1169
- }
1170
- const normalized = normalizeStatusResponse(statusResult.value);
1171
- if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
1172
- return this.finalizeSuccess({
1173
- ...latestCheckoutResult,
1174
- payment_status: normalized.status,
1175
- payment_reference: paymentReference || latestCheckoutResult.payment_reference
1176
- });
1177
- }
1178
- if (isPaymentStatusFailure(normalized.status)) {
1179
- return this.fail(
1180
- normalized.status ? normalized.status.toUpperCase() : "PAYMENT_FAILED",
1181
- normalized.message || "Payment failed during confirmation.",
1182
- false
1183
- );
1184
- }
1185
- if (isPaymentStatusRequiresAction(normalized.status)) {
1186
- const authorizationType = latestCheckoutResult.authorization_type;
1187
- const paymentId = latestCheckoutResult.payment_id;
1188
- if (!authorizationType || !normalizeAuthorizationType(authorizationType) || !paymentId) {
1189
- return this.fail(
1190
- "INVALID_AUTHORIZATION_RESPONSE",
1191
- "The payment provider returned an incomplete authorization response.",
1192
- false
1193
- );
1194
- }
1195
- return this.resolveNextAction(
1196
- {
1197
- ...latestCheckoutResult,
1198
- payment_status: normalized.status,
1199
- payment_reference: paymentReference,
1200
- display_text: normalized.message || latestCheckoutResult.display_text,
1201
- next_action: {
1202
- type: NEXT_ACTION_AUTHORIZATION,
1203
- authorization_type: authorizationType,
1204
- display_text: normalized.message || latestCheckoutResult.display_text
1205
- }
1206
- },
1207
- resolutionState
1208
- );
1209
- }
1210
- await this.wait(this.pollIntervalMs);
1219
+ register(adapter) {
1220
+ const provider = normalizeProvider(adapter.provider);
1221
+ if (!provider) {
1222
+ throw new TypeError("Payment authorization adapter provider cannot be empty.");
1211
1223
  }
1212
- return this.fail(
1213
- "PAYMENT_TIMEOUT",
1214
- "Payment confirmation timed out. Please retry checkout.",
1215
- true
1216
- );
1224
+ this.adapters.set(provider, adapter);
1225
+ return this;
1217
1226
  }
1218
- async handleAuthorization(input) {
1219
- const authorizationType = input.authorizationType;
1220
- const paymentId = input.paymentId;
1221
- if (!authorizationType || !paymentId) {
1222
- return {
1223
- ok: false,
1224
- result: this.fail(
1225
- "INVALID_AUTHORIZATION_RESPONSE",
1226
- "The payment provider returned an incomplete authorization response.",
1227
- false
1228
- )
1229
- };
1230
- }
1231
- this.emit("awaiting_authorization", {
1232
- authorization_type: authorizationType,
1233
- display_text: input.displayText,
1234
- provider: input.provider,
1235
- order_id: input.orderId,
1236
- order_number: input.orderNumber
1237
- });
1238
- let authorizationRequest;
1239
- const submit = (authorization) => {
1240
- this.ensureNotAborted();
1241
- let normalizedAuthorization;
1242
- try {
1243
- normalizedAuthorization = normalizeAuthorizationSubmission(
1244
- authorizationType,
1245
- authorization
1246
- );
1247
- } catch (error) {
1248
- return Promise.reject(error);
1249
- }
1250
- if (!authorizationRequest) {
1251
- const request = this.client.checkout.submitAuthorization({
1252
- payment_id: paymentId,
1253
- authorization: normalizedAuthorization
1254
- }).then((submitResult) => {
1255
- if (!submitResult.ok) {
1256
- throw new Error(submitResult.error.message || "Authorization failed.");
1257
- }
1258
- return submitResult.value;
1259
- });
1260
- authorizationRequest = request;
1261
- void request.catch(() => {
1262
- if (authorizationRequest === request) {
1263
- authorizationRequest = void 0;
1264
- }
1265
- });
1266
- }
1267
- return authorizationRequest.then(() => void 0);
1268
- };
1269
- try {
1270
- if (this.onAuthorizationRequired) {
1271
- await waitForAbortable(
1272
- Promise.resolve(this.onAuthorizationRequired(authorizationType, submit)),
1273
- this.signal
1274
- );
1275
- } else {
1276
- return {
1277
- ok: false,
1278
- result: this.fail(
1279
- "AUTHORIZATION_REQUIRED",
1280
- "Authorization callback is required in headless checkout mode.",
1281
- false
1282
- )
1283
- };
1284
- }
1285
- if (!authorizationRequest) {
1286
- return {
1287
- ok: false,
1288
- result: this.fail(
1289
- "AUTHORIZATION_NOT_SUBMITTED",
1290
- "The authorization callback returned without submitting a value.",
1291
- true
1292
- )
1293
- };
1294
- }
1295
- return {
1296
- ok: true,
1297
- value: await waitForAbortable(authorizationRequest, this.signal)
1298
- };
1299
- } catch (error) {
1300
- if (isAbortError(error)) {
1301
- return {
1302
- ok: false,
1303
- result: this.fail("CANCELLED", "Checkout was cancelled.", true)
1304
- };
1305
- }
1306
- const message = error instanceof Error ? error.message : "Payment authorization failed.";
1307
- return {
1308
- ok: false,
1309
- result: this.fail("AUTHORIZATION_FAILED", message, true)
1310
- };
1311
- }
1227
+ unregister(provider) {
1228
+ return this.adapters.delete(normalizeProvider(provider));
1312
1229
  }
1313
- async finalizeSuccess(checkoutResult) {
1314
- const currency = this.getOrderCurrency(checkoutResult);
1315
- if (!currency) {
1316
- return this.fail("INVALID_CURRENCY", "Payment currency is missing or unsupported.", false);
1317
- }
1318
- this.emit("finalizing", {
1319
- order_id: checkoutResult.order_id,
1320
- order_number: checkoutResult.order_number
1321
- });
1322
- const enrolledInLink = await this.maybeEnrollInLink(checkoutResult.order_id);
1323
- this.emit("success", {
1324
- order_id: checkoutResult.order_id,
1325
- order_number: checkoutResult.order_number
1326
- });
1327
- return {
1328
- success: true,
1329
- order: {
1330
- id: checkoutResult.order_id,
1331
- order_number: checkoutResult.order_number || checkoutResult.order_id,
1332
- status: checkoutResult.payment_status || "paid",
1333
- total: this.getOrderTotal(checkoutResult),
1334
- currency
1335
- },
1336
- enrolled_in_link: enrolledInLink
1337
- };
1230
+ get(provider) {
1231
+ return this.adapters.get(normalizeProvider(provider));
1338
1232
  }
1339
- async maybeEnrollInLink(orderId) {
1340
- if (!this.enrollInLink || !orderId) {
1341
- return false;
1342
- }
1343
- let businessId = this.businessId;
1344
- if (!businessId) {
1345
- const businessResult = await this.client.business.getInfo();
1346
- if (!businessResult.ok || !businessResult.value?.id) {
1347
- return false;
1348
- }
1349
- businessId = businessResult.value.id;
1350
- }
1351
- const address = this.getEnrollmentAddress();
1352
- const mobileMoney = this.getEnrollmentMobileMoney();
1353
- try {
1354
- const enrollment = await this.client.link.enrollAndLinkOrder({
1355
- order_id: orderId,
1356
- business_id: businessId,
1357
- order_type: this.orderType || this.checkoutData.order_type,
1358
- address,
1359
- mobile_money: mobileMoney
1360
- });
1361
- return enrollment.ok && enrollment.value.success;
1362
- } catch {
1363
- return false;
1364
- }
1233
+ supports(provider) {
1234
+ return this.adapters.has(normalizeProvider(provider));
1365
1235
  }
1366
- getEnrollmentAddress() {
1367
- const source = this.addressData ?? this.checkoutData.address_info;
1368
- if (!source?.street_address) {
1369
- return void 0;
1370
- }
1371
- return {
1372
- label: "Default",
1373
- street_address: source.street_address,
1374
- apartment: source.apartment || void 0,
1375
- city: source.city || "",
1376
- region: source.region || "",
1377
- postal_code: source.postal_code || void 0,
1378
- country: source.country || void 0,
1379
- delivery_instructions: source.delivery_instructions || void 0,
1380
- phone_for_delivery: source.phone_for_delivery || void 0,
1381
- latitude: source.latitude,
1382
- longitude: source.longitude
1383
- };
1236
+ providers() {
1237
+ return [...this.adapters.keys()];
1384
1238
  }
1385
- getEnrollmentMobileMoney() {
1386
- if (this.paymentData?.type === PAYMENT_METHOD.MOBILE_MONEY && this.paymentData.phone_number && this.paymentData.provider && isValidMobileMoneyProvider(this.paymentData.provider)) {
1239
+ async authorize(action, options) {
1240
+ const provider = normalizeProvider(action.provider);
1241
+ if (!provider) {
1387
1242
  return {
1388
- phone_number: this.paymentData.phone_number,
1389
- provider: this.paymentData.provider,
1390
- label: this.paymentData.label || "Mobile Money"
1243
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1244
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
1245
+ message: "Card authorization response did not identify a payment provider.",
1246
+ recoverable: false
1391
1247
  };
1392
1248
  }
1393
- if (this.checkoutData.mobile_money_details?.phone_number) {
1249
+ const adapter = this.adapters.get(provider);
1250
+ if (!adapter) {
1394
1251
  return {
1395
- phone_number: this.checkoutData.mobile_money_details.phone_number,
1396
- provider: this.checkoutData.mobile_money_details.provider,
1397
- label: "Mobile Money"
1252
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1253
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
1254
+ message: `Card authorization provider '${provider}' is not supported by this SDK client.`,
1255
+ recoverable: false
1398
1256
  };
1399
1257
  }
1400
- return void 0;
1401
- }
1402
- getOrderTotal(checkoutResult) {
1403
- if (checkoutResult.fx?.pay_amount !== void 0) {
1404
- return formatMoney(checkoutResult.fx.pay_amount);
1405
- }
1406
- if (this.cartTotal !== void 0) {
1407
- return formatMoney(this.cartTotal);
1258
+ try {
1259
+ const normalizedAction = action.provider === provider ? action : { ...action, provider };
1260
+ return await adapter.authorize({ ...options, action: normalizedAction });
1261
+ } catch (error) {
1262
+ return {
1263
+ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.FAILED,
1264
+ code: ErrorCode.PAYMENT_FAILED,
1265
+ message: error instanceof Error ? error.message : `Card authorization provider '${provider}' failed unexpectedly.`,
1266
+ recoverable: true
1267
+ };
1408
1268
  }
1409
- return "0.00";
1410
1269
  }
1411
- getOrderCurrency(checkoutResult) {
1412
- if (checkoutResult.fx?.pay_currency) {
1413
- const currency2 = checkoutResult.fx.pay_currency.trim().toUpperCase();
1414
- return isSupportedCurrency(currency2) ? currency2 : void 0;
1415
- }
1416
- if (this.checkoutData.pay_currency) {
1417
- const currency2 = this.checkoutData.pay_currency.trim().toUpperCase();
1418
- return isSupportedCurrency(currency2) ? currency2 : void 0;
1419
- }
1420
- const currency = this.cartCurrency.trim().toUpperCase();
1421
- return isSupportedCurrency(currency) ? currency : void 0;
1270
+ };
1271
+ function createDefaultPaymentAuthorizationAdapterRegistry() {
1272
+ return new PaymentAuthorizationAdapterRegistry([
1273
+ new PaystackAuthorizationAdapter(),
1274
+ new StripeAuthorizationAdapter()
1275
+ ]);
1276
+ }
1277
+
1278
+ // src/utils/purchase-intent-confirmation.ts
1279
+ var EXACT_ZERO_MONEY = /^[+-]?0+(?:\.0+)?(?:[eE][+-]?\d+)?$/;
1280
+ function isExactlyZeroMoney(value) {
1281
+ if (typeof value === "number") return Object.is(value, 0) || Object.is(value, -0);
1282
+ return EXACT_ZERO_MONEY.test(value.trim());
1283
+ }
1284
+ function selectPurchaseIntentPaymentOption(terms, selection) {
1285
+ return terms.payment_options.filter((option) => option.status === PAYMENT_OPTION_STATUS.ELIGIBLE).sort((left, right) => left.rank - right.rank).find(
1286
+ (option) => option.method_kind === selection.method_kind && (selection.payment_option_id === void 0 || option.id === selection.payment_option_id)
1287
+ );
1288
+ }
1289
+ function createPurchaseIntentConfirmation(terms, paymentOption, idempotencyKey) {
1290
+ const normalizedKey = idempotencyKey.trim();
1291
+ if (!normalizedKey) throw new TypeError("idempotencyKey must not be empty");
1292
+ const requiresOption = purchaseIntentRequiresPaymentOption(terms);
1293
+ if (!paymentOption && requiresOption) {
1294
+ throw new TypeError("Nonzero rail terms require an eligible payment option");
1295
+ }
1296
+ if (paymentOption && !requiresOption) {
1297
+ throw new TypeError("Zero-rail terms must not name a payment option");
1298
+ }
1299
+ const authoritativeOption = paymentOption ? terms.payment_options.find(
1300
+ (candidate) => candidate.id === paymentOption.id && candidate.status === PAYMENT_OPTION_STATUS.ELIGIBLE
1301
+ ) : void 0;
1302
+ if (paymentOption && !authoritativeOption) {
1303
+ throw new TypeError("Payment option is not eligible for these checkout terms");
1422
1304
  }
1423
- emit(status, context = {}) {
1424
- if (!this.onStatusChange) {
1425
- return;
1305
+ return {
1306
+ accepted_intent_revision: terms.intent_revision,
1307
+ accepted_terms_version: terms.terms_version,
1308
+ accepted_due_now: terms.money.due_now,
1309
+ // Rail fees and routes may differ by option. Consent is to the exact
1310
+ // server-issued buyer debit, never an ambiguous principal/fee aggregate.
1311
+ accepted_customer_debit_amount: authoritativeOption?.customer_debit_amount ?? terms.money.rail_amount,
1312
+ currency: terms.money.currency,
1313
+ payment_option_id: authoritativeOption?.id ?? null,
1314
+ idempotency_key: normalizedKey
1315
+ };
1316
+ }
1317
+ function purchaseIntentRequiresPaymentOption(terms) {
1318
+ return terms.payment_options.length > 0 || !isExactlyZeroMoney(terms.money.rail_amount);
1319
+ }
1320
+
1321
+ // src/utils/purchase-intent-adapter.ts
1322
+ function tenderPreference(form) {
1323
+ if (form.payment_method === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY) {
1324
+ if (form.link_payment_method_id) {
1325
+ return {
1326
+ method_kind: PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY,
1327
+ authority: {
1328
+ authority_kind: MOBILE_MONEY_TENDER_AUTHORITY_KIND.SAVED_REFERENCE,
1329
+ stored_instrument_id: form.link_payment_method_id
1330
+ }
1331
+ };
1426
1332
  }
1427
- try {
1428
- this.onStatusChange(status, context);
1429
- } catch {
1333
+ const details = form.mobile_money_details;
1334
+ if (!details?.phone_number || !details.provider) {
1335
+ throw new CimplifyError(
1336
+ ErrorCode.INVALID_PAYMENT_METHOD,
1337
+ "Mobile money requires a phone number and network.",
1338
+ false
1339
+ );
1430
1340
  }
1431
- }
1432
- notifyAuthorizationUiChange(open) {
1433
- try {
1434
- this.onAuthorizationUiChange?.(open);
1435
- } catch {
1341
+ const network = purchaseIntentMobileMoneyNetwork(details.provider);
1342
+ if (!network) {
1343
+ throw new CimplifyError(
1344
+ ErrorCode.INVALID_PAYMENT_METHOD,
1345
+ "That mobile money network is not available for PurchaseIntent checkout.",
1346
+ false
1347
+ );
1436
1348
  }
1437
- }
1438
- fail(code, message, recoverable) {
1439
- this.emit("failed", {
1440
- display_text: message
1441
- });
1442
1349
  return {
1443
- success: false,
1444
- error: {
1445
- code,
1446
- message,
1447
- recoverable
1350
+ method_kind: PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY,
1351
+ authority: {
1352
+ authority_kind: MOBILE_MONEY_TENDER_AUTHORITY_KIND.TRANSIENT,
1353
+ phone_number: details.phone_number,
1354
+ network
1448
1355
  }
1449
1356
  };
1450
1357
  }
1451
- isSuccessfulStatus(status) {
1452
- const normalized = normalizeStatusResponse({ status, paid: false });
1453
- return normalized.paid || isPaymentStatusSuccess(normalized.status);
1358
+ if (form.payment_method === LEGACY_CHECKOUT_PAYMENT_METHOD_ALIAS.CASH || form.payment_method === PURCHASE_INTENT_PAYMENT_METHOD.OFFLINE) {
1359
+ throw new CimplifyError(
1360
+ ErrorCode.INVALID_PAYMENT_METHOD,
1361
+ "Offline tender requires an authenticated staff checkout.",
1362
+ false
1363
+ );
1364
+ }
1365
+ if (form.payment_method === PURCHASE_INTENT_PAYMENT_METHOD.CARD) {
1366
+ return {
1367
+ method_kind: PURCHASE_INTENT_PAYMENT_METHOD.CARD,
1368
+ stored_instrument_id: form.link_payment_method_id
1369
+ };
1454
1370
  }
1455
- isFailureStatus(status) {
1456
- const normalized = normalizeStatusResponse({ status, paid: false });
1457
- return isPaymentStatusFailure(normalized.status);
1371
+ if (form.payment_method === LEGACY_CHECKOUT_PAYMENT_METHOD_ALIAS.STORE_CREDIT) {
1372
+ return void 0;
1458
1373
  }
1459
- ensureNotAborted() {
1460
- if (this.signal?.aborted) {
1461
- throw createAbortError();
1374
+ throw new CimplifyError(
1375
+ ErrorCode.INVALID_PAYMENT_METHOD,
1376
+ "Checkout payment method is not supported.",
1377
+ false
1378
+ );
1379
+ }
1380
+ function checkoutFormToIntentRequest(form, clientIntentKey) {
1381
+ return {
1382
+ client_intent_key: clientIntentKey,
1383
+ source: { kind: PURCHASE_INTENT_SOURCE_KIND.CART, cart_id: form.cart_id },
1384
+ checkout: {
1385
+ customer: form.customer,
1386
+ order_type: form.order_type,
1387
+ address_info: form.address_info,
1388
+ location_id: form.location_id,
1389
+ delivery_rate_id: form.delivery_rate_id,
1390
+ special_instructions: form.special_instructions,
1391
+ link_address_id: form.link_address_id,
1392
+ metadata: form.metadata,
1393
+ pay_currency: form.pay_currency,
1394
+ fx_quote_id: form.fx_quote_id,
1395
+ use_store_credit: form.use_store_credit || form.payment_method === LEGACY_CHECKOUT_PAYMENT_METHOD_ALIAS.STORE_CREDIT,
1396
+ voucher_code: form.voucher_code,
1397
+ pay_deposit: form.pay_deposit,
1398
+ tender_preference: tenderPreference(form)
1462
1399
  }
1463
- }
1464
- wait(ms) {
1465
- this.ensureNotAborted();
1466
- return new Promise((resolve, reject) => {
1467
- const timer = setTimeout(() => {
1468
- if (this.signal) {
1469
- this.signal.removeEventListener("abort", onAbort);
1400
+ };
1401
+ }
1402
+ function selectCheckoutPaymentOption(terms, form) {
1403
+ const preference = tenderPreference(form);
1404
+ if (!preference) return void 0;
1405
+ return terms.payment_options.filter((option) => option.status === PAYMENT_OPTION_STATUS.ELIGIBLE).sort((left, right) => left.rank - right.rank).find((option) => option.method_kind === preference.method_kind);
1406
+ }
1407
+ function checkoutFormToConfirmation(_form, terms, paymentOption, idempotencyKey) {
1408
+ return createPurchaseIntentConfirmation(terms, paymentOption, idempotencyKey);
1409
+ }
1410
+ function committedBase(response) {
1411
+ return {
1412
+ intent_id: response.intent_id,
1413
+ attempt_id: response.attempt_id,
1414
+ order_id: response.order_id,
1415
+ order_number: response.order_number || response.order_id,
1416
+ payment_id: response.payment_id
1417
+ };
1418
+ }
1419
+ function confirmationToCheckoutResult(response) {
1420
+ switch (response.status) {
1421
+ case PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED:
1422
+ return ok({
1423
+ ...committedBase(response),
1424
+ payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.SUCCEEDED,
1425
+ requires_authorization: false,
1426
+ next_action: { type: CHECKOUT_NEXT_ACTION.NONE }
1427
+ });
1428
+ case PURCHASE_INTENT_CONFIRM_STATUS.COLLECTION_PROCESSING:
1429
+ return ok({
1430
+ ...committedBase(response),
1431
+ payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.PROCESSING,
1432
+ requires_authorization: false,
1433
+ next_action: { type: CHECKOUT_NEXT_ACTION.POLL }
1434
+ });
1435
+ case PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_OUTCOME_UNKNOWN:
1436
+ return ok({
1437
+ ...committedBase(response),
1438
+ payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.UNKNOWN,
1439
+ requires_authorization: false,
1440
+ next_action: { type: CHECKOUT_NEXT_ACTION.POLL }
1441
+ });
1442
+ case PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_FAILED:
1443
+ return err(
1444
+ new CimplifyError(
1445
+ PURCHASE_INTENT_ERROR_CODE.PAYMENT_FAILED,
1446
+ "Payment failed. The order remains recorded, but collection did not succeed.",
1447
+ true
1448
+ )
1449
+ );
1450
+ case PURCHASE_INTENT_CONFIRM_STATUS.REDIRECT_REQUIRED:
1451
+ return ok({
1452
+ ...committedBase(response),
1453
+ payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.REQUIRES_ACTION,
1454
+ requires_authorization: false,
1455
+ authorization_url: response.authorization_url,
1456
+ next_action: {
1457
+ type: CHECKOUT_NEXT_ACTION.REDIRECT,
1458
+ authorization_url: response.authorization_url
1470
1459
  }
1471
- resolve();
1472
- }, ms);
1473
- const onAbort = () => {
1474
- clearTimeout(timer);
1475
- reject(createAbortError());
1476
- };
1477
- if (this.signal) {
1478
- this.signal.addEventListener("abort", onAbort, { once: true });
1479
- }
1480
- });
1460
+ });
1461
+ case PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED:
1462
+ return ok({
1463
+ ...committedBase(response),
1464
+ payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.REQUIRES_ACTION,
1465
+ requires_authorization: false,
1466
+ provider: response.provider,
1467
+ client_secret: response.client_secret,
1468
+ public_key: response.public_key,
1469
+ next_action: {
1470
+ type: CHECKOUT_NEXT_ACTION.CARD_POPUP,
1471
+ provider: response.provider,
1472
+ client_secret: response.client_secret,
1473
+ public_key: response.public_key,
1474
+ provider_account_id: response.provider_account_id ?? void 0
1475
+ }
1476
+ });
1477
+ case PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED:
1478
+ return ok({
1479
+ ...committedBase(response),
1480
+ payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.REQUIRES_ACTION,
1481
+ requires_authorization: true,
1482
+ authorization_type: response.authorization_type,
1483
+ display_text: response.display_text,
1484
+ next_action: {
1485
+ type: CHECKOUT_NEXT_ACTION.AUTHORIZATION,
1486
+ authorization_type: response.authorization_type,
1487
+ display_text: response.display_text
1488
+ }
1489
+ });
1490
+ case PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED:
1491
+ return err(
1492
+ new CimplifyError(
1493
+ PURCHASE_INTENT_ERROR_CODE.TERMS_CHANGED,
1494
+ "Checkout terms changed. Review the replacement terms before confirming again.",
1495
+ true
1496
+ )
1497
+ );
1498
+ case PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE:
1499
+ return err(
1500
+ new CimplifyError(
1501
+ PURCHASE_INTENT_ERROR_CODE.SELECTION_UNAVAILABLE,
1502
+ "One or more checkout selections are no longer available.",
1503
+ true
1504
+ )
1505
+ );
1506
+ case PURCHASE_INTENT_CONFIRM_STATUS.OFFER_ENDED:
1507
+ return err(
1508
+ new CimplifyError(
1509
+ PURCHASE_INTENT_ERROR_CODE.OFFER_ENDED,
1510
+ "This offer has ended.",
1511
+ false
1512
+ )
1513
+ );
1514
+ case PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE:
1515
+ return err(
1516
+ new CimplifyError(
1517
+ PURCHASE_INTENT_ERROR_CODE.SOURCE_UNAVAILABLE,
1518
+ "This purchase source is unavailable.",
1519
+ false
1520
+ )
1521
+ );
1481
1522
  }
1482
- };
1523
+ }
1483
1524
 
1484
1525
  // src/checkout.ts
1526
+ function checkoutActionAuthority(response) {
1527
+ if (response.status !== PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED && response.status !== PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED) {
1528
+ return null;
1529
+ }
1530
+ const paymentId = response.payment_id?.trim();
1531
+ return paymentId ? {
1532
+ intentId: response.intent_id,
1533
+ attemptId: response.attempt_id,
1534
+ paymentId
1535
+ } : null;
1536
+ }
1537
+ function isAmbiguousCheckoutError(error) {
1538
+ return error.code === ErrorCode.NETWORK_ERROR || error.code === ErrorCode.TIMEOUT;
1539
+ }
1540
+ function authorizationKind(value) {
1541
+ if (value && typeof value === "object" && AUTHORIZATION_TYPE.ADDRESS in value) {
1542
+ return AUTHORIZATION_TYPE.ADDRESS;
1543
+ }
1544
+ if (value === AUTHORIZATION_TYPE.OTP || value === AUTHORIZATION_TYPE.PIN || value === AUTHORIZATION_TYPE.PHONE || value === AUTHORIZATION_TYPE.BIRTHDAY) {
1545
+ return value;
1546
+ }
1547
+ return void 0;
1548
+ }
1549
+ function normalizeAuthorizationSubmission(expected, submission) {
1550
+ if (submission.type !== expected) throw new Error(`Expected ${expected} authorization details.`);
1551
+ if (submission.type === AUTHORIZATION_TYPE.ADDRESS) {
1552
+ const address = submission.address.trim();
1553
+ const city = submission.city.trim();
1554
+ const state = submission.state.trim();
1555
+ const zipCode = submission.zip_code.trim();
1556
+ if (!address || !city || !state || !zipCode) {
1557
+ throw new Error("Complete all authorization address fields.");
1558
+ }
1559
+ return { type: AUTHORIZATION_TYPE.ADDRESS, address, city, state, zip_code: zipCode };
1560
+ }
1561
+ const value = submission.value.trim();
1562
+ if (!value) throw new Error("Authorization value is required.");
1563
+ return { type: submission.type, value };
1564
+ }
1565
+ var DECIMAL_WIRE_VALUE = /^([+-]?)(\d+)(?:\.(\d+))?(?:[eE][+-]?\d+)?$/;
1566
+ function decimalSign(value) {
1567
+ if (typeof value === "number") {
1568
+ if (!Number.isFinite(value)) return null;
1569
+ if (Object.is(value, 0) || Object.is(value, -0)) return 0;
1570
+ return value > 0 ? 1 : -1;
1571
+ }
1572
+ const match = DECIMAL_WIRE_VALUE.exec(value.trim());
1573
+ if (!match) return null;
1574
+ const nonzero = [...match[2], ...match[3] ?? ""].some((digit) => digit !== "0");
1575
+ if (!nonzero) return 0;
1576
+ return match[1] === "-" ? -1 : 1;
1577
+ }
1485
1578
  function toTerminalFailure(code, message, recoverable) {
1486
1579
  return {
1487
1580
  success: false,
@@ -1497,7 +1590,7 @@ function isValidCustomerName(name) {
1497
1590
  return trimmed.length >= 2 && /\p{L}/u.test(trimmed);
1498
1591
  }
1499
1592
  function isOfflinePaymentMethod(method) {
1500
- return method === "cash" || method === "offline";
1593
+ return method === LEGACY_CHECKOUT_PAYMENT_METHOD_ALIAS.CASH || method === PURCHASE_INTENT_PAYMENT_METHOD.OFFLINE;
1501
1594
  }
1502
1595
  function notifyCheckoutStatus(observer, status, context = {}) {
1503
1596
  try {
@@ -1505,7 +1598,35 @@ function notifyCheckoutStatus(observer, status, context = {}) {
1505
1598
  } catch {
1506
1599
  }
1507
1600
  }
1601
+ function notifyAuthorizationUi(observer, open) {
1602
+ try {
1603
+ observer?.(open);
1604
+ } catch {
1605
+ }
1606
+ }
1508
1607
  var MAX_STOREFRONT_COLLECTION_OPTIONS = 32;
1608
+ var CHECKOUT_INTENT_SCOPE_PREFIX = "checkout:";
1609
+ var CHECKOUT_CONFIRMATION_SCOPE_PREFIX = "checkout-confirm:";
1610
+ var CHECKOUT_INTENT_RECOVERY_FINGERPRINT = "cart-intent-v1";
1611
+ var ABORT_ERROR_NAME = "AbortError";
1612
+ var AUTHORIZATION_POPUP_NAME = "cimplify-auth";
1613
+ var AUTHORIZATION_POPUP_FEATURES = "width=520,height=760,noopener,noreferrer";
1614
+ var ORDER_TOKEN_QUERY_PARAMETER = "token";
1615
+ var CHECKOUT_API_ROUTE = {
1616
+ COLLECTION_OPTIONS: "/api/v1/payments/collection-options",
1617
+ paymentStatus: (orderId) => `/api/v1/orders/${orderId}/payment-status`,
1618
+ updateCustomer: (orderId) => `/api/v1/orders/${orderId}/customer`,
1619
+ verifyPayment: (orderId) => `/api/v1/orders/${orderId}/verify-payment`
1620
+ };
1621
+ var CHECKOUT_STATUS_BY_PURCHASE_INTENT_RESOLVE_STATUS = {
1622
+ [PURCHASE_INTENT_RESOLVE_STATUS.CONFIRMING]: CHECKOUT_STATUS.PROCESSING,
1623
+ [PURCHASE_INTENT_RESOLVE_STATUS.POLLING]: CHECKOUT_STATUS.POLLING,
1624
+ [PURCHASE_INTENT_RESOLVE_STATUS.AWAITING_ACTION]: CHECKOUT_STATUS.AWAITING_AUTHORIZATION,
1625
+ [PURCHASE_INTENT_RESOLVE_STATUS.REVIEWING_TERMS]: CHECKOUT_STATUS.REVIEWING_TERMS,
1626
+ [PURCHASE_INTENT_RESOLVE_STATUS.COMPLETED]: CHECKOUT_STATUS.FINALIZING,
1627
+ [PURCHASE_INTENT_RESOLVE_STATUS.FAILED]: CHECKOUT_STATUS.FAILED,
1628
+ [PURCHASE_INTENT_RESOLVE_STATUS.OUTCOME_UNKNOWN]: CHECKOUT_STATUS.RECOVERING
1629
+ };
1509
1630
  function normalizeCollectionOptions(value, expectedCurrency) {
1510
1631
  if (!value || typeof value !== "object") return null;
1511
1632
  const response = value;
@@ -1528,7 +1649,8 @@ function normalizeCollectionOptions(value, expectedCurrency) {
1528
1649
  if (option.method !== PAYMENT_METHOD.MOBILE_MONEY || typeof option.network !== "string" || !option.network.trim()) {
1529
1650
  continue;
1530
1651
  }
1531
- const network = option.network.trim().toLowerCase();
1652
+ const network = purchaseIntentMobileMoneyNetwork(option.network);
1653
+ if (!network) continue;
1532
1654
  const key = `${PAYMENT_METHOD.MOBILE_MONEY}:${network}`;
1533
1655
  if (seen.has(key)) continue;
1534
1656
  seen.add(key);
@@ -1539,18 +1661,22 @@ function normalizeCollectionOptions(value, expectedCurrency) {
1539
1661
  var CheckoutService = class {
1540
1662
  constructor(client) {
1541
1663
  this.client = client;
1664
+ /** Raw checkout material is component-lifetime only, never browser storage. */
1665
+ this.intentCommands = /* @__PURE__ */ new Map();
1666
+ /** Opaque owner/attempt authority for deprecated action submission. */
1667
+ this.actionAuthorities = /* @__PURE__ */ new Map();
1542
1668
  }
1543
1669
  validateCheckoutData(data) {
1544
1670
  if (isOfflinePaymentMethod(data.payment_method)) {
1545
1671
  return new CimplifyError(
1546
- "INVALID_PAYMENT_METHOD",
1672
+ ErrorCode.INVALID_PAYMENT_METHOD,
1547
1673
  "Offline payment isn't available for online checkout. Please pay by card or mobile money.",
1548
1674
  false
1549
1675
  );
1550
1676
  }
1551
1677
  if (!isValidCustomerName(data.customer?.name)) {
1552
1678
  return new CimplifyError(
1553
- "INVALID_CUSTOMER_NAME",
1679
+ ErrorCode.INVALID_CUSTOMER_NAME,
1554
1680
  "Enter your name, not a phone number.",
1555
1681
  false
1556
1682
  );
@@ -1559,7 +1685,7 @@ var CheckoutService = class {
1559
1685
  }
1560
1686
  orderTokenParam(orderId) {
1561
1687
  const token = this.client.getOrderToken(orderId);
1562
- return token ? `?token=${encodeURIComponent(token)}` : "";
1688
+ return token ? `?${ORDER_TOKEN_QUERY_PARAMETER}=${encodeURIComponent(token)}` : "";
1563
1689
  }
1564
1690
  /** Available storefront collection methods for one presentment currency. */
1565
1691
  async collectionOptions(currency) {
@@ -1575,7 +1701,7 @@ var CheckoutService = class {
1575
1701
  }
1576
1702
  const response = await safe(
1577
1703
  this.client.get(
1578
- `/api/v1/payments/collection-options?currency=${encodeURIComponent(normalized)}`
1704
+ `${CHECKOUT_API_ROUTE.COLLECTION_OPTIONS}?currency=${encodeURIComponent(normalized)}`
1579
1705
  )
1580
1706
  );
1581
1707
  if (!response.ok) return response;
@@ -1595,52 +1721,181 @@ var CheckoutService = class {
1595
1721
  authorizeCardPopup(action, options) {
1596
1722
  return this.client.authorizationAdapters.authorize(action, options);
1597
1723
  }
1598
- async process(data) {
1724
+ async startIntentCheckout(data, options) {
1599
1725
  const invalid = this.validateCheckoutData(data);
1600
- if (invalid) {
1601
- return err(invalid);
1602
- }
1603
- const intentScope = `checkout:${data.cart_id}`;
1604
- const intentFingerprint = JSON.stringify([
1605
- data.payment_method,
1606
- data.mobile_money_details,
1607
- data.pay_currency,
1608
- data.voucher_code,
1609
- data.pay_deposit
1726
+ if (invalid) return err(invalid);
1727
+ const intentScope = `${CHECKOUT_INTENT_SCOPE_PREFIX}${data.cart_id}`;
1728
+ const inMemoryCommand = JSON.stringify(data);
1729
+ const previousCommand = this.intentCommands.get(intentScope);
1730
+ if (previousCommand !== void 0 && previousCommand !== inMemoryCommand) {
1731
+ clearIntentIdempotencyKey(intentScope);
1732
+ }
1733
+ this.intentCommands.set(intentScope, inMemoryCommand);
1734
+ const clientIntentKey = data.idempotency_key || intentIdempotencyKey(intentScope, CHECKOUT_INTENT_RECOVERY_FINGERPRINT);
1735
+ const checkoutData = { ...data, idempotency_key: clientIntentKey };
1736
+ let mintRequest;
1737
+ try {
1738
+ mintRequest = checkoutFormToIntentRequest(checkoutData, clientIntentKey);
1739
+ } catch (error) {
1740
+ clearIntentIdempotencyKey(intentScope);
1741
+ this.intentCommands.delete(intentScope);
1742
+ return err(
1743
+ error instanceof CimplifyError ? error : new CimplifyError(
1744
+ ErrorCode.CHECKOUT_VALIDATION_FAILED,
1745
+ "Checkout details are invalid.",
1746
+ false
1747
+ )
1748
+ );
1749
+ }
1750
+ const minted = await this.client.purchaseIntents.mint(mintRequest, options);
1751
+ if (!minted.ok) {
1752
+ if (!isAmbiguousCheckoutError(minted.error)) {
1753
+ clearIntentIdempotencyKey(intentScope);
1754
+ this.intentCommands.delete(intentScope);
1755
+ }
1756
+ return minted;
1757
+ }
1758
+ const terms = minted.value.current_terms;
1759
+ if (!terms) {
1760
+ clearIntentIdempotencyKey(intentScope);
1761
+ this.intentCommands.delete(intentScope);
1762
+ return err(
1763
+ new CimplifyError(
1764
+ ErrorCode.CHECKOUT_TERMS_UNAVAILABLE,
1765
+ "Checkout terms are not available for this purchase intent.",
1766
+ true
1767
+ )
1768
+ );
1769
+ }
1770
+ let paymentOption;
1771
+ try {
1772
+ paymentOption = selectCheckoutPaymentOption(terms, checkoutData);
1773
+ } catch (error) {
1774
+ return err(
1775
+ error instanceof CimplifyError ? error : new CimplifyError(
1776
+ ErrorCode.INVALID_PAYMENT_METHOD,
1777
+ "Payment details are invalid.",
1778
+ false
1779
+ )
1780
+ );
1781
+ }
1782
+ if (!paymentOption && purchaseIntentRequiresPaymentOption(terms)) {
1783
+ return err(
1784
+ new CimplifyError(
1785
+ ErrorCode.PAYMENT_CAPABILITY_MISMATCH,
1786
+ "The selected payment method is not eligible for these checkout terms.",
1787
+ true
1788
+ )
1789
+ );
1790
+ }
1791
+ const confirmationScope = `${CHECKOUT_CONFIRMATION_SCOPE_PREFIX}${minted.value.id}`;
1792
+ const confirmationFingerprint = JSON.stringify([
1793
+ terms.intent_revision,
1794
+ terms.terms_version,
1795
+ terms.terms_digest,
1796
+ paymentOption?.id ?? null,
1797
+ terms.money.due_now,
1798
+ paymentOption?.customer_debit_amount ?? terms.money.rail_amount,
1799
+ terms.money.currency
1610
1800
  ]);
1611
- const checkoutData = {
1612
- ...data,
1613
- idempotency_key: data.idempotency_key || intentIdempotencyKey(intentScope, intentFingerprint)
1614
- };
1615
- const result = await safe(this.client.post("/api/v1/checkout", checkoutData));
1616
- if (result.ok) {
1617
- clearIntentIdempotencyKey(intentScope);
1618
- if (result.value.bill_token) {
1619
- this.client.setOrderToken(result.value.order_id, result.value.bill_token);
1801
+ const confirmationKey = intentIdempotencyKey(
1802
+ confirmationScope,
1803
+ confirmationFingerprint
1804
+ );
1805
+ const confirmation = checkoutFormToConfirmation(
1806
+ checkoutData,
1807
+ terms,
1808
+ paymentOption,
1809
+ confirmationKey
1810
+ );
1811
+ const confirmed = await this.client.purchaseIntents.confirm(
1812
+ minted.value.id,
1813
+ confirmation,
1814
+ options
1815
+ );
1816
+ if (!confirmed.ok) {
1817
+ if (!isAmbiguousCheckoutError(confirmed.error)) {
1818
+ clearIntentIdempotencyKey(confirmationScope);
1620
1819
  }
1621
- } else if (result.error.code !== ErrorCode.NETWORK_ERROR && result.error.code !== ErrorCode.TIMEOUT) {
1622
- clearIntentIdempotencyKey(intentScope);
1820
+ return confirmed;
1821
+ }
1822
+ return ok({
1823
+ intentId: minted.value.id,
1824
+ intentScope,
1825
+ confirmationScope,
1826
+ confirmation,
1827
+ initialResponse: confirmed.value,
1828
+ terms
1829
+ });
1830
+ }
1831
+ /** @deprecated Use `client.purchaseIntents.mint()` then `confirm()`. */
1832
+ async process(data, options) {
1833
+ const started = await this.startIntentCheckout(data, options);
1834
+ if (!started.ok) return started;
1835
+ this.reconcileActionAuthority(started.value.initialResponse);
1836
+ const result = confirmationToCheckoutResult(started.value.initialResponse);
1837
+ if (started.value.initialResponse.status === PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED || started.value.initialResponse.status === PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_FAILED) {
1838
+ clearIntentIdempotencyKey(started.value.intentScope);
1839
+ clearIntentIdempotencyKey(started.value.confirmationScope);
1840
+ this.intentCommands.delete(started.value.intentScope);
1623
1841
  }
1624
1842
  return result;
1625
1843
  }
1626
1844
  async initializePayment(_orderId, _method) {
1627
1845
  return err(
1628
1846
  new CimplifyError(
1629
- "NOT_IMPLEMENTED",
1847
+ ErrorCode.NOT_IMPLEMENTED,
1630
1848
  "initializePayment is not available in REST mode.",
1631
1849
  false
1632
1850
  )
1633
1851
  );
1634
1852
  }
1635
- async submitAuthorization(input) {
1636
- return safe(this.client.post("/api/v1/payments/authorization", input));
1853
+ /**
1854
+ * @deprecated Use `client.purchaseIntents.submitAction()`. This adapter works
1855
+ * only while this CheckoutService instance retains the opaque authority
1856
+ * returned by its own `process()` call; it never falls back to a payment-id
1857
+ * mutation.
1858
+ */
1859
+ async submitAuthorization(input, options) {
1860
+ const paymentId = input.payment_id.trim();
1861
+ const authority = this.actionAuthorities.get(paymentId);
1862
+ if (!authority) {
1863
+ return err(
1864
+ new CimplifyError(
1865
+ ErrorCode.CHECKOUT_AUTHORITY_REQUIRED,
1866
+ "The purchase-intent action authority is unavailable. Restart checkout before submitting authorization.",
1867
+ false
1868
+ )
1869
+ );
1870
+ }
1871
+ const submitted = await this.client.purchaseIntents.submitAction(
1872
+ authority.intentId,
1873
+ authority.attemptId,
1874
+ { authorization: input.authorization },
1875
+ {
1876
+ signal: options?.signal,
1877
+ timeoutMs: options?.timeoutMs
1878
+ }
1879
+ );
1880
+ if (!submitted.ok) return submitted;
1881
+ this.reconcileActionAuthority(submitted.value, paymentId);
1882
+ return confirmationToCheckoutResult(submitted.value);
1883
+ }
1884
+ reconcileActionAuthority(response, previousPaymentId) {
1885
+ if (previousPaymentId) this.actionAuthorities.delete(previousPaymentId);
1886
+ if ("payment_id" in response && typeof response.payment_id === "string" && response.payment_id.trim()) {
1887
+ this.actionAuthorities.delete(response.payment_id.trim());
1888
+ }
1889
+ const authority = checkoutActionAuthority(response);
1890
+ if (authority) this.actionAuthorities.set(authority.paymentId, authority);
1637
1891
  }
1638
- async pollPaymentStatus(orderId) {
1892
+ async pollPaymentStatus(orderId, options) {
1639
1893
  const encodedId = encodeURIComponent(orderId);
1640
1894
  const tokenParam = this.orderTokenParam(orderId);
1641
1895
  return safe(
1642
1896
  this.client.get(
1643
- `/api/v1/orders/${encodedId}/payment-status${tokenParam}`
1897
+ `${CHECKOUT_API_ROUTE.paymentStatus(encodedId)}${tokenParam}`,
1898
+ options
1644
1899
  )
1645
1900
  );
1646
1901
  }
@@ -1648,20 +1903,58 @@ var CheckoutService = class {
1648
1903
  const encodedId = encodeURIComponent(orderId);
1649
1904
  const tokenParam = this.orderTokenParam(orderId);
1650
1905
  return safe(
1651
- this.client.post(`/api/v1/orders/${encodedId}/customer${tokenParam}`, customer)
1906
+ this.client.post(`${CHECKOUT_API_ROUTE.updateCustomer(encodedId)}${tokenParam}`, customer)
1652
1907
  );
1653
1908
  }
1654
1909
  async verifyPayment(orderId) {
1655
1910
  const encodedId = encodeURIComponent(orderId);
1656
1911
  const tokenParam = this.orderTokenParam(orderId);
1657
- return safe(this.client.post(`/api/v1/orders/${encodedId}/verify-payment${tokenParam}`));
1912
+ return safe(
1913
+ this.client.post(`${CHECKOUT_API_ROUTE.verifyPayment(encodedId)}${tokenParam}`)
1914
+ );
1915
+ }
1916
+ async maybeEnrollResolvedOrder(orderId, data) {
1917
+ if (data.enroll_in_link === false) return false;
1918
+ const business = await safe(this.client.resolveBusinessId());
1919
+ if (!business.ok) return false;
1920
+ const address = data.address_info.street_address ? {
1921
+ label: "Default",
1922
+ street_address: data.address_info.street_address,
1923
+ apartment: data.address_info.apartment,
1924
+ city: data.address_info.city ?? "",
1925
+ region: data.address_info.region ?? "",
1926
+ postal_code: data.address_info.postal_code,
1927
+ country: data.address_info.country,
1928
+ delivery_instructions: data.address_info.delivery_instructions,
1929
+ phone_for_delivery: data.address_info.phone_for_delivery,
1930
+ latitude: data.address_info.latitude,
1931
+ longitude: data.address_info.longitude
1932
+ } : void 0;
1933
+ const mobileMoney = data.payment_method === PAYMENT_METHOD.MOBILE_MONEY && data.mobile_money_details ? {
1934
+ phone_number: data.mobile_money_details.phone_number,
1935
+ provider: data.mobile_money_details.provider,
1936
+ label: "Mobile Money"
1937
+ } : void 0;
1938
+ try {
1939
+ const enrollment = await this.client.link.enrollAndLinkOrder({
1940
+ order_id: orderId,
1941
+ business_id: business.value,
1942
+ order_type: data.order_type,
1943
+ address,
1944
+ mobile_money: mobileMoney
1945
+ });
1946
+ return enrollment.ok && enrollment.value.success;
1947
+ } catch {
1948
+ return false;
1949
+ }
1658
1950
  }
1951
+ /** @deprecated Use `client.purchaseIntents.resolve()`. */
1659
1952
  async processAndResolve(data) {
1660
- notifyCheckoutStatus(data.on_status_change, "preparing");
1953
+ notifyCheckoutStatus(data.on_status_change, CHECKOUT_STATUS.PREPARING);
1661
1954
  if (!data.cart_id) {
1662
1955
  return ok(
1663
1956
  toTerminalFailure(
1664
- "INVALID_CART",
1957
+ ErrorCode.INVALID_CART,
1665
1958
  "A valid cart is required before checkout can start.",
1666
1959
  false
1667
1960
  )
@@ -1669,13 +1962,19 @@ var CheckoutService = class {
1669
1962
  }
1670
1963
  const cartResult = await this.client.cart.get();
1671
1964
  if (!cartResult.ok || !cartResult.value?.id) {
1672
- return ok(toTerminalFailure("INVALID_CART", "Unable to load cart for checkout.", false));
1965
+ return ok(
1966
+ toTerminalFailure(
1967
+ ErrorCode.INVALID_CART,
1968
+ "Unable to load cart for checkout.",
1969
+ false
1970
+ )
1971
+ );
1673
1972
  }
1674
1973
  const cart = cartResult.value;
1675
1974
  if (cart.id !== data.cart_id || cart.items.length === 0) {
1676
1975
  return ok(
1677
1976
  toTerminalFailure(
1678
- "INVALID_CART",
1977
+ ErrorCode.INVALID_CART,
1679
1978
  "Cart is empty or no longer valid. Please refresh and try again.",
1680
1979
  false
1681
1980
  )
@@ -1705,7 +2004,7 @@ var CheckoutService = class {
1705
2004
  if (!isSupportedCurrency(rawBaseCurrency)) {
1706
2005
  return ok(
1707
2006
  toTerminalFailure(
1708
- "INVALID_CURRENCY",
2007
+ ErrorCode.UNSUPPORTED_CURRENCY,
1709
2008
  "Cart currency is missing or unsupported. Refresh the cart before checkout.",
1710
2009
  false
1711
2010
  )
@@ -1716,15 +2015,24 @@ var CheckoutService = class {
1716
2015
  if (payCurrency && !isSupportedCurrency(payCurrency)) {
1717
2016
  return ok(
1718
2017
  toTerminalFailure(
1719
- "INVALID_CURRENCY",
2018
+ ErrorCode.UNSUPPORTED_CURRENCY,
1720
2019
  "Payment currency is unsupported for checkout.",
1721
2020
  false
1722
2021
  )
1723
2022
  );
1724
2023
  }
1725
2024
  const payCurrencyCode = payCurrency && isSupportedCurrency(payCurrency) ? payCurrency : void 0;
1726
- const cartTotalAmount = typeof cart.pricing.total_price === "number" ? cart.pricing.total_price : Number.parseFloat(cart.pricing.total_price || "0");
1727
- if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
2025
+ const cartTotalSign = decimalSign(cart.pricing.total_price);
2026
+ if (cartTotalSign === null || cartTotalSign < 0) {
2027
+ return ok(
2028
+ toTerminalFailure(
2029
+ ErrorCode.CHECKOUT_VALIDATION_FAILED,
2030
+ "Cart total is not a valid non-negative decimal amount.",
2031
+ false
2032
+ )
2033
+ );
2034
+ }
2035
+ if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && cartTotalSign > 0) {
1728
2036
  const fxQuoteResult = await this.client.fx.lockQuote({
1729
2037
  from: baseCurrency,
1730
2038
  to: payCurrencyCode,
@@ -1733,7 +2041,7 @@ var CheckoutService = class {
1733
2041
  if (!fxQuoteResult.ok) {
1734
2042
  return ok(
1735
2043
  toTerminalFailure(
1736
- "FX_QUOTE_FAILED",
2044
+ ErrorCode.FX_QUOTE_FAILED,
1737
2045
  fxQuoteResult.error.message || "Unable to lock FX quote for checkout.",
1738
2046
  true
1739
2047
  )
@@ -1742,35 +2050,266 @@ var CheckoutService = class {
1742
2050
  checkoutData.pay_currency = payCurrencyCode;
1743
2051
  checkoutData.fx_quote_id = fxQuoteResult.value.id;
1744
2052
  }
1745
- notifyCheckoutStatus(data.on_status_change, "processing");
1746
- const processResult = await this.process(checkoutData);
1747
- if (!processResult.ok) {
2053
+ notifyCheckoutStatus(data.on_status_change, CHECKOUT_STATUS.PROCESSING);
2054
+ const started = await this.startIntentCheckout(checkoutData, { signal: data.signal });
2055
+ if (!started.ok) {
1748
2056
  return ok(
1749
2057
  toTerminalFailure(
1750
- processResult.error.code || "CHECKOUT_FAILED",
1751
- processResult.error.message || "Unable to process checkout.",
1752
- processResult.error.retryable ?? true
2058
+ started.error.code || ErrorCode.CHECKOUT_FAILED,
2059
+ started.error.message || "Unable to process checkout.",
2060
+ started.error.retryable ?? true
2061
+ )
2062
+ );
2063
+ }
2064
+ let actionFailure;
2065
+ try {
2066
+ const resolved = await this.client.purchaseIntents.resolve(started.value.intentId, {
2067
+ confirmation: started.value.confirmation,
2068
+ initialResponse: started.value.initialResponse,
2069
+ pollIntervalMs: data.poll_interval_ms,
2070
+ maxPollAttempts: data.max_poll_attempts,
2071
+ signal: data.signal,
2072
+ onStatusChange: (status, response) => {
2073
+ const context = {};
2074
+ if (response && "order_id" in response) context.order_id = response.order_id;
2075
+ if (response?.status === PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED) {
2076
+ context.replacement_terms = response.replacement;
2077
+ context.terms_deltas = response.deltas;
2078
+ }
2079
+ if (response?.status === PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE) {
2080
+ context.unavailable_lines = response.lines;
2081
+ }
2082
+ const checkoutStatus = CHECKOUT_STATUS_BY_PURCHASE_INTENT_RESOLVE_STATUS[status];
2083
+ notifyCheckoutStatus(data.on_status_change, checkoutStatus, context);
2084
+ },
2085
+ onActionRequired: async (action) => {
2086
+ switch (action.status) {
2087
+ case PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED: {
2088
+ const allowPopups = data.allow_popups ?? typeof window !== "undefined";
2089
+ if (!allowPopups) {
2090
+ actionFailure = {
2091
+ code: ErrorCode.PROVIDER_UNAVAILABLE,
2092
+ message: "Card authorization UI is unavailable in this environment.",
2093
+ recoverable: false
2094
+ };
2095
+ return PURCHASE_INTENT_ACTION_DECISION.STOP;
2096
+ }
2097
+ let outcome;
2098
+ try {
2099
+ notifyAuthorizationUi(data.on_authorization_ui_change, true);
2100
+ outcome = await this.authorizeCardPopup(
2101
+ {
2102
+ type: CHECKOUT_NEXT_ACTION.CARD_POPUP,
2103
+ provider: action.provider,
2104
+ client_secret: action.client_secret,
2105
+ public_key: action.public_key,
2106
+ provider_account_id: action.provider_account_id ?? void 0
2107
+ },
2108
+ {
2109
+ email: checkoutData.customer.email,
2110
+ currency: started.value.terms.money.currency,
2111
+ returnUrl: data.return_url,
2112
+ signal: data.signal
2113
+ }
2114
+ );
2115
+ } finally {
2116
+ notifyAuthorizationUi(data.on_authorization_ui_change, false);
2117
+ }
2118
+ if (outcome.status === PAYMENT_AUTHORIZATION_OUTCOME_STATUS.SUBMITTED) {
2119
+ return PURCHASE_INTENT_ACTION_DECISION.CONTINUE;
2120
+ }
2121
+ actionFailure = outcome.status === PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED ? {
2122
+ code: ErrorCode.PAYMENT_CANCELLED,
2123
+ message: "Card payment was cancelled before submission.",
2124
+ recoverable: true
2125
+ } : {
2126
+ code: outcome.code,
2127
+ message: outcome.message,
2128
+ recoverable: outcome.recoverable
2129
+ };
2130
+ return PURCHASE_INTENT_ACTION_DECISION.STOP;
2131
+ }
2132
+ case PURCHASE_INTENT_CONFIRM_STATUS.REDIRECT_REQUIRED: {
2133
+ const authorizationUrl = paymentAuthorizationUrl(action.authorization_url);
2134
+ if (!authorizationUrl) {
2135
+ actionFailure = {
2136
+ code: ErrorCode.AUTHORIZATION_FAILED,
2137
+ message: "The provider returned an invalid authorization URL.",
2138
+ recoverable: false
2139
+ };
2140
+ return PURCHASE_INTENT_ACTION_DECISION.STOP;
2141
+ }
2142
+ if (typeof window !== "undefined") {
2143
+ if (data.allow_popups === false) {
2144
+ window.location.assign(authorizationUrl);
2145
+ } else {
2146
+ const popup = window.open(
2147
+ authorizationUrl,
2148
+ AUTHORIZATION_POPUP_NAME,
2149
+ AUTHORIZATION_POPUP_FEATURES
2150
+ );
2151
+ if (!popup) {
2152
+ actionFailure = {
2153
+ code: ErrorCode.POPUP_BLOCKED,
2154
+ message: "Authorization popup was blocked.",
2155
+ recoverable: true
2156
+ };
2157
+ return PURCHASE_INTENT_ACTION_DECISION.STOP;
2158
+ }
2159
+ }
2160
+ }
2161
+ actionFailure = {
2162
+ code: ErrorCode.REDIRECT_REQUIRED,
2163
+ message: "Complete authorization at the payment provider, then resume checkout.",
2164
+ recoverable: true
2165
+ };
2166
+ return PURCHASE_INTENT_ACTION_DECISION.STOP;
2167
+ }
2168
+ case PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED: {
2169
+ const expected = authorizationKind(action.authorization_type);
2170
+ if (!expected || !data.on_authorization_required) {
2171
+ actionFailure = {
2172
+ code: ErrorCode.AUTHORIZATION_REQUIRED,
2173
+ message: "Additional payment authorization is required.",
2174
+ recoverable: true
2175
+ };
2176
+ return PURCHASE_INTENT_ACTION_DECISION.STOP;
2177
+ }
2178
+ let submission;
2179
+ await data.on_authorization_required(expected, async (candidate) => {
2180
+ if (submission) throw new Error("Authorization was already submitted.");
2181
+ submission = normalizeAuthorizationSubmission(expected, candidate);
2182
+ });
2183
+ if (!submission) {
2184
+ actionFailure = {
2185
+ code: ErrorCode.AUTHORIZATION_REQUIRED,
2186
+ message: "Payment authorization was not submitted.",
2187
+ recoverable: true
2188
+ };
2189
+ return PURCHASE_INTENT_ACTION_DECISION.STOP;
2190
+ }
2191
+ return {
2192
+ kind: PURCHASE_INTENT_ACTION_DECISION.SUBMIT,
2193
+ request: { authorization: submission }
2194
+ };
2195
+ }
2196
+ }
2197
+ }
2198
+ });
2199
+ if (!resolved.ok) {
2200
+ return ok(
2201
+ toTerminalFailure(
2202
+ resolved.error.code || ErrorCode.CHECKOUT_FAILED,
2203
+ resolved.error.message || "Unable to resolve checkout.",
2204
+ resolved.error.retryable ?? true
2205
+ )
2206
+ );
2207
+ }
2208
+ switch (resolved.value.kind) {
2209
+ case PURCHASE_INTENT_RESOLUTION_KIND.COMPLETED: {
2210
+ clearIntentIdempotencyKey(started.value.intentScope);
2211
+ clearIntentIdempotencyKey(started.value.confirmationScope);
2212
+ const response = resolved.value.response;
2213
+ notifyCheckoutStatus(data.on_status_change, CHECKOUT_STATUS.FINALIZING, {
2214
+ order_id: response.order_id
2215
+ });
2216
+ const enrolledInLink = await this.maybeEnrollResolvedOrder(response.order_id, data);
2217
+ notifyCheckoutStatus(data.on_status_change, CHECKOUT_STATUS.SUCCESS, {
2218
+ order_id: response.order_id
2219
+ });
2220
+ return ok({
2221
+ success: true,
2222
+ order: {
2223
+ id: response.order_id,
2224
+ order_number: response.order_number ?? response.order_id,
2225
+ status: CHECKOUT_ORDER_RESULT_STATUS.CONFIRMED,
2226
+ total: String(cart.pricing.total_price),
2227
+ currency: started.value.terms.money.currency
2228
+ },
2229
+ enrolled_in_link: enrolledInLink
2230
+ });
2231
+ }
2232
+ case PURCHASE_INTENT_RESOLUTION_KIND.FAILED:
2233
+ clearIntentIdempotencyKey(started.value.confirmationScope);
2234
+ return ok(
2235
+ toTerminalFailure(
2236
+ PURCHASE_INTENT_ERROR_CODE.PAYMENT_FAILED,
2237
+ "Payment failed. Choose another method or retry collection.",
2238
+ true
2239
+ )
2240
+ );
2241
+ case PURCHASE_INTENT_RESOLUTION_KIND.OUTCOME_UNKNOWN:
2242
+ return ok(
2243
+ toTerminalFailure(
2244
+ ErrorCode.PAYMENT_OUTCOME_UNKNOWN,
2245
+ "Payment outcome is still unknown. Resume this intent before retrying.",
2246
+ true
2247
+ )
2248
+ );
2249
+ case PURCHASE_INTENT_RESOLUTION_KIND.TIMED_OUT:
2250
+ return ok(
2251
+ toTerminalFailure(
2252
+ ErrorCode.TIMEOUT,
2253
+ "Payment is still processing. Resume this intent to continue polling.",
2254
+ true
2255
+ )
2256
+ );
2257
+ case PURCHASE_INTENT_RESOLUTION_KIND.ACTION_REQUIRED:
2258
+ return ok(
2259
+ toTerminalFailure(
2260
+ actionFailure?.code ?? ErrorCode.AUTHORIZATION_REQUIRED,
2261
+ actionFailure?.message ?? "Checkout requires additional authorization.",
2262
+ actionFailure?.recoverable ?? true
2263
+ )
2264
+ );
2265
+ case PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED: {
2266
+ const response = resolved.value.response;
2267
+ switch (response.status) {
2268
+ case PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED:
2269
+ return ok(
2270
+ toTerminalFailure(
2271
+ PURCHASE_INTENT_ERROR_CODE.TERMS_CHANGED,
2272
+ "Checkout terms changed and require explicit review.",
2273
+ true
2274
+ )
2275
+ );
2276
+ case PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE:
2277
+ return ok(
2278
+ toTerminalFailure(
2279
+ PURCHASE_INTENT_ERROR_CODE.SELECTION_UNAVAILABLE,
2280
+ "One or more checkout selections are unavailable.",
2281
+ true
2282
+ )
2283
+ );
2284
+ case PURCHASE_INTENT_CONFIRM_STATUS.OFFER_ENDED:
2285
+ return ok(
2286
+ toTerminalFailure(
2287
+ PURCHASE_INTENT_ERROR_CODE.OFFER_ENDED,
2288
+ "This offer has ended.",
2289
+ false
2290
+ )
2291
+ );
2292
+ case PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE:
2293
+ return ok(
2294
+ toTerminalFailure(
2295
+ PURCHASE_INTENT_ERROR_CODE.SOURCE_UNAVAILABLE,
2296
+ "This purchase source is unavailable.",
2297
+ false
2298
+ )
2299
+ );
2300
+ }
2301
+ }
2302
+ }
2303
+ } catch (error) {
2304
+ const cancelled = data.signal?.aborted || error instanceof Error && error.name === ABORT_ERROR_NAME;
2305
+ return ok(
2306
+ toTerminalFailure(
2307
+ cancelled ? ErrorCode.CANCELLED : ErrorCode.AUTHORIZATION_FAILED,
2308
+ cancelled ? "Checkout was cancelled." : error instanceof Error ? error.message : "Checkout authorization failed.",
2309
+ true
1753
2310
  )
1754
2311
  );
1755
2312
  }
1756
- const resolver = new CheckoutResolver({
1757
- client: this.client,
1758
- checkoutData,
1759
- pollIntervalMs: data.poll_interval_ms,
1760
- maxPollAttempts: data.max_poll_attempts,
1761
- onStatusChange: data.on_status_change,
1762
- onAuthorizationUiChange: data.on_authorization_ui_change,
1763
- onAuthorizationRequired: data.on_authorization_required,
1764
- signal: data.signal,
1765
- returnUrl: data.return_url,
1766
- enrollInLink: data.enroll_in_link,
1767
- cartTotal: cart.pricing.total_price,
1768
- cartCurrency: checkoutData.pay_currency || baseCurrency,
1769
- orderType: checkoutData.order_type,
1770
- allowPopups: data.allow_popups
1771
- });
1772
- const resolved = await resolver.resolve(processResult.value);
1773
- return ok(resolved);
1774
2313
  }
1775
2314
  };
1776
2315
 
@@ -1833,43 +2372,86 @@ function encodePathSegment(value) {
1833
2372
  var LinkService = class {
1834
2373
  constructor(client) {
1835
2374
  this.client = client;
2375
+ this.activationGeneration = 0;
2376
+ }
2377
+ beginActivation() {
2378
+ return {
2379
+ generation: ++this.activationGeneration,
2380
+ sessionEpoch: this.client.getSessionEpoch(),
2381
+ publicKeyEpoch: this.client.getPublicKeyEpoch()
2382
+ };
2383
+ }
2384
+ isActivationFenceCurrent(fence) {
2385
+ return fence.generation === this.activationGeneration && fence.sessionEpoch === this.client.getSessionEpoch() && fence.publicKeyEpoch === this.client.getPublicKeyEpoch();
2386
+ }
2387
+ supersededActivation() {
2388
+ return {
2389
+ ok: false,
2390
+ error: new CimplifyError(
2391
+ "session_superseded",
2392
+ "A newer authentication session replaced this one"
2393
+ )
2394
+ };
2395
+ }
2396
+ async activateAuthenticatedSession(result, fence) {
2397
+ if (!result.ok || !result.value.session_token) return result;
2398
+ if (!this.isActivationFenceCurrent(fence)) return this.supersededActivation();
2399
+ const accessToken = result.value.session_token;
2400
+ this.client.setAccessToken(accessToken);
2401
+ const installedSessionEpoch = this.client.getSessionEpoch();
2402
+ if (!this.client.getPublicKey().trim()) {
2403
+ this.client.activity.refreshIdentity();
2404
+ return result;
2405
+ }
2406
+ const claim = await this.client.support.claimConversation();
2407
+ if (fence.generation !== this.activationGeneration || fence.publicKeyEpoch !== this.client.getPublicKeyEpoch() || this.client.getAccessToken() !== accessToken) {
2408
+ return this.supersededActivation();
2409
+ }
2410
+ if (!claim.ok && claim.error.code !== "already_claimed") {
2411
+ if (this.client.getSessionEpoch() !== installedSessionEpoch) {
2412
+ return this.supersededActivation();
2413
+ }
2414
+ if (this.client.hasClaimedSessionAuthority()) {
2415
+ this.client.clearSession();
2416
+ } else {
2417
+ this.client.setAccessToken(null);
2418
+ }
2419
+ return claim;
2420
+ }
2421
+ this.client.activity.refreshIdentity();
2422
+ return result;
1836
2423
  }
1837
2424
  async requestOtp(input, opts) {
1838
2425
  const idempotencyKey = opts?.idempotencyKey ?? generateIdempotencyKey();
1839
2426
  return safe(
1840
- this.client.linkPost("/v1/link/auth/request-otp", input, { idempotencyKey })
2427
+ this.client.linkPost("/v1/link/auth/request-otp", input, {
2428
+ idempotencyKey
2429
+ })
1841
2430
  );
1842
2431
  }
1843
2432
  async verifyOtp(input, opts) {
1844
2433
  const idempotencyKey = opts?.idempotencyKey ?? generateIdempotencyKey();
2434
+ const activationFence = opts?.activateSession === false ? null : this.beginActivation();
1845
2435
  const { business_id: _ignoredBusinessId, ...body } = input;
1846
2436
  const result = await safe(
1847
2437
  this.client.linkPost("/v1/link/auth/verify-otp", body, { idempotencyKey })
1848
2438
  );
1849
- if (result.ok && result.value.session_token) {
1850
- this.client.setAccessToken(result.value.session_token);
1851
- this.client.activity.refreshIdentity();
1852
- this.client.autoClaimChatIdentity();
1853
- }
1854
- return result;
2439
+ return activationFence ? this.activateAuthenticatedSession(result, activationFence) : result;
1855
2440
  }
1856
2441
  async refreshSession(sessionToken, opts) {
1857
2442
  const idempotencyKey = opts?.idempotencyKey ?? generateIdempotencyKey();
2443
+ const activationFence = opts?.activateSession === false ? null : this.beginActivation();
1858
2444
  const body = sessionToken ? { session_token: sessionToken } : {};
1859
2445
  const result = await safe(
1860
2446
  this.client.linkPost("/v1/link/auth/refresh", body, { idempotencyKey })
1861
2447
  );
1862
- if (result.ok && result.value.session_token) {
1863
- this.client.setAccessToken(result.value.session_token);
1864
- this.client.activity.refreshIdentity();
1865
- this.client.autoClaimChatIdentity();
1866
- }
1867
- return result;
2448
+ return activationFence ? this.activateAuthenticatedSession(result, activationFence) : result;
1868
2449
  }
1869
2450
  async logout() {
1870
- const result = await safe(this.client.linkPost("/v1/link/auth/logout"));
2451
+ this.activationGeneration += 1;
2452
+ const request = safe(this.client.linkPost("/v1/link/auth/logout"));
1871
2453
  this.client.clearSession();
1872
- return result;
2454
+ return request;
1873
2455
  }
1874
2456
  async checkStatus(contact) {
1875
2457
  return safe(this.client.linkPost("/v1/link/check-status", { contact }));
@@ -1915,11 +2497,9 @@ var LinkService = class {
1915
2497
  async enrollAndLinkOrder(data, opts) {
1916
2498
  const idempotencyKey = opts?.idempotencyKey ?? generateIdempotencyKey();
1917
2499
  return safe(
1918
- this.client.linkPost(
1919
- "/v1/link/enroll-and-link-order",
1920
- data,
1921
- { idempotencyKey }
1922
- )
2500
+ this.client.linkPost("/v1/link/enroll-and-link-order", data, {
2501
+ idempotencyKey
2502
+ })
1923
2503
  );
1924
2504
  }
1925
2505
  async updatePreferences(preferences) {
@@ -2342,6 +2922,7 @@ var MESSAGE_TYPES = {
2342
2922
  INIT: "init",
2343
2923
  SET_TOKEN: "set_token",
2344
2924
  SET_SESSION: "set_session",
2925
+ SESSION_CLEARED: "session_cleared",
2345
2926
  SET_CART: "set_cart",
2346
2927
  GET_DATA: "get_data",
2347
2928
  NAVIGATE: "navigate",
@@ -2358,9 +2939,13 @@ var MESSAGE_TYPES = {
2358
2939
  LOGOUT_COMPLETE: "logout_complete",
2359
2940
  AUTH_REQUESTED: "auth_requested",
2360
2941
  SESSION_ESTABLISHED: "session_established",
2942
+ SESSION_ESTABLISHMENT_FAILED: "session_establishment_failed",
2943
+ CONTINUE_WITHOUT_SAVED_DETAILS: "continue_without_saved_details",
2361
2944
  CONTACT_PROVIDED: "contact_provided",
2362
2945
  CHECKOUT_STATUS: "checkout_status",
2363
2946
  CHECKOUT_COMPLETE: "checkout_complete",
2947
+ TERMS_CHANGED: "terms_changed",
2948
+ SELECTION_UNAVAILABLE: "selection_unavailable",
2364
2949
  ORDER_TYPE_CHANGED: "order_type_changed",
2365
2950
  REQUEST_SUBMIT: "request_submit"
2366
2951
  };
@@ -2721,6 +3306,7 @@ function toCheckoutError(code, message, recoverable) {
2721
3306
  };
2722
3307
  }
2723
3308
  var DEFAULT_LINK_URL = "https://link.cimplify.io";
3309
+ var AUTH_ESTABLISHMENT_WAIT_MS = 8e3;
2724
3310
  var AUTH_CONTACT_TYPES = new Set(Object.values(ELEMENTS_AUTH_CONTACT_TYPES));
2725
3311
  var AUTH_MODES = new Set(Object.values(ELEMENTS_AUTH_MODES));
2726
3312
  var AUTH_PROMPTS = new Set(Object.values(ELEMENTS_AUTH_PROMPTS));
@@ -2771,6 +3357,7 @@ var KNOWN_IFRAME_TYPES = /* @__PURE__ */ new Set([
2771
3357
  MESSAGE_TYPES.HEIGHT_CHANGE,
2772
3358
  MESSAGE_TYPES.AUTH_REQUESTED,
2773
3359
  MESSAGE_TYPES.SESSION_ESTABLISHED,
3360
+ MESSAGE_TYPES.CONTINUE_WITHOUT_SAVED_DETAILS,
2774
3361
  MESSAGE_TYPES.ERROR,
2775
3362
  MESSAGE_TYPES.ADDRESS_CHANGED,
2776
3363
  MESSAGE_TYPES.ADDRESS_SELECTED,
@@ -2779,6 +3366,8 @@ var KNOWN_IFRAME_TYPES = /* @__PURE__ */ new Set([
2779
3366
  MESSAGE_TYPES.LOGOUT_COMPLETE,
2780
3367
  MESSAGE_TYPES.CHECKOUT_STATUS,
2781
3368
  MESSAGE_TYPES.CHECKOUT_COMPLETE,
3369
+ MESSAGE_TYPES.TERMS_CHANGED,
3370
+ MESSAGE_TYPES.SELECTION_UNAVAILABLE,
2782
3371
  MESSAGE_TYPES.ORDER_TYPE_CHANGED,
2783
3372
  MESSAGE_TYPES.REQUEST_SUBMIT
2784
3373
  ]);
@@ -2815,6 +3404,7 @@ function parseIframeMessage(data) {
2815
3404
  if (!record.method || typeof record.method !== "object") return null;
2816
3405
  break;
2817
3406
  case MESSAGE_TYPES.CONTACT_PROVIDED:
3407
+ case MESSAGE_TYPES.CONTINUE_WITHOUT_SAVED_DETAILS:
2818
3408
  if (typeof record.contact !== "string" || typeof record.contactType !== "string") return null;
2819
3409
  break;
2820
3410
  case MESSAGE_TYPES.ERROR:
@@ -2824,7 +3414,15 @@ function parseIframeMessage(data) {
2824
3414
  if (typeof record.success !== "boolean") return null;
2825
3415
  break;
2826
3416
  case MESSAGE_TYPES.CHECKOUT_STATUS:
2827
- if (typeof record.status !== "string" || !record.context || typeof record.context !== "object") return null;
3417
+ if (typeof record.status !== "string" || !record.context || typeof record.context !== "object")
3418
+ return null;
3419
+ break;
3420
+ case MESSAGE_TYPES.TERMS_CHANGED:
3421
+ if (typeof record.intent_id !== "string" || !record.replacement || typeof record.replacement !== "object" || !Array.isArray(record.deltas))
3422
+ return null;
3423
+ break;
3424
+ case MESSAGE_TYPES.SELECTION_UNAVAILABLE:
3425
+ if (typeof record.intent_id !== "string" || !Array.isArray(record.lines)) return null;
2828
3426
  break;
2829
3427
  case MESSAGE_TYPES.READY:
2830
3428
  case MESSAGE_TYPES.HEIGHT_CHANGE:
@@ -2847,6 +3445,8 @@ var CimplifyElements = class {
2847
3445
  this.paymentData = null;
2848
3446
  this.checkoutInProgress = false;
2849
3447
  this.authInProgress = false;
3448
+ this.authEstablishmentInProgress = false;
3449
+ this.authGeneration = 0;
2850
3450
  this.activeCheckoutAbort = null;
2851
3451
  this.businessIdResolvePromise = null;
2852
3452
  this.client = client;
@@ -2881,6 +3481,12 @@ var CimplifyElements = class {
2881
3481
  }
2882
3482
  }
2883
3483
  destroy() {
3484
+ this.authGeneration += 1;
3485
+ if (this.authEstablishmentInProgress) {
3486
+ this.client.setAccessToken(null);
3487
+ }
3488
+ this.authInProgress = false;
3489
+ this.authEstablishmentInProgress = false;
2884
3490
  this.elements.forEach((element) => element.destroy());
2885
3491
  this.elements.clear();
2886
3492
  if (typeof window !== "undefined") {
@@ -2890,7 +3496,10 @@ var CimplifyElements = class {
2890
3496
  async submitCheckout(data) {
2891
3497
  const result = await this.processCheckout({
2892
3498
  cart_id: data.cart_id,
2893
- order_type: data.order_type ?? "delivery",
3499
+ intent_id: data.intent_id,
3500
+ hosted_session_id: data.hosted_session_id,
3501
+ initial_terms: data.initial_terms,
3502
+ order_type: data.order_type ?? ORDER_TYPE.DELIVERY,
2894
3503
  location_id: data.location_id,
2895
3504
  notes: data.notes,
2896
3505
  scheduled_time: data.scheduled_time,
@@ -2902,7 +3511,7 @@ var CimplifyElements = class {
2902
3511
  success: true,
2903
3512
  order: {
2904
3513
  id: result.order?.id || "",
2905
- status: result.order?.status || "unknown",
3514
+ status: result.order?.status || CHECKOUT_ORDER_RESULT_STATUS.UNKNOWN,
2906
3515
  total: result.order?.total || ""
2907
3516
  }
2908
3517
  };
@@ -2910,7 +3519,7 @@ var CimplifyElements = class {
2910
3519
  return {
2911
3520
  success: false,
2912
3521
  error: {
2913
- code: result.error?.code || "CHECKOUT_FAILED",
3522
+ code: result.error?.code || ErrorCode.CHECKOUT_FAILED,
2914
3523
  message: result.error?.message || "Checkout failed"
2915
3524
  }
2916
3525
  };
@@ -2918,22 +3527,39 @@ var CimplifyElements = class {
2918
3527
  processCheckout(options) {
2919
3528
  let abortFn = null;
2920
3529
  const task = (async () => {
2921
- this.debug("[cimplify:checkout] processCheckout called", { cart_id: options.cart_id, order_type: options.order_type, location_id: options.location_id });
3530
+ this.debug("[cimplify:checkout] processCheckout called", {
3531
+ cart_id: options.cart_id,
3532
+ intent_id: options.intent_id,
3533
+ hosted_session_id: options.hosted_session_id,
3534
+ order_type: options.order_type,
3535
+ location_id: options.location_id
3536
+ });
2922
3537
  if (this.checkoutInProgress) {
2923
3538
  this.debug("[cimplify:checkout] BLOCKED: already processing");
2924
3539
  return toCheckoutError(
2925
- "ALREADY_PROCESSING",
3540
+ ErrorCode.ALREADY_PROCESSING,
2926
3541
  "Checkout is already in progress.",
2927
3542
  false
2928
3543
  );
2929
3544
  }
2930
3545
  this.checkoutInProgress = true;
2931
- if (!options.cart_id) {
2932
- this.debug("[cimplify:checkout] BLOCKED: no cart_id");
3546
+ if (this.authEstablishmentInProgress) {
3547
+ await this.waitForAuthEstablishment(AUTH_ESTABLISHMENT_WAIT_MS);
3548
+ if (this.authEstablishmentInProgress) {
3549
+ this.checkoutInProgress = false;
3550
+ return toCheckoutError(
3551
+ ErrorCode.AUTH_INCOMPLETE,
3552
+ "Authentication must finish establishing the checkout session.",
3553
+ true
3554
+ );
3555
+ }
3556
+ }
3557
+ if (!options.cart_id && !options.intent_id) {
3558
+ this.debug("[cimplify:checkout] BLOCKED: no cart_id or intent_id");
2933
3559
  this.checkoutInProgress = false;
2934
3560
  return toCheckoutError(
2935
- "INVALID_CART",
2936
- "A valid cart is required before checkout can start.",
3561
+ ErrorCode.CHECKOUT_AUTHORITY_REQUIRED,
3562
+ "A cart or existing purchase intent is required before checkout can start.",
2937
3563
  false
2938
3564
  );
2939
3565
  }
@@ -2941,7 +3567,7 @@ var CimplifyElements = class {
2941
3567
  this.debug("[cimplify:checkout] BLOCKED: no order_type");
2942
3568
  this.checkoutInProgress = false;
2943
3569
  return toCheckoutError(
2944
- "ORDER_TYPE_REQUIRED",
3570
+ ErrorCode.ORDER_TYPE_REQUIRED,
2945
3571
  "Order type is required before checkout can start.",
2946
3572
  false
2947
3573
  );
@@ -2951,7 +3577,7 @@ var CimplifyElements = class {
2951
3577
  this.debug("[cimplify:checkout] BLOCKED: no checkout element");
2952
3578
  this.checkoutInProgress = false;
2953
3579
  return toCheckoutError(
2954
- "NO_PAYMENT_ELEMENT",
3580
+ ErrorCode.NO_PAYMENT_ELEMENT,
2955
3581
  "Checkout element must be mounted before checkout.",
2956
3582
  false
2957
3583
  );
@@ -2960,7 +3586,7 @@ var CimplifyElements = class {
2960
3586
  this.debug("[cimplify:checkout] BLOCKED: checkout element not mounted");
2961
3587
  this.checkoutInProgress = false;
2962
3588
  return toCheckoutError(
2963
- "PAYMENT_NOT_MOUNTED",
3589
+ ErrorCode.PAYMENT_NOT_MOUNTED,
2964
3590
  "Checkout element must be mounted before checkout.",
2965
3591
  false
2966
3592
  );
@@ -2972,7 +3598,7 @@ var CimplifyElements = class {
2972
3598
  this.debug("[cimplify:checkout] BLOCKED: auth incomplete");
2973
3599
  this.checkoutInProgress = false;
2974
3600
  return toCheckoutError(
2975
- "AUTH_INCOMPLETE",
3601
+ ErrorCode.AUTH_INCOMPLETE,
2976
3602
  "Authentication must complete before checkout can start.",
2977
3603
  true
2978
3604
  );
@@ -2985,10 +3611,19 @@ var CimplifyElements = class {
2985
3611
  }
2986
3612
  await this.hydrateCustomerData();
2987
3613
  }
2988
- this.debug("[cimplify:checkout] customer data hydrated", { customerData: this.customerData, accountId: this.accountId, isUnifiedCheckout });
3614
+ this.debug("[cimplify:checkout] customer data hydrated", {
3615
+ customerData: this.customerData,
3616
+ accountId: this.accountId,
3617
+ isUnifiedCheckout
3618
+ });
2989
3619
  const processMessage = {
2990
3620
  type: MESSAGE_TYPES.PROCESS_CHECKOUT,
2991
- cart_id: options.cart_id,
3621
+ // Existing intent authority wins if a compatibility caller supplied
3622
+ // both values; the iframe must never mint a replacement.
3623
+ cart_id: options.intent_id ? void 0 : options.cart_id,
3624
+ intent_id: options.intent_id,
3625
+ hosted_session_id: options.hosted_session_id,
3626
+ initial_terms: options.intent_id ? options.initial_terms : void 0,
2992
3627
  order_type: options.order_type,
2993
3628
  location_id: options.location_id,
2994
3629
  notes: options.notes,
@@ -3027,7 +3662,7 @@ var CimplifyElements = class {
3027
3662
  const timeout = setTimeout(() => {
3028
3663
  settle(
3029
3664
  toCheckoutError(
3030
- "TIMEOUT",
3665
+ ErrorCode.TIMEOUT,
3031
3666
  "Checkout timed out before receiving a terminal response.",
3032
3667
  true
3033
3668
  )
@@ -3048,8 +3683,26 @@ var CimplifyElements = class {
3048
3683
  options.on_status_change?.(message.status, message.context);
3049
3684
  return;
3050
3685
  }
3686
+ if (message.type === MESSAGE_TYPES.TERMS_CHANGED) {
3687
+ options.on_status_change?.(CHECKOUT_STATUS.REVIEWING_TERMS, {
3688
+ intent_id: message.intent_id,
3689
+ replacement_terms: message.replacement,
3690
+ terms_deltas: message.deltas
3691
+ });
3692
+ return;
3693
+ }
3694
+ if (message.type === MESSAGE_TYPES.SELECTION_UNAVAILABLE) {
3695
+ options.on_status_change?.(CHECKOUT_STATUS.REVIEWING_TERMS, {
3696
+ intent_id: message.intent_id,
3697
+ unavailable_lines: message.lines
3698
+ });
3699
+ return;
3700
+ }
3051
3701
  if (message.type === MESSAGE_TYPES.CHECKOUT_COMPLETE) {
3052
- this.debug("[cimplify:checkout] received checkout_complete", { success: message.success, error: message.error });
3702
+ this.debug("[cimplify:checkout] received checkout_complete", {
3703
+ success: message.success,
3704
+ error: message.error
3705
+ });
3053
3706
  settle({
3054
3707
  success: message.success,
3055
3708
  order: message.order,
@@ -3061,7 +3714,7 @@ var CimplifyElements = class {
3061
3714
  checkoutElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
3062
3715
  settle(
3063
3716
  toCheckoutError(
3064
- "AUTH_LOST",
3717
+ ErrorCode.AUTH_LOST,
3065
3718
  "Authentication was cleared during checkout.",
3066
3719
  true
3067
3720
  )
@@ -3073,16 +3726,18 @@ var CimplifyElements = class {
3073
3726
  }
3074
3727
  abortFn = () => {
3075
3728
  checkoutElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
3076
- settle(
3077
- toCheckoutError(
3078
- "CANCELLED",
3079
- "Checkout was cancelled.",
3080
- true
3081
- )
3082
- );
3729
+ settle(toCheckoutError(ErrorCode.CANCELLED, "Checkout was cancelled.", true));
3083
3730
  };
3084
3731
  this.activeCheckoutAbort = abortFn;
3085
- 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 });
3732
+ this.debug("[cimplify:checkout] sending process_checkout to checkout iframe", {
3733
+ cart_id: processMessage.cart_id,
3734
+ intent_id: processMessage.intent_id,
3735
+ hosted_session_id: processMessage.hosted_session_id,
3736
+ order_type: processMessage.order_type,
3737
+ hasCustomer: !!processMessage.customer,
3738
+ hasAddress: !!processMessage.address,
3739
+ isUnifiedCheckout
3740
+ });
3086
3741
  checkoutElement.sendMessage(processMessage);
3087
3742
  this.debug("[cimplify:checkout] postMessage sent, waiting for checkout_complete...");
3088
3743
  });
@@ -3128,11 +3783,16 @@ var CimplifyElements = class {
3128
3783
  getAppearance() {
3129
3784
  return this.options.appearance;
3130
3785
  }
3131
- async hydrateCustomerData() {
3132
- if (!this.accessToken || this.customerData) return;
3786
+ isAuthGenerationCurrent(generation, token) {
3787
+ return this.authGeneration === generation && this.client.getAccessToken() === token;
3788
+ }
3789
+ async hydrateCustomerData(generation = this.authGeneration, token = this.accessToken, force = false) {
3790
+ if (!token || !this.isAuthGenerationCurrent(generation, token)) return false;
3791
+ if (this.customerData && !force) return true;
3133
3792
  const linkDataResult = await this.client.link.getLinkData();
3793
+ if (!this.isAuthGenerationCurrent(generation, token)) return false;
3134
3794
  if (!linkDataResult.ok || !linkDataResult.value?.customer) {
3135
- return;
3795
+ return true;
3136
3796
  }
3137
3797
  const customer = linkDataResult.value.customer;
3138
3798
  this.customerData = {
@@ -3141,6 +3801,7 @@ var CimplifyElements = class {
3141
3801
  phone: customer.phone || null
3142
3802
  };
3143
3803
  this.customerId = customer.id;
3804
+ return true;
3144
3805
  }
3145
3806
  sourceElementFor(event, message) {
3146
3807
  for (const element of this.elements.values()) {
@@ -3179,48 +3840,123 @@ var CimplifyElements = class {
3179
3840
  } catch {
3180
3841
  }
3181
3842
  }
3843
+ clearClaimedAuthorityAfterRestoreFailure() {
3844
+ if (!this.client.getAccessToken() && !this.client.hasClaimedSessionAuthority()) return;
3845
+ this.accessToken = null;
3846
+ this.accountId = null;
3847
+ this.customerId = null;
3848
+ this.customerData = null;
3849
+ this.addressData = null;
3850
+ this.paymentData = null;
3851
+ this.authEstablishmentInProgress = false;
3852
+ this.client.clearSession();
3853
+ this.broadcastClearedSession();
3854
+ }
3182
3855
  async restoreSession() {
3183
3856
  if (typeof window === "undefined" || this.accessToken) return;
3857
+ const generation = ++this.authGeneration;
3184
3858
  const storageKey = this.sessionStorageKey();
3185
3859
  if (!storageKey) return;
3186
3860
  let refreshToken = null;
3187
3861
  try {
3188
3862
  refreshToken = window.localStorage.getItem(storageKey);
3189
3863
  } catch {
3864
+ this.clearClaimedAuthorityAfterRestoreFailure();
3190
3865
  return;
3191
3866
  }
3192
- if (!refreshToken) return;
3193
- const result = await this.client.link.refreshSession(refreshToken);
3867
+ if (!refreshToken) {
3868
+ this.clearClaimedAuthorityAfterRestoreFailure();
3869
+ return;
3870
+ }
3871
+ const result = await this.client.link.refreshSession(refreshToken, {
3872
+ activateSession: false
3873
+ });
3874
+ if (this.authGeneration !== generation) return;
3194
3875
  if (!result.ok || !result.value.session_token) {
3195
3876
  this.clearPersistedSession();
3877
+ this.clearClaimedAuthorityAfterRestoreFailure();
3196
3878
  return;
3197
3879
  }
3198
3880
  this.persistRefreshToken(result.value.refresh_token ?? void 0);
3199
- const data = await this.applyAccessToken(result.value.session_token);
3200
- this.broadcastAccessToken(result.value.session_token);
3201
- this.elements.forEach((element) => element._emitAuthenticated(data));
3881
+ const application = await this.applyAccessToken(result.value.session_token, generation);
3882
+ if (application.kind === "claim_failed") {
3883
+ this.clearPersistedSession();
3884
+ this.broadcastClearedSession();
3885
+ return;
3886
+ }
3887
+ if (application.kind !== "authenticated" || !this.broadcastAccessToken(result.value.session_token, generation)) {
3888
+ return;
3889
+ }
3890
+ this.elements.forEach((element) => element._emitAuthenticated(application.data));
3202
3891
  }
3203
- async applyAccessToken(token) {
3892
+ async applyAccessToken(token, generation) {
3893
+ if (this.authGeneration !== generation) return { kind: "incomplete" };
3894
+ this.authEstablishmentInProgress = true;
3895
+ this.accessToken = null;
3896
+ this.accountId = null;
3897
+ this.customerId = null;
3898
+ this.customerData = null;
3899
+ this.addressData = null;
3900
+ this.paymentData = null;
3901
+ this.client.setAccessToken(token);
3902
+ const claim = await this.client.support.claimConversation();
3903
+ if (!this.isAuthGenerationCurrent(generation, token)) return { kind: "incomplete" };
3904
+ if (!claim.ok && claim.error.code !== "already_claimed") {
3905
+ if (this.client.hasClaimedSessionAuthority()) {
3906
+ this.client.clearSession();
3907
+ } else {
3908
+ this.client.setAccessToken(null);
3909
+ }
3910
+ this.authEstablishmentInProgress = false;
3911
+ return {
3912
+ kind: "claim_failed",
3913
+ code: claim.error.code,
3914
+ message: claim.error.userMessage
3915
+ };
3916
+ }
3204
3917
  this.accessToken = token;
3205
3918
  this.accountId = decodeJwtSub(token);
3206
- this.client.setAccessToken(token);
3207
- this.client.autoClaimChatIdentity();
3208
- await this.hydrateCustomerData();
3919
+ if (!await this.hydrateCustomerData(generation, token, true)) {
3920
+ return { kind: "incomplete" };
3921
+ }
3922
+ if (!this.isAuthGenerationCurrent(generation, token)) return { kind: "incomplete" };
3923
+ this.authEstablishmentInProgress = false;
3209
3924
  return {
3210
- accountId: this.accountId ?? "",
3211
- customerId: this.customerId ?? "",
3212
- token,
3213
- customer: this.customerData ?? { name: "", email: null, phone: null }
3925
+ kind: "authenticated",
3926
+ data: {
3927
+ accountId: this.accountId ?? "",
3928
+ customerId: this.customerId ?? "",
3929
+ token,
3930
+ customer: this.customerData ?? { name: "", email: null, phone: null }
3931
+ }
3214
3932
  };
3215
3933
  }
3216
- broadcastAccessToken(token) {
3934
+ async waitForAuthEstablishment(timeoutMs) {
3935
+ const deadline = Date.now() + timeoutMs;
3936
+ while (this.authEstablishmentInProgress && Date.now() < deadline) {
3937
+ await new Promise((resolve) => setTimeout(resolve, 100));
3938
+ }
3939
+ }
3940
+ broadcastAccessToken(token, generation) {
3941
+ if (!this.isAuthGenerationCurrent(generation, token) || this.accessToken !== token || this.authEstablishmentInProgress) {
3942
+ return false;
3943
+ }
3217
3944
  const sessionToken = this.client.getSessionToken();
3218
3945
  this.elements.forEach((element) => {
3219
- element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
3220
3946
  if (sessionToken) {
3221
3947
  element.sendMessage({ type: MESSAGE_TYPES.SET_SESSION, sessionToken });
3222
3948
  }
3949
+ element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
3950
+ });
3951
+ return true;
3952
+ }
3953
+ broadcastClearedSession() {
3954
+ const sessionToken = this.client.getSessionToken();
3955
+ if (!sessionToken) return null;
3956
+ this.elements.forEach((element) => {
3957
+ element.sendMessage({ type: MESSAGE_TYPES.SESSION_CLEARED, sessionToken });
3223
3958
  });
3959
+ return sessionToken;
3224
3960
  }
3225
3961
  startEmbeddedAuth(sourceElement, request) {
3226
3962
  if (this.authInProgress) return;
@@ -3229,11 +3965,12 @@ var CimplifyElements = class {
3229
3965
  const redirectUri = auth.redirectUri ?? readPublicEnv("NEXT_PUBLIC_CIMPLIFY_REDIRECT_URI");
3230
3966
  if (!clientId || !redirectUri) {
3231
3967
  sourceElement._emitError({
3232
- code: "AUTH_CONFIG_REQUIRED",
3968
+ code: ErrorCode.AUTH_CONFIG_REQUIRED,
3233
3969
  message: "Cimplify OAuth clientId and redirectUri are required."
3234
3970
  });
3235
3971
  return;
3236
3972
  }
3973
+ const generation = ++this.authGeneration;
3237
3974
  this.authInProgress = true;
3238
3975
  void startSignIn({
3239
3976
  clientId,
@@ -3248,19 +3985,27 @@ var CimplifyElements = class {
3248
3985
  mode: "modal",
3249
3986
  silentFirst: request.mode === ELEMENTS_AUTH_MODES.CHECKOUT && request.prompt !== ELEMENTS_AUTH_PROMPTS.LOGIN,
3250
3987
  onSuccess: async ({ accessToken }) => {
3988
+ if (this.authGeneration !== generation) return;
3251
3989
  this.authInProgress = false;
3252
3990
  if (!accessToken) {
3253
3991
  sourceElement._emitError({
3254
- code: "AUTH_CALLBACK_MISSING_TOKEN",
3992
+ code: ErrorCode.AUTH_CALLBACK_MISSING_TOKEN,
3255
3993
  message: "Sign-in callback did not return an access token."
3256
3994
  });
3257
3995
  return;
3258
3996
  }
3259
- const data = await this.applyAccessToken(accessToken);
3260
- this.broadcastAccessToken(accessToken);
3261
- sourceElement._emitAuthenticated(data);
3997
+ const application = await this.applyAccessToken(accessToken, generation);
3998
+ if (application.kind === "authenticated" && this.broadcastAccessToken(accessToken, generation)) {
3999
+ sourceElement._emitAuthenticated(application.data);
4000
+ } else if (application.kind === "claim_failed") {
4001
+ sourceElement._emitError({
4002
+ code: application.code,
4003
+ message: application.message
4004
+ });
4005
+ }
3262
4006
  },
3263
4007
  onError: (err2) => {
4008
+ if (this.authGeneration !== generation) return;
3264
4009
  this.authInProgress = false;
3265
4010
  sourceElement._emitError({
3266
4011
  code: err2.error,
@@ -3268,9 +4013,10 @@ var CimplifyElements = class {
3268
4013
  });
3269
4014
  }
3270
4015
  }).catch((error) => {
4016
+ if (this.authGeneration !== generation) return;
3271
4017
  this.authInProgress = false;
3272
4018
  sourceElement._emitError({
3273
- code: "AUTH_START_FAILED",
4019
+ code: ErrorCode.AUTH_START_FAILED,
3274
4020
  message: error instanceof Error ? error.message : "Unable to start sign-in."
3275
4021
  });
3276
4022
  });
@@ -3288,11 +4034,29 @@ var CimplifyElements = class {
3288
4034
  this.startEmbeddedAuth(sourceElement, message);
3289
4035
  break;
3290
4036
  case MESSAGE_TYPES.SESSION_ESTABLISHED:
3291
- this.persistRefreshToken(message.refreshToken);
3292
- void this.applyAccessToken(message.accessToken).then((data) => {
3293
- this.broadcastAccessToken(message.accessToken);
3294
- sourceElement._emitAuthenticated(data);
3295
- });
4037
+ {
4038
+ this.authInProgress = false;
4039
+ const generation = ++this.authGeneration;
4040
+ void this.applyAccessToken(message.accessToken, generation).then((application) => {
4041
+ if (this.authGeneration !== generation) return;
4042
+ if (application.kind === "claim_failed") {
4043
+ this.clearPersistedSession();
4044
+ sourceElement.sendMessage({
4045
+ type: MESSAGE_TYPES.SESSION_ESTABLISHMENT_FAILED,
4046
+ accessToken: message.accessToken,
4047
+ code: application.code,
4048
+ message: application.message
4049
+ });
4050
+ this.broadcastClearedSession();
4051
+ return;
4052
+ }
4053
+ if (application.kind !== "authenticated" || !this.broadcastAccessToken(message.accessToken, generation)) {
4054
+ return;
4055
+ }
4056
+ this.persistRefreshToken(message.refreshToken);
4057
+ sourceElement._emitAuthenticated(application.data);
4058
+ });
4059
+ }
3296
4060
  break;
3297
4061
  case MESSAGE_TYPES.ADDRESS_CHANGED:
3298
4062
  case MESSAGE_TYPES.ADDRESS_SELECTED:
@@ -3305,13 +4069,41 @@ var CimplifyElements = class {
3305
4069
  if (!this.customerData) {
3306
4070
  this.customerData = { name: "", email: null, phone: null };
3307
4071
  }
3308
- if (message.contactType === "email") {
4072
+ if (message.contactType === ELEMENTS_AUTH_CONTACT_TYPES.EMAIL) {
4073
+ this.customerData.email = message.contact;
4074
+ } else {
4075
+ this.customerData.phone = message.contact;
4076
+ }
4077
+ break;
4078
+ case MESSAGE_TYPES.CONTINUE_WITHOUT_SAVED_DETAILS: {
4079
+ this.authGeneration += 1;
4080
+ this.authInProgress = false;
4081
+ this.authEstablishmentInProgress = false;
4082
+ const carriedIdentityAuthority = Boolean(this.client.getAccessToken()) || this.client.hasClaimedSessionAuthority();
4083
+ this.accessToken = null;
4084
+ this.accountId = null;
4085
+ this.customerId = null;
4086
+ this.customerData = { name: "", email: null, phone: null };
4087
+ this.addressData = null;
4088
+ this.paymentData = null;
4089
+ this.clearPersistedSession();
4090
+ if (carriedIdentityAuthority) {
4091
+ this.client.clearSession();
4092
+ } else {
4093
+ this.client.setAccessToken(null);
4094
+ }
4095
+ this.broadcastClearedSession();
4096
+ if (message.contactType === ELEMENTS_AUTH_CONTACT_TYPES.EMAIL) {
3309
4097
  this.customerData.email = message.contact;
3310
4098
  } else {
3311
4099
  this.customerData.phone = message.contact;
3312
4100
  }
3313
4101
  break;
4102
+ }
3314
4103
  case MESSAGE_TYPES.LOGOUT_COMPLETE:
4104
+ this.authGeneration += 1;
4105
+ this.authInProgress = false;
4106
+ this.authEstablishmentInProgress = false;
3315
4107
  if (this.checkoutInProgress && this.activeCheckoutAbort) {
3316
4108
  this.activeCheckoutAbort();
3317
4109
  }
@@ -3323,6 +4115,7 @@ var CimplifyElements = class {
3323
4115
  this.paymentData = null;
3324
4116
  this.clearPersistedSession();
3325
4117
  this.client.clearSession();
4118
+ this.broadcastClearedSession();
3326
4119
  break;
3327
4120
  }
3328
4121
  }
@@ -3336,7 +4129,7 @@ var CimplifyElements = class {
3336
4129
  if (!this.customerData) {
3337
4130
  this.customerData = { name: "", email: null, phone: null };
3338
4131
  }
3339
- if (contactType === "email") {
4132
+ if (contactType === ELEMENTS_AUTH_CONTACT_TYPES.EMAIL) {
3340
4133
  this.customerData.email = contact;
3341
4134
  } else {
3342
4135
  this.customerData.phone = contact;
@@ -3456,10 +4249,9 @@ var CimplifyElement = class {
3456
4249
  const iframe = this.iframe;
3457
4250
  if (!iframe || !iframe.isConnected) {
3458
4251
  if (iframe && !iframe.isConnected) {
3459
- console.warn(
3460
- "[cimplify:checkout] sendMessage DROPPED \u2014 iframe detached",
3461
- { type: message.type }
3462
- );
4252
+ console.warn("[cimplify:checkout] sendMessage DROPPED \u2014 iframe detached", {
4253
+ type: message.type
4254
+ });
3463
4255
  }
3464
4256
  return;
3465
4257
  }
@@ -3500,7 +4292,7 @@ var CimplifyElement = class {
3500
4292
  if (!resolvedBusinessId || !this.container || !this.mounted) {
3501
4293
  console.error("Unable to mount element without a resolved business ID");
3502
4294
  this.emit(EVENT_TYPES.ERROR, {
3503
- code: "BUSINESS_ID_REQUIRED",
4295
+ code: ErrorCode.BUSINESS_ID_REQUIRED,
3504
4296
  message: "Unable to initialize checkout without a business ID."
3505
4297
  });
3506
4298
  return;
@@ -3555,16 +4347,19 @@ var CimplifyElement = class {
3555
4347
  orderTypes: this.options.orderTypes,
3556
4348
  defaultOrderType: this.options.defaultOrderType,
3557
4349
  renderSubmitButton: true,
3558
- submitLabel: this.options.submitLabel
4350
+ submitLabel: this.options.submitLabel,
4351
+ intentId: this.options.intentId,
4352
+ hostedSessionId: this.options.hostedSessionId,
4353
+ initialTerms: this.options.initialTerms
3559
4354
  });
3560
4355
  const token = this.parent.getAccessToken();
3561
- if (token) {
3562
- this.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
3563
- }
3564
4356
  const sessionToken = this.parent.getSessionToken();
3565
4357
  if (sessionToken) {
3566
4358
  this.sendMessage({ type: MESSAGE_TYPES.SET_SESSION, sessionToken });
3567
4359
  }
4360
+ if (token) {
4361
+ this.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
4362
+ }
3568
4363
  this.pendingInit = null;
3569
4364
  }
3570
4365
  this.flushPending();
@@ -3613,4 +4408,4 @@ function createElements(client, businessId, options) {
3613
4408
  return new CimplifyElements(client, businessId, options);
3614
4409
  }
3615
4410
 
3616
- export { AuthService, BusinessService, CartOperations, CatalogueQueries, CheckoutService, CimplifyElement, CimplifyElements, DEFAULT_ISSUER, ELEMENTS_AUTH_CONTACT_TYPES, ELEMENTS_AUTH_MODES, ELEMENTS_AUTH_PROMPTS, ELEMENT_TYPES, EVENT_TYPES, FxService, InventoryService, LinkService, LiteService, MESSAGE_TYPES, OrderQueries, PKCE_COOKIE, PKCE_COOKIE_PATH, SchedulingService, combine, combineObject, createElements, decodePkceState, err, flatMap, fromPromise, generateIdempotencyKey, generatePkce, generateState, getAuthorizationEndpoint, getOrElse, isErr, isOk, mapError, mapResult, ok, safe, startSignIn, toNullable, tryCatch, unwrap };
4411
+ export { AuthService, BusinessService, CartOperations, CatalogueQueries, CheckoutService, CimplifyElement, CimplifyElements, DEFAULT_ISSUER, ELEMENTS_AUTH_CONTACT_TYPES, ELEMENTS_AUTH_MODES, ELEMENTS_AUTH_PROMPTS, ELEMENT_TYPES, EVENT_TYPES, FxService, InventoryService, LinkService, LiteService, MESSAGE_TYPES, OrderQueries, PAYMENT_AUTHORIZATION_OUTCOME_STATUS, PAYMENT_AUTHORIZATION_PROVIDER, PKCE_COOKIE, PKCE_COOKIE_PATH, PaymentAuthorizationAdapterRegistry, PaystackAuthorizationAdapter, SchedulingService, StripeAuthorizationAdapter, clearIntentIdempotencyKey, combine, combineObject, createDefaultPaymentAuthorizationAdapterRegistry, createElements, createPurchaseIntentConfirmation, decodePkceState, err, flatMap, fromPromise, generateIdempotencyKey, generatePkce, generateState, getAuthorizationEndpoint, getOrElse, intentIdempotencyKey, isErr, isOk, mapError, mapResult, ok, purchaseIntentRequiresPaymentOption, safe, selectPurchaseIntentPaymentOption, startSignIn, toNullable, tryCatch, unwrap };