@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.
- package/dist/advanced.d.mts +3 -7
- package/dist/advanced.d.ts +3 -7
- package/dist/advanced.js +23 -23
- package/dist/advanced.mjs +4 -4
- package/dist/{client-DHBwvh7U.d.ts → catalogue-BlQ85Uma.d.mts} +492 -748
- package/dist/{client-BfpNjJ_a.d.mts → catalogue-C8TqOj9K.d.ts} +492 -748
- package/dist/{payment-H5LoIvXU.d.mts → checkout-BqK9XUz5.d.mts} +4338 -1495
- package/dist/{payment-H5LoIvXU.d.ts → checkout-BqK9XUz5.d.ts} +4338 -1495
- package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
- package/dist/{chunk-C2JSNJUC.mjs → chunk-6G3ANKQA.mjs} +1080 -369
- package/dist/{chunk-QOWPGS7Z.js → chunk-BWEIMKLF.js} +572 -17
- package/dist/{chunk-JSWJ4XDE.js → chunk-DQEXY542.js} +1630 -824
- package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
- package/dist/{chunk-VIL6IQCG.js → chunk-HYFA67A7.js} +1084 -373
- package/dist/{chunk-H7ACMHM3.mjs → chunk-L5SNEU2O.mjs} +553 -16
- package/dist/{chunk-JG5CQNCB.js → chunk-RBNBSQI7.mjs} +221 -19
- package/dist/{chunk-VNPEYLB2.mjs → chunk-SFUJMS5E.js} +265 -2
- package/dist/{chunk-M4URVKHE.mjs → chunk-U54CWUUT.mjs} +11 -2
- package/dist/{chunk-GERXZQSX.mjs → chunk-UFOWPN3D.mjs} +967 -582
- package/dist/{chunk-J6PNHDHL.mjs → chunk-V4457OKB.mjs} +1580 -785
- package/dist/{chunk-R6OLQ33I.js → chunk-WXREIBFW.js} +1041 -654
- package/dist/{chunk-NNG6IFFX.js → chunk-XAUSDMMZ.js} +13 -3
- package/dist/{client-DpqRobY9.d.mts → client-DhXeiUrf.d.ts} +2 -2
- package/dist/{client-Dqg2mc-7.d.ts → client-mXiUrV4X.d.mts} +2 -2
- package/dist/{index-C8uDqLwJ.d.ts → index-B0AdZvrM.d.mts} +9 -4
- package/dist/{index-e2y6AK70.d.mts → index-CK8Nk5uQ.d.ts} +9 -4
- package/dist/index-DPWI5XcU.d.ts +8 -0
- package/dist/index-DVfUh-ci.d.mts +8 -0
- package/dist/index.d.mts +34 -15
- package/dist/index.d.ts +34 -15
- package/dist/index.js +306 -133
- package/dist/index.mjs +7 -6
- package/dist/mock/cli.mjs +1494 -395
- package/dist/mock/library.js +1220 -361
- package/dist/mock/library.mjs +1220 -361
- package/dist/mock/msw.js +1220 -361
- package/dist/mock/msw.mjs +1220 -361
- package/dist/{price-C2RqscMY.d.ts → price-6hQIIac9.d.ts} +1 -1
- package/dist/{price-BvePgDXe.d.mts → price-BUDv1rI7.d.mts} +1 -1
- package/dist/react.d.mts +10 -5
- package/dist/react.d.ts +10 -5
- package/dist/react.js +279 -225
- package/dist/react.mjs +140 -86
- package/dist/{server-DoXIpSd7.d.ts → server-DJ0mSs9i.d.mts} +33 -57
- package/dist/{server-C7FyLdrz.d.mts → server-DSTX76Ki.d.ts} +33 -57
- package/dist/server.d.mts +3 -4
- package/dist/server.d.ts +3 -4
- package/dist/server.js +12 -11
- package/dist/server.mjs +6 -5
- package/dist/styles.css +2 -2
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +4 -4
- package/dist/testing/msw.mjs +3 -3
- package/dist/testing/suite.d.mts +5 -6
- package/dist/testing/suite.d.ts +5 -6
- package/dist/testing/suite.js +29 -28
- package/dist/testing/suite.mjs +10 -9
- package/dist/testing.d.mts +2217 -14
- package/dist/testing.d.ts +2217 -14
- package/dist/testing.js +173 -83
- package/dist/testing.mjs +28 -10
- package/dist/utils.d.mts +3 -4
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +35 -31
- package/dist/utils.mjs +2 -2
- package/package.json +9 -9
- package/registry/chat-widget.json +1 -1
- package/registry/order-history.json +1 -1
- package/registry/order-summary.json +1 -1
- package/dist/link-cuNVYc2s.d.mts +0 -297
- package/dist/link-cuNVYc2s.d.ts +0 -297
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { CimplifyError, isSupportedCurrency,
|
|
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/
|
|
789
|
-
var
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
var
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
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
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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
|
-
|
|
865
|
-
|
|
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
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
|
|
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
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
false
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
|
|
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
|
-
|
|
931
|
-
|
|
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
|
-
|
|
934
|
-
|
|
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 (
|
|
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
|
-
|
|
942
|
-
|
|
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 (
|
|
917
|
+
if (result.error === PAYSTACK_POPUP_ERROR.PROVIDER_UNAVAILABLE) {
|
|
946
918
|
return {
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
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 {
|
|
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
|
-
|
|
955
|
-
|
|
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
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
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
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
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
|
|
982
|
-
if (!
|
|
1076
|
+
const modal = createStripeModal();
|
|
1077
|
+
if (!modal) {
|
|
983
1078
|
return {
|
|
984
|
-
|
|
985
|
-
|
|
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
|
|
1085
|
+
let paymentElement;
|
|
1086
|
+
let elements;
|
|
989
1087
|
try {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
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
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
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
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
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
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
return
|
|
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 (
|
|
1094
|
-
|
|
1156
|
+
if (settled || context.signal?.aborted) {
|
|
1157
|
+
settle({ status: PAYMENT_AUTHORIZATION_OUTCOME_STATUS.CANCELLED });
|
|
1158
|
+
return;
|
|
1095
1159
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
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
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
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
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
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
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
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
|
-
|
|
1140
|
-
const
|
|
1141
|
-
|
|
1142
|
-
|
|
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
|
-
|
|
1213
|
-
|
|
1214
|
-
"Payment confirmation timed out. Please retry checkout.",
|
|
1215
|
-
true
|
|
1216
|
-
);
|
|
1224
|
+
this.adapters.set(provider, adapter);
|
|
1225
|
+
return this;
|
|
1217
1226
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
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
|
-
|
|
1314
|
-
|
|
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
|
-
|
|
1340
|
-
|
|
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
|
-
|
|
1367
|
-
|
|
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
|
-
|
|
1386
|
-
|
|
1239
|
+
async authorize(action, options) {
|
|
1240
|
+
const provider = normalizeProvider(action.provider);
|
|
1241
|
+
if (!provider) {
|
|
1387
1242
|
return {
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
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
|
-
|
|
1249
|
+
const adapter = this.adapters.get(provider);
|
|
1250
|
+
if (!adapter) {
|
|
1394
1251
|
return {
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
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
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
return
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
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
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
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
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
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
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
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
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
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
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
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
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
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
|
-
|
|
1456
|
-
|
|
1457
|
-
return isPaymentStatusFailure(normalized.status);
|
|
1371
|
+
if (form.payment_method === LEGACY_CHECKOUT_PAYMENT_METHOD_ALIAS.STORE_CREDIT) {
|
|
1372
|
+
return void 0;
|
|
1458
1373
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
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
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
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
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
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 ===
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 ?
|
|
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
|
-
|
|
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
|
|
1724
|
+
async startIntentCheckout(data, options) {
|
|
1599
1725
|
const invalid = this.validateCheckoutData(data);
|
|
1600
|
-
if (invalid)
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
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
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
const
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
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
|
-
|
|
1622
|
-
|
|
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
|
-
|
|
1847
|
+
ErrorCode.NOT_IMPLEMENTED,
|
|
1630
1848
|
"initializePayment is not available in REST mode.",
|
|
1631
1849
|
false
|
|
1632
1850
|
)
|
|
1633
1851
|
);
|
|
1634
1852
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
1953
|
+
notifyCheckoutStatus(data.on_status_change, CHECKOUT_STATUS.PREPARING);
|
|
1661
1954
|
if (!data.cart_id) {
|
|
1662
1955
|
return ok(
|
|
1663
1956
|
toTerminalFailure(
|
|
1664
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1727
|
-
if (
|
|
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
|
-
|
|
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,
|
|
1746
|
-
const
|
|
1747
|
-
if (!
|
|
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
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
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, {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2451
|
+
this.activationGeneration += 1;
|
|
2452
|
+
const request = safe(this.client.linkPost("/v1/link/auth/logout"));
|
|
1871
2453
|
this.client.clearSession();
|
|
1872
|
-
return
|
|
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
|
-
|
|
1920
|
-
|
|
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")
|
|
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
|
-
|
|
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 ||
|
|
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 ||
|
|
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", {
|
|
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
|
-
|
|
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 (
|
|
2932
|
-
this.
|
|
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
|
-
|
|
2936
|
-
"A
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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", {
|
|
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
|
-
|
|
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
|
-
|
|
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", {
|
|
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
|
-
|
|
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", {
|
|
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
|
-
|
|
3132
|
-
|
|
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)
|
|
3193
|
-
|
|
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
|
|
3200
|
-
|
|
3201
|
-
|
|
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.
|
|
3207
|
-
|
|
3208
|
-
|
|
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
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
3260
|
-
this.broadcastAccessToken(accessToken)
|
|
3261
|
-
|
|
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:
|
|
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
|
-
|
|
3292
|
-
|
|
3293
|
-
this.
|
|
3294
|
-
|
|
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 ===
|
|
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 ===
|
|
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
|
-
|
|
3461
|
-
|
|
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:
|
|
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 };
|