@cimplify/sdk 0.102.0 → 0.103.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.d.mts +3 -3
- package/dist/advanced.d.ts +3 -3
- package/dist/advanced.js +23 -22
- package/dist/advanced.mjs +4 -3
- package/dist/{chunk-DCMXUKDQ.mjs → chunk-C2JSNJUC.mjs} +404 -95
- package/dist/chunk-CNN7Z2IP.js +125 -0
- package/dist/{chunk-DKLHCZ5M.mjs → chunk-GERXZQSX.mjs} +487 -11
- package/dist/chunk-GOHSXBKW.mjs +105 -0
- package/dist/{chunk-4KNFBPV2.mjs → chunk-H7ACMHM3.mjs} +32 -14
- package/dist/{chunk-Z3K2D3TE.mjs → chunk-J6PNHDHL.mjs} +453 -483
- package/dist/{chunk-NIOUSKQQ.js → chunk-JG5CQNCB.js} +5 -0
- package/dist/{chunk-YER7DIOM.js → chunk-JSWJ4XDE.js} +471 -519
- package/dist/{chunk-DLW3RWRK.mjs → chunk-M4URVKHE.mjs} +46 -12
- package/dist/{chunk-WMEC5JWN.js → chunk-NNG6IFFX.js} +50 -13
- package/dist/{chunk-LOWSMGRT.js → chunk-QOWPGS7Z.js} +34 -16
- package/dist/{chunk-YDA46OZ6.js → chunk-R6OLQ33I.js} +560 -80
- package/dist/{chunk-EAZGA7LD.js → chunk-VIL6IQCG.js} +406 -97
- package/dist/{chunk-O3YUHLZL.mjs → chunk-VNPEYLB2.mjs} +5 -0
- package/dist/{client-B40KFazg.d.ts → client-BfpNjJ_a.d.mts} +90 -299
- package/dist/{client-BizGfqB7.d.mts → client-DHBwvh7U.d.ts} +90 -299
- package/dist/{client-BdErclfU.d.mts → client-DpqRobY9.d.mts} +2 -2
- package/dist/{client-nsiMwuC1.d.ts → client-Dqg2mc-7.d.ts} +2 -2
- package/dist/{index-DpmnI__D.d.mts → index-C8uDqLwJ.d.ts} +14 -4
- package/dist/{index-nEyXWIyR.d.ts → index-e2y6AK70.d.mts} +14 -4
- package/dist/index.d.mts +81 -8
- package/dist/index.d.ts +81 -8
- package/dist/index.js +202 -169
- package/dist/index.mjs +6 -5
- package/dist/link-cuNVYc2s.d.mts +297 -0
- package/dist/link-cuNVYc2s.d.ts +297 -0
- package/dist/mock/cli.mjs +410 -94
- package/dist/mock/library.js +410 -94
- package/dist/mock/library.mjs +410 -94
- package/dist/mock/msw.js +410 -94
- package/dist/mock/msw.mjs +410 -94
- package/dist/{product-Byf0unSy.d.mts → payment-H5LoIvXU.d.mts} +189 -7
- package/dist/{product-Byf0unSy.d.ts → payment-H5LoIvXU.d.ts} +189 -7
- package/dist/{price-CS4E5zLS.d.mts → price-BvePgDXe.d.mts} +1 -1
- package/dist/{price-BstChV9a.d.ts → price-C2RqscMY.d.ts} +1 -1
- package/dist/react.d.mts +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.js +141 -140
- package/dist/react.mjs +5 -4
- package/dist/{server-DqbZh8Bk.d.mts → server-C7FyLdrz.d.mts} +11 -9
- package/dist/{server-Cj40R2ZD.d.ts → server-DoXIpSd7.d.ts} +11 -9
- package/dist/server.d.mts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +11 -10
- package/dist/server.mjs +5 -4
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +4 -3
- package/dist/testing/msw.mjs +3 -2
- package/dist/testing/suite.d.mts +6 -6
- package/dist/testing/suite.d.ts +6 -6
- package/dist/testing/suite.js +26 -25
- package/dist/testing/suite.mjs +7 -6
- package/dist/testing.d.mts +154 -55
- package/dist/testing.d.ts +154 -55
- package/dist/testing.js +83 -82
- package/dist/testing.mjs +8 -7
- package/dist/utils.d.mts +4 -4
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +37 -29
- package/dist/utils.mjs +2 -2
- package/package.json +7 -1
- package/dist/payment-l0rkieEQ.d.mts +0 -113
- package/dist/payment-vzNnIi1n.d.ts +0 -113
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { MOBILE_MONEY_PROVIDER, PAYMENT_METHOD, MAX_PAYMENT_AUTHORIZATION_STEPS } from './chunk-GOHSXBKW.mjs';
|
|
2
|
+
import { normalizeStatusResponse, paymentAuthorizationUrl, isPaymentStatusSuccess, isPaymentStatusFailure, isPaymentStatusRequiresAction } from './chunk-M4URVKHE.mjs';
|
|
3
|
+
import { CimplifyError, isSupportedCurrency, ErrorCode, enrichError, getErrorHint } from './chunk-VNPEYLB2.mjs';
|
|
3
4
|
|
|
4
5
|
// src/types/result.ts
|
|
5
6
|
function ok(value) {
|
|
@@ -784,222 +785,15 @@ var CartOperations = class {
|
|
|
784
785
|
}
|
|
785
786
|
};
|
|
786
787
|
|
|
787
|
-
// src/constants.ts
|
|
788
|
-
var CHECKOUT_MODE = {
|
|
789
|
-
LINK: "link",
|
|
790
|
-
GUEST: "guest"
|
|
791
|
-
};
|
|
792
|
-
var ORDER_TYPE = {
|
|
793
|
-
DELIVERY: "delivery",
|
|
794
|
-
PICKUP: "pickup",
|
|
795
|
-
DINE_IN: "dine-in",
|
|
796
|
-
WALK_IN: "walk-in"
|
|
797
|
-
};
|
|
798
|
-
var PAYMENT_METHOD = {
|
|
799
|
-
MOBILE_MONEY: "mobile_money",
|
|
800
|
-
CARD: "card"
|
|
801
|
-
};
|
|
802
|
-
var CHECKOUT_STEP = {
|
|
803
|
-
AUTHENTICATION: "authentication",
|
|
804
|
-
ORDER_DETAILS: "order_details",
|
|
805
|
-
PAYMENT_METHOD: "payment_method",
|
|
806
|
-
PAYMENT: "payment",
|
|
807
|
-
CONFIRMATION: "confirmation"
|
|
808
|
-
};
|
|
809
|
-
var PAYMENT_STATE = {
|
|
810
|
-
INITIAL: "initial",
|
|
811
|
-
PREPARING: "preparing",
|
|
812
|
-
PROCESSING: "processing",
|
|
813
|
-
VERIFYING: "verifying",
|
|
814
|
-
AWAITING_AUTHORIZATION: "awaiting_authorization",
|
|
815
|
-
SUCCESS: "success",
|
|
816
|
-
ERROR: "error",
|
|
817
|
-
TIMEOUT: "timeout"
|
|
818
|
-
};
|
|
819
|
-
var PICKUP_TIME_TYPE = {
|
|
820
|
-
ASAP: "asap",
|
|
821
|
-
SCHEDULED: "scheduled"
|
|
822
|
-
};
|
|
823
|
-
var MOBILE_MONEY_PROVIDER = {
|
|
824
|
-
MTN: "mtn",
|
|
825
|
-
VODAFONE: "vodafone",
|
|
826
|
-
TELECEL: "telecel",
|
|
827
|
-
AIRTEL: "airtel",
|
|
828
|
-
AIRTELTIGO: "airteltigo",
|
|
829
|
-
MPESA: "mpesa"
|
|
830
|
-
};
|
|
831
|
-
var AUTHORIZATION_TYPE = {
|
|
832
|
-
OTP: "otp",
|
|
833
|
-
PIN: "pin",
|
|
834
|
-
PHONE: "phone",
|
|
835
|
-
BIRTHDAY: "birthday",
|
|
836
|
-
ADDRESS: "address"
|
|
837
|
-
};
|
|
838
|
-
var DEVICE_TYPE = {
|
|
839
|
-
MOBILE: "mobile",
|
|
840
|
-
DESKTOP: "desktop",
|
|
841
|
-
TABLET: "tablet",
|
|
842
|
-
UNKNOWN: "unknown"
|
|
843
|
-
};
|
|
844
|
-
var CONTACT_TYPE = {
|
|
845
|
-
PHONE: "phone",
|
|
846
|
-
EMAIL: "email"
|
|
847
|
-
};
|
|
848
|
-
var LINK_QUERY = {
|
|
849
|
-
DATA: "link.data",
|
|
850
|
-
ADDRESSES: "link.addresses",
|
|
851
|
-
MOBILE_MONEY: "link.mobile_money",
|
|
852
|
-
PREFERENCES: "link.preferences",
|
|
853
|
-
SESSIONS: "link.sessions"
|
|
854
|
-
};
|
|
855
|
-
var LINK_MUTATION = {
|
|
856
|
-
CHECK_STATUS: "link.check_status",
|
|
857
|
-
ENROLL: "link.enroll",
|
|
858
|
-
ENROLL_AND_LINK_ORDER: "link.enroll_and_link_order",
|
|
859
|
-
UPDATE_PREFERENCES: "link.update_preferences",
|
|
860
|
-
CREATE_ADDRESS: "link.create_address",
|
|
861
|
-
UPDATE_ADDRESS: "link.update_address",
|
|
862
|
-
DELETE_ADDRESS: "link.delete_address",
|
|
863
|
-
SET_DEFAULT_ADDRESS: "link.set_default_address",
|
|
864
|
-
TRACK_ADDRESS_USAGE: "link.track_address_usage",
|
|
865
|
-
CREATE_MOBILE_MONEY: "link.create_mobile_money",
|
|
866
|
-
DELETE_MOBILE_MONEY: "link.delete_mobile_money",
|
|
867
|
-
SET_DEFAULT_MOBILE_MONEY: "link.set_default_mobile_money",
|
|
868
|
-
TRACK_MOBILE_MONEY_USAGE: "link.track_mobile_money_usage",
|
|
869
|
-
VERIFY_MOBILE_MONEY: "link.verify_mobile_money",
|
|
870
|
-
REVOKE_SESSION: "link.revoke_session",
|
|
871
|
-
REVOKE_ALL_SESSIONS: "link.revoke_all_sessions"
|
|
872
|
-
};
|
|
873
|
-
var AUTH_MUTATION = {
|
|
874
|
-
REQUEST_OTP: "auth.request_otp",
|
|
875
|
-
VERIFY_OTP: "auth.verify_otp"
|
|
876
|
-
};
|
|
877
|
-
var CHECKOUT_MUTATION = {
|
|
878
|
-
PROCESS: "checkout.process"
|
|
879
|
-
};
|
|
880
|
-
var PAYMENT_MUTATION = {
|
|
881
|
-
SUBMIT_AUTHORIZATION: "payment.submit_authorization",
|
|
882
|
-
CHECK_STATUS: "order.poll_payment_status"
|
|
883
|
-
};
|
|
884
|
-
var ORDER_MUTATION = {
|
|
885
|
-
UPDATE_CUSTOMER: "order.update_order_customer"
|
|
886
|
-
};
|
|
887
|
-
var DEFAULT_CURRENCY = "GHS";
|
|
888
|
-
var DEFAULT_COUNTRY = "GHA";
|
|
889
|
-
|
|
890
|
-
// src/utils/paystack.ts
|
|
891
|
-
async function openPaystackPopup(options, signal) {
|
|
892
|
-
if (typeof window === "undefined") {
|
|
893
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
894
|
-
}
|
|
895
|
-
let PaystackPop;
|
|
896
|
-
try {
|
|
897
|
-
const specifier = "@paystack/inline-js";
|
|
898
|
-
const imported = await import(
|
|
899
|
-
/* webpackIgnore: true */
|
|
900
|
-
/* @vite-ignore */
|
|
901
|
-
specifier
|
|
902
|
-
);
|
|
903
|
-
PaystackPop = imported.default;
|
|
904
|
-
} catch {
|
|
905
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
906
|
-
}
|
|
907
|
-
return new Promise((resolve) => {
|
|
908
|
-
let settled = false;
|
|
909
|
-
const resolveOnce = (result) => {
|
|
910
|
-
if (settled) {
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
settled = true;
|
|
914
|
-
if (signal) {
|
|
915
|
-
signal.removeEventListener("abort", onAbort);
|
|
916
|
-
}
|
|
917
|
-
resolve(result);
|
|
918
|
-
};
|
|
919
|
-
const onAbort = () => {
|
|
920
|
-
resolveOnce({ success: false, error: "CANCELLED" });
|
|
921
|
-
};
|
|
922
|
-
if (signal?.aborted) {
|
|
923
|
-
resolveOnce({ success: false, error: "CANCELLED" });
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
if (signal) {
|
|
927
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
928
|
-
}
|
|
929
|
-
try {
|
|
930
|
-
const popup = new PaystackPop();
|
|
931
|
-
popup.newTransaction({
|
|
932
|
-
key: options.key,
|
|
933
|
-
email: options.email,
|
|
934
|
-
amount: options.amount,
|
|
935
|
-
currency: options.currency,
|
|
936
|
-
reference: options.reference,
|
|
937
|
-
accessCode: options.accessCode,
|
|
938
|
-
onSuccess: (transaction) => {
|
|
939
|
-
resolveOnce({
|
|
940
|
-
success: true,
|
|
941
|
-
reference: transaction.reference ?? options.reference
|
|
942
|
-
});
|
|
943
|
-
},
|
|
944
|
-
onCancel: () => {
|
|
945
|
-
resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
|
|
946
|
-
},
|
|
947
|
-
onError: (error) => {
|
|
948
|
-
resolveOnce({
|
|
949
|
-
success: false,
|
|
950
|
-
error: error?.message || "PAYMENT_FAILED"
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
});
|
|
954
|
-
} catch {
|
|
955
|
-
resolveOnce({ success: false, error: "POPUP_BLOCKED" });
|
|
956
|
-
}
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
|
|
960
788
|
// src/utils/checkout-resolver.ts
|
|
961
789
|
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
962
790
|
var DEFAULT_MAX_POLL_ATTEMPTS = 60;
|
|
963
791
|
var MAX_CONSECUTIVE_NETWORK_ERRORS = 5;
|
|
964
|
-
var CARD_PROVIDER_PAYSTACK = "paystack";
|
|
965
792
|
var NEXT_ACTION_NONE = "none";
|
|
966
793
|
var NEXT_ACTION_CARD_POPUP = "card_popup";
|
|
967
794
|
var NEXT_ACTION_REDIRECT = "redirect";
|
|
968
795
|
var NEXT_ACTION_AUTHORIZATION = "authorization";
|
|
969
|
-
|
|
970
|
-
if (!error || error === "PAYMENT_CANCELLED" || error === "CANCELLED") {
|
|
971
|
-
return "PAYMENT_CANCELLED";
|
|
972
|
-
}
|
|
973
|
-
if (error === "PROVIDER_UNAVAILABLE") {
|
|
974
|
-
return "PROVIDER_UNAVAILABLE";
|
|
975
|
-
}
|
|
976
|
-
if (error === "POPUP_BLOCKED") {
|
|
977
|
-
return "POPUP_BLOCKED";
|
|
978
|
-
}
|
|
979
|
-
return "PAYMENT_FAILED";
|
|
980
|
-
}
|
|
981
|
-
function normalizeCardProvider(value) {
|
|
982
|
-
const normalized = value?.trim().toLowerCase();
|
|
983
|
-
return normalized && normalized.length > 0 ? normalized : CARD_PROVIDER_PAYSTACK;
|
|
984
|
-
}
|
|
985
|
-
async function openCardPopup(provider, checkoutResult, email, currency, signal) {
|
|
986
|
-
if (typeof window === "undefined") {
|
|
987
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
988
|
-
}
|
|
989
|
-
if (provider === CARD_PROVIDER_PAYSTACK) {
|
|
990
|
-
return openPaystackPopup(
|
|
991
|
-
{
|
|
992
|
-
key: checkoutResult.public_key || "",
|
|
993
|
-
email,
|
|
994
|
-
accessCode: checkoutResult.client_secret,
|
|
995
|
-
reference: checkoutResult.payment_reference || checkoutResult.client_secret,
|
|
996
|
-
currency
|
|
997
|
-
},
|
|
998
|
-
signal
|
|
999
|
-
);
|
|
1000
|
-
}
|
|
1001
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
1002
|
-
}
|
|
796
|
+
var NEXT_ACTION_POLL = "poll";
|
|
1003
797
|
var VALID_MOBILE_MONEY_PROVIDERS = new Set(Object.values(MOBILE_MONEY_PROVIDER));
|
|
1004
798
|
function isValidMobileMoneyProvider(value) {
|
|
1005
799
|
return VALID_MOBILE_MONEY_PROVIDERS.has(value);
|
|
@@ -1016,6 +810,32 @@ function normalizeAuthorizationType(value) {
|
|
|
1016
810
|
}
|
|
1017
811
|
return void 0;
|
|
1018
812
|
}
|
|
813
|
+
function normalizeAuthorizationSubmission(expectedType, submission) {
|
|
814
|
+
if (!submission || submission.type !== expectedType) {
|
|
815
|
+
throw new Error(`Expected ${expectedType} authorization details.`);
|
|
816
|
+
}
|
|
817
|
+
if (submission.type === "address") {
|
|
818
|
+
const address = submission.address.trim();
|
|
819
|
+
const city = submission.city.trim();
|
|
820
|
+
const state = submission.state.trim();
|
|
821
|
+
const zipCode = submission.zip_code.trim();
|
|
822
|
+
if (!address || !city || !state || !zipCode) {
|
|
823
|
+
throw new Error("Complete all authorization address fields.");
|
|
824
|
+
}
|
|
825
|
+
return {
|
|
826
|
+
type: "address",
|
|
827
|
+
address,
|
|
828
|
+
city,
|
|
829
|
+
state,
|
|
830
|
+
zip_code: zipCode
|
|
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
|
+
}
|
|
1019
839
|
function formatMoney(value) {
|
|
1020
840
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1021
841
|
return value.toFixed(2);
|
|
@@ -1036,6 +856,27 @@ function isAbortError(error) {
|
|
|
1036
856
|
}
|
|
1037
857
|
return error instanceof Error && (error.name === "AbortError" || error.message === "CANCELLED");
|
|
1038
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
|
+
const onAbort = () => {
|
|
864
|
+
signal.removeEventListener("abort", onAbort);
|
|
865
|
+
reject(createAbortError());
|
|
866
|
+
};
|
|
867
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
868
|
+
promise.then(
|
|
869
|
+
(value) => {
|
|
870
|
+
signal.removeEventListener("abort", onAbort);
|
|
871
|
+
resolve(value);
|
|
872
|
+
},
|
|
873
|
+
(error) => {
|
|
874
|
+
signal.removeEventListener("abort", onAbort);
|
|
875
|
+
reject(error);
|
|
876
|
+
}
|
|
877
|
+
);
|
|
878
|
+
});
|
|
879
|
+
}
|
|
1039
880
|
var CheckoutResolver = class {
|
|
1040
881
|
constructor(options) {
|
|
1041
882
|
this.client = options.client;
|
|
@@ -1043,6 +884,7 @@ var CheckoutResolver = class {
|
|
|
1043
884
|
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
1044
885
|
this.maxPollAttempts = options.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;
|
|
1045
886
|
this.onStatusChange = options.onStatusChange;
|
|
887
|
+
this.onAuthorizationUiChange = options.onAuthorizationUiChange;
|
|
1046
888
|
this.onAuthorizationRequired = options.onAuthorizationRequired;
|
|
1047
889
|
this.signal = options.signal;
|
|
1048
890
|
this.returnUrl = options.returnUrl;
|
|
@@ -1061,116 +903,22 @@ var CheckoutResolver = class {
|
|
|
1061
903
|
if (!checkoutResult.order_id) {
|
|
1062
904
|
return this.fail("CHECKOUT_FAILED", "Checkout did not return an order ID.", false);
|
|
1063
905
|
}
|
|
1064
|
-
if (checkoutResult.next_action) {
|
|
1065
|
-
return this.resolveNextAction(checkoutResult);
|
|
1066
|
-
}
|
|
1067
|
-
let latestCheckoutResult = checkoutResult;
|
|
1068
|
-
let authorizationType = normalizeAuthorizationType(checkoutResult.authorization_type);
|
|
1069
|
-
let paymentReference = checkoutResult.payment_reference;
|
|
1070
906
|
if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
|
|
1071
907
|
return this.finalizeSuccess(checkoutResult);
|
|
1072
908
|
}
|
|
1073
|
-
if (
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
order_id: checkoutResult.order_id,
|
|
1078
|
-
order_number: checkoutResult.order_number
|
|
909
|
+
if (this.isFailureStatus(checkoutResult.payment_status)) {
|
|
910
|
+
const normalized = normalizeStatusResponse({
|
|
911
|
+
status: checkoutResult.payment_status,
|
|
912
|
+
paid: false
|
|
1079
913
|
});
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
false
|
|
1085
|
-
);
|
|
1086
|
-
}
|
|
1087
|
-
const popupResult = await openCardPopup(
|
|
1088
|
-
provider,
|
|
1089
|
-
checkoutResult,
|
|
1090
|
-
this.checkoutData.customer.email || "customer@cimplify.io",
|
|
1091
|
-
this.getOrderCurrency(checkoutResult),
|
|
1092
|
-
this.signal
|
|
914
|
+
return this.fail(
|
|
915
|
+
normalized.status.toUpperCase(),
|
|
916
|
+
checkoutResult.display_text || "Payment failed before confirmation.",
|
|
917
|
+
false
|
|
1093
918
|
);
|
|
1094
|
-
if (!popupResult.success) {
|
|
1095
|
-
const popupError = normalizeCardPopupError(popupResult.error);
|
|
1096
|
-
if (popupError === "POPUP_BLOCKED") {
|
|
1097
|
-
return this.fail(
|
|
1098
|
-
"POPUP_BLOCKED",
|
|
1099
|
-
"Unable to open card payment popup. Please allow popups and try again.",
|
|
1100
|
-
true
|
|
1101
|
-
);
|
|
1102
|
-
}
|
|
1103
|
-
if (popupError === "PROVIDER_UNAVAILABLE") {
|
|
1104
|
-
return this.fail(
|
|
1105
|
-
"PROVIDER_UNAVAILABLE",
|
|
1106
|
-
"Card payment provider is unavailable.",
|
|
1107
|
-
false
|
|
1108
|
-
);
|
|
1109
|
-
}
|
|
1110
|
-
return this.fail(
|
|
1111
|
-
"PAYMENT_CANCELLED",
|
|
1112
|
-
"Card payment was cancelled before completion.",
|
|
1113
|
-
true
|
|
1114
|
-
);
|
|
1115
|
-
}
|
|
1116
|
-
paymentReference = popupResult.reference || paymentReference;
|
|
1117
|
-
}
|
|
1118
|
-
if (checkoutResult.authorization_url) {
|
|
1119
|
-
if (typeof window !== "undefined" && this.returnUrl) {
|
|
1120
|
-
window.location.assign(checkoutResult.authorization_url);
|
|
1121
|
-
return this.fail(
|
|
1122
|
-
"REDIRECT_REQUIRED",
|
|
1123
|
-
"Redirecting to complete payment authorization.",
|
|
1124
|
-
true
|
|
1125
|
-
);
|
|
1126
|
-
}
|
|
1127
|
-
if (typeof window !== "undefined") {
|
|
1128
|
-
const popup = window.open(
|
|
1129
|
-
checkoutResult.authorization_url,
|
|
1130
|
-
"cimplify-auth",
|
|
1131
|
-
"width=520,height=760,noopener,noreferrer"
|
|
1132
|
-
);
|
|
1133
|
-
if (!popup) {
|
|
1134
|
-
return this.fail(
|
|
1135
|
-
"POPUP_BLOCKED",
|
|
1136
|
-
"Authorization popup was blocked. Please allow popups and retry.",
|
|
1137
|
-
true
|
|
1138
|
-
);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
if (checkoutResult.requires_authorization || isPaymentStatusRequiresAction(checkoutResult.payment_status)) {
|
|
1143
|
-
const authorization = await this.handleAuthorization({
|
|
1144
|
-
authorizationType,
|
|
1145
|
-
paymentReference,
|
|
1146
|
-
displayText: checkoutResult.display_text,
|
|
1147
|
-
provider: checkoutResult.provider
|
|
1148
|
-
});
|
|
1149
|
-
if (!authorization.ok) {
|
|
1150
|
-
return authorization.result;
|
|
1151
|
-
}
|
|
1152
|
-
if (authorization.value) {
|
|
1153
|
-
latestCheckoutResult = authorization.value;
|
|
1154
|
-
paymentReference = authorization.value.payment_reference || paymentReference;
|
|
1155
|
-
authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
|
|
1156
|
-
if (this.isSuccessfulStatus(authorization.value.payment_status)) {
|
|
1157
|
-
return this.finalizeSuccess(authorization.value);
|
|
1158
|
-
}
|
|
1159
|
-
if (this.isFailureStatus(authorization.value.payment_status)) {
|
|
1160
|
-
return this.fail(
|
|
1161
|
-
"AUTHORIZATION_FAILED",
|
|
1162
|
-
authorization.value.display_text || "Payment authorization failed.",
|
|
1163
|
-
false
|
|
1164
|
-
);
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
919
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
latestCheckoutResult,
|
|
1171
|
-
paymentReference,
|
|
1172
|
-
authorizationType
|
|
1173
|
-
});
|
|
920
|
+
const nextAction = checkoutResult.next_action ?? this.deriveLegacyNextAction(checkoutResult);
|
|
921
|
+
return this.resolveNextAction({ ...checkoutResult, next_action: nextAction });
|
|
1174
922
|
} catch (error) {
|
|
1175
923
|
if (isAbortError(error)) {
|
|
1176
924
|
return this.fail("CANCELLED", "Checkout was cancelled.", true);
|
|
@@ -1179,99 +927,172 @@ var CheckoutResolver = class {
|
|
|
1179
927
|
return this.fail("CHECKOUT_FAILED", message, true);
|
|
1180
928
|
}
|
|
1181
929
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
930
|
+
deriveLegacyNextAction(checkoutResult) {
|
|
931
|
+
if (checkoutResult.client_secret && checkoutResult.public_key) {
|
|
932
|
+
return {
|
|
933
|
+
type: NEXT_ACTION_CARD_POPUP,
|
|
934
|
+
provider: checkoutResult.provider?.trim().toLowerCase() || "",
|
|
935
|
+
client_secret: checkoutResult.client_secret,
|
|
936
|
+
public_key: checkoutResult.public_key
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
if (checkoutResult.authorization_url) {
|
|
940
|
+
return {
|
|
941
|
+
type: NEXT_ACTION_REDIRECT,
|
|
942
|
+
authorization_url: checkoutResult.authorization_url
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
if (checkoutResult.requires_authorization && checkoutResult.authorization_type) {
|
|
946
|
+
return {
|
|
947
|
+
type: NEXT_ACTION_AUTHORIZATION,
|
|
948
|
+
authorization_type: checkoutResult.authorization_type,
|
|
949
|
+
display_text: checkoutResult.display_text
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
return { type: NEXT_ACTION_POLL };
|
|
953
|
+
}
|
|
954
|
+
async runCardPopup(action, checkoutResult) {
|
|
955
|
+
if (typeof action.provider !== "string" || !action.provider.trim() || typeof action.client_secret !== "string" || !action.client_secret.trim() || typeof action.public_key !== "string" || !action.public_key.trim()) {
|
|
956
|
+
return {
|
|
957
|
+
ok: false,
|
|
958
|
+
result: this.fail(
|
|
959
|
+
"INVALID_CARD_AUTHORIZATION",
|
|
960
|
+
"The payment provider returned an incomplete card authorization response.",
|
|
961
|
+
false
|
|
962
|
+
)
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
this.emit("awaiting_authorization", {
|
|
966
|
+
display_text: "Complete payment in the card authorization window.",
|
|
967
|
+
order_id: checkoutResult.order_id,
|
|
968
|
+
order_number: checkoutResult.order_number,
|
|
969
|
+
provider: action.provider
|
|
970
|
+
});
|
|
971
|
+
if (!this.allowPopups) {
|
|
972
|
+
return {
|
|
973
|
+
ok: false,
|
|
974
|
+
result: this.fail(
|
|
975
|
+
"PROVIDER_UNAVAILABLE",
|
|
976
|
+
"Card authorization UI is unavailable in this environment.",
|
|
977
|
+
false
|
|
978
|
+
)
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
const currency = this.getOrderCurrency(checkoutResult);
|
|
982
|
+
if (!currency) {
|
|
983
|
+
return {
|
|
984
|
+
ok: false,
|
|
985
|
+
result: this.fail("INVALID_CURRENCY", "Payment currency is missing or unsupported.", false)
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
let outcome;
|
|
989
|
+
try {
|
|
990
|
+
this.notifyAuthorizationUiChange(true);
|
|
991
|
+
outcome = await this.client.checkout.authorizeCardPopup(action, {
|
|
992
|
+
email: this.checkoutData.customer.email || "customer@cimplify.io",
|
|
993
|
+
currency,
|
|
994
|
+
reference: checkoutResult.payment_reference,
|
|
995
|
+
returnUrl: this.returnUrl,
|
|
996
|
+
signal: this.signal
|
|
997
|
+
});
|
|
998
|
+
} finally {
|
|
999
|
+
this.notifyAuthorizationUiChange(false);
|
|
1000
|
+
}
|
|
1001
|
+
if (outcome.status === "submitted") {
|
|
1002
|
+
return { ok: true, reference: outcome.reference };
|
|
1003
|
+
}
|
|
1004
|
+
if (outcome.status === "cancelled") {
|
|
1005
|
+
return {
|
|
1006
|
+
ok: false,
|
|
1007
|
+
result: this.fail(
|
|
1008
|
+
"PAYMENT_CANCELLED",
|
|
1009
|
+
"Card payment was cancelled before submission.",
|
|
1010
|
+
true
|
|
1011
|
+
)
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
return {
|
|
1015
|
+
ok: false,
|
|
1016
|
+
result: this.fail(outcome.code, outcome.message, outcome.recoverable)
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
async resolveNextAction(checkoutResult, resolutionState = {
|
|
1020
|
+
authorizationSteps: 0,
|
|
1021
|
+
pollAttempts: 0,
|
|
1022
|
+
consecutivePollErrors: 0
|
|
1023
|
+
}) {
|
|
1184
1024
|
if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
|
|
1185
1025
|
return this.finalizeSuccess(checkoutResult);
|
|
1186
1026
|
}
|
|
1187
1027
|
let latestCheckoutResult = checkoutResult;
|
|
1188
1028
|
let paymentReference = checkoutResult.payment_reference;
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
"Card payment popup is unavailable in this environment.",
|
|
1203
|
-
false
|
|
1204
|
-
);
|
|
1029
|
+
let action = checkoutResult.next_action;
|
|
1030
|
+
actionLoop: while (true) {
|
|
1031
|
+
this.ensureNotAborted();
|
|
1032
|
+
switch (action.type) {
|
|
1033
|
+
case NEXT_ACTION_NONE:
|
|
1034
|
+
break actionLoop;
|
|
1035
|
+
case NEXT_ACTION_CARD_POPUP: {
|
|
1036
|
+
const limitFailure = this.consumeAuthorizationStep(resolutionState);
|
|
1037
|
+
if (limitFailure) return limitFailure;
|
|
1038
|
+
const popup = await this.runCardPopup(action, latestCheckoutResult);
|
|
1039
|
+
if (!popup.ok) return popup.result;
|
|
1040
|
+
paymentReference = popup.reference || paymentReference;
|
|
1041
|
+
break actionLoop;
|
|
1205
1042
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
this.signal
|
|
1212
|
-
);
|
|
1213
|
-
if (!popupResult.success) {
|
|
1214
|
-
const popupError = normalizeCardPopupError(popupResult.error);
|
|
1215
|
-
if (popupError === "POPUP_BLOCKED") {
|
|
1216
|
-
return this.fail(
|
|
1217
|
-
"POPUP_BLOCKED",
|
|
1218
|
-
"Unable to open card payment popup. Please allow popups and try again.",
|
|
1219
|
-
true
|
|
1220
|
-
);
|
|
1221
|
-
}
|
|
1222
|
-
if (popupError === "PROVIDER_UNAVAILABLE") {
|
|
1043
|
+
case NEXT_ACTION_REDIRECT: {
|
|
1044
|
+
const limitFailure = this.consumeAuthorizationStep(resolutionState);
|
|
1045
|
+
if (limitFailure) return limitFailure;
|
|
1046
|
+
const authorizationUrl = paymentAuthorizationUrl(action.authorization_url);
|
|
1047
|
+
if (!authorizationUrl) {
|
|
1223
1048
|
return this.fail(
|
|
1224
|
-
"
|
|
1225
|
-
"
|
|
1049
|
+
"INVALID_REDIRECT_AUTHORIZATION",
|
|
1050
|
+
"The payment provider returned an invalid authorization URL.",
|
|
1226
1051
|
false
|
|
1227
1052
|
);
|
|
1228
1053
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
"Card payment was cancelled before completion.",
|
|
1232
|
-
true
|
|
1233
|
-
);
|
|
1234
|
-
}
|
|
1235
|
-
paymentReference = popupResult.reference || paymentReference;
|
|
1236
|
-
break;
|
|
1237
|
-
}
|
|
1238
|
-
case NEXT_ACTION_REDIRECT: {
|
|
1239
|
-
if (typeof window !== "undefined" && this.returnUrl) {
|
|
1240
|
-
window.location.assign(action.authorization_url);
|
|
1241
|
-
return this.fail(
|
|
1242
|
-
"REDIRECT_REQUIRED",
|
|
1243
|
-
"Redirecting to complete payment authorization.",
|
|
1244
|
-
true
|
|
1245
|
-
);
|
|
1246
|
-
}
|
|
1247
|
-
if (typeof window !== "undefined") {
|
|
1248
|
-
const popup = window.open(
|
|
1249
|
-
action.authorization_url,
|
|
1250
|
-
"cimplify-auth",
|
|
1251
|
-
"width=520,height=760,noopener,noreferrer"
|
|
1252
|
-
);
|
|
1253
|
-
if (!popup) {
|
|
1054
|
+
if (typeof window !== "undefined" && this.returnUrl) {
|
|
1055
|
+
window.location.assign(authorizationUrl);
|
|
1254
1056
|
return this.fail(
|
|
1255
|
-
"
|
|
1256
|
-
"
|
|
1057
|
+
"REDIRECT_REQUIRED",
|
|
1058
|
+
"Redirecting to complete payment authorization.",
|
|
1257
1059
|
true
|
|
1258
1060
|
);
|
|
1259
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;
|
|
1260
1077
|
}
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1078
|
+
case NEXT_ACTION_AUTHORIZATION: {
|
|
1079
|
+
const limitFailure = this.consumeAuthorizationStep(resolutionState);
|
|
1080
|
+
if (limitFailure) return limitFailure;
|
|
1081
|
+
const authType = normalizeAuthorizationType(action.authorization_type);
|
|
1082
|
+
const authorization = await this.handleAuthorization({
|
|
1083
|
+
authorizationType: authType,
|
|
1084
|
+
paymentId: latestCheckoutResult.payment_id,
|
|
1085
|
+
displayText: action.display_text,
|
|
1086
|
+
provider: latestCheckoutResult.provider,
|
|
1087
|
+
orderId: latestCheckoutResult.order_id,
|
|
1088
|
+
orderNumber: latestCheckoutResult.order_number
|
|
1089
|
+
});
|
|
1090
|
+
if (!authorization.ok) {
|
|
1091
|
+
return authorization.result;
|
|
1092
|
+
}
|
|
1093
|
+
if (!authorization.value) {
|
|
1094
|
+
break actionLoop;
|
|
1095
|
+
}
|
|
1275
1096
|
latestCheckoutResult = authorization.value;
|
|
1276
1097
|
paymentReference = authorization.value.payment_reference || paymentReference;
|
|
1277
1098
|
if (this.isSuccessfulStatus(authorization.value.payment_status)) {
|
|
@@ -1284,24 +1105,45 @@ var CheckoutResolver = class {
|
|
|
1284
1105
|
false
|
|
1285
1106
|
);
|
|
1286
1107
|
}
|
|
1108
|
+
action = authorization.value.next_action ?? this.deriveLegacyNextAction(authorization.value);
|
|
1109
|
+
continue actionLoop;
|
|
1287
1110
|
}
|
|
1288
|
-
|
|
1111
|
+
case NEXT_ACTION_POLL:
|
|
1112
|
+
break actionLoop;
|
|
1113
|
+
default:
|
|
1114
|
+
return this.fail(
|
|
1115
|
+
"UNSUPPORTED_NEXT_ACTION",
|
|
1116
|
+
"The payment provider requested an unsupported authorization action.",
|
|
1117
|
+
false
|
|
1118
|
+
);
|
|
1289
1119
|
}
|
|
1290
1120
|
}
|
|
1291
1121
|
return this.pollUntilTerminal({
|
|
1292
|
-
orderId:
|
|
1122
|
+
orderId: latestCheckoutResult.order_id,
|
|
1293
1123
|
latestCheckoutResult,
|
|
1294
1124
|
paymentReference,
|
|
1295
|
-
|
|
1125
|
+
resolutionState
|
|
1296
1126
|
});
|
|
1297
1127
|
}
|
|
1128
|
+
consumeAuthorizationStep(resolutionState) {
|
|
1129
|
+
if (resolutionState.authorizationSteps >= MAX_PAYMENT_AUTHORIZATION_STEPS) {
|
|
1130
|
+
return this.fail(
|
|
1131
|
+
"AUTHORIZATION_LIMIT_EXCEEDED",
|
|
1132
|
+
"The payment provider requested too many authorization steps.",
|
|
1133
|
+
false
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
resolutionState.authorizationSteps += 1;
|
|
1137
|
+
return void 0;
|
|
1138
|
+
}
|
|
1298
1139
|
async pollUntilTerminal(input) {
|
|
1299
|
-
|
|
1300
|
-
let latestCheckoutResult = input.latestCheckoutResult;
|
|
1140
|
+
const latestCheckoutResult = input.latestCheckoutResult;
|
|
1301
1141
|
let paymentReference = input.paymentReference;
|
|
1302
|
-
|
|
1303
|
-
|
|
1142
|
+
const { resolutionState } = input;
|
|
1143
|
+
while (resolutionState.pollAttempts < this.maxPollAttempts) {
|
|
1304
1144
|
this.ensureNotAborted();
|
|
1145
|
+
const attempt = resolutionState.pollAttempts;
|
|
1146
|
+
resolutionState.pollAttempts += 1;
|
|
1305
1147
|
this.emit("polling", {
|
|
1306
1148
|
poll_attempt: attempt,
|
|
1307
1149
|
max_poll_attempts: this.maxPollAttempts,
|
|
@@ -1310,8 +1152,8 @@ var CheckoutResolver = class {
|
|
|
1310
1152
|
});
|
|
1311
1153
|
const statusResult = await this.client.checkout.pollPaymentStatus(input.orderId);
|
|
1312
1154
|
if (!statusResult.ok) {
|
|
1313
|
-
|
|
1314
|
-
if (
|
|
1155
|
+
resolutionState.consecutivePollErrors += 1;
|
|
1156
|
+
if (resolutionState.consecutivePollErrors >= MAX_CONSECUTIVE_NETWORK_ERRORS) {
|
|
1315
1157
|
return this.fail(
|
|
1316
1158
|
"NETWORK_ERROR",
|
|
1317
1159
|
"Unable to confirm payment due to repeated network errors.",
|
|
@@ -1321,13 +1163,17 @@ var CheckoutResolver = class {
|
|
|
1321
1163
|
await this.wait(this.pollIntervalMs);
|
|
1322
1164
|
continue;
|
|
1323
1165
|
}
|
|
1324
|
-
|
|
1166
|
+
resolutionState.consecutivePollErrors = 0;
|
|
1325
1167
|
if (statusResult.value.reference) {
|
|
1326
1168
|
paymentReference = statusResult.value.reference;
|
|
1327
1169
|
}
|
|
1328
1170
|
const normalized = normalizeStatusResponse(statusResult.value);
|
|
1329
1171
|
if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
|
|
1330
|
-
return this.finalizeSuccess(
|
|
1172
|
+
return this.finalizeSuccess({
|
|
1173
|
+
...latestCheckoutResult,
|
|
1174
|
+
payment_status: normalized.status,
|
|
1175
|
+
payment_reference: paymentReference || latestCheckoutResult.payment_reference
|
|
1176
|
+
});
|
|
1331
1177
|
}
|
|
1332
1178
|
if (isPaymentStatusFailure(normalized.status)) {
|
|
1333
1179
|
return this.fail(
|
|
@@ -1337,43 +1183,32 @@ var CheckoutResolver = class {
|
|
|
1337
1183
|
);
|
|
1338
1184
|
}
|
|
1339
1185
|
if (isPaymentStatusRequiresAction(normalized.status)) {
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
}
|
|
1349
|
-
if (authorization.value) {
|
|
1350
|
-
latestCheckoutResult = authorization.value;
|
|
1351
|
-
paymentReference = authorization.value.payment_reference || paymentReference;
|
|
1352
|
-
authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
|
|
1353
|
-
if (this.isSuccessfulStatus(authorization.value.payment_status)) {
|
|
1354
|
-
return this.finalizeSuccess(authorization.value);
|
|
1355
|
-
}
|
|
1356
|
-
if (this.isFailureStatus(authorization.value.payment_status)) {
|
|
1357
|
-
return this.fail(
|
|
1358
|
-
"AUTHORIZATION_FAILED",
|
|
1359
|
-
authorization.value.display_text || "Payment authorization failed.",
|
|
1360
|
-
false
|
|
1361
|
-
);
|
|
1362
|
-
}
|
|
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
|
+
);
|
|
1363
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
|
+
);
|
|
1364
1209
|
}
|
|
1365
1210
|
await this.wait(this.pollIntervalMs);
|
|
1366
1211
|
}
|
|
1367
|
-
try {
|
|
1368
|
-
const finalResult = await this.client.checkout.pollPaymentStatus(input.orderId);
|
|
1369
|
-
if (finalResult.ok) {
|
|
1370
|
-
const normalized = normalizeStatusResponse(finalResult.value);
|
|
1371
|
-
if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
|
|
1372
|
-
return this.finalizeSuccess(latestCheckoutResult);
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
} catch {
|
|
1376
|
-
}
|
|
1377
1212
|
return this.fail(
|
|
1378
1213
|
"PAYMENT_TIMEOUT",
|
|
1379
1214
|
"Payment confirmation timed out. Please retry checkout.",
|
|
@@ -1382,35 +1217,61 @@ var CheckoutResolver = class {
|
|
|
1382
1217
|
}
|
|
1383
1218
|
async handleAuthorization(input) {
|
|
1384
1219
|
const authorizationType = input.authorizationType;
|
|
1385
|
-
const
|
|
1386
|
-
if (!authorizationType || !
|
|
1387
|
-
return {
|
|
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
|
+
};
|
|
1388
1230
|
}
|
|
1389
1231
|
this.emit("awaiting_authorization", {
|
|
1390
1232
|
authorization_type: authorizationType,
|
|
1391
1233
|
display_text: input.displayText,
|
|
1392
|
-
provider: input.provider
|
|
1234
|
+
provider: input.provider,
|
|
1235
|
+
order_id: input.orderId,
|
|
1236
|
+
order_number: input.orderNumber
|
|
1393
1237
|
});
|
|
1394
|
-
let
|
|
1395
|
-
const submit =
|
|
1238
|
+
let authorizationRequest;
|
|
1239
|
+
const submit = (authorization) => {
|
|
1396
1240
|
this.ensureNotAborted();
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1241
|
+
let normalizedAuthorization;
|
|
1242
|
+
try {
|
|
1243
|
+
normalizedAuthorization = normalizeAuthorizationSubmission(
|
|
1244
|
+
authorizationType,
|
|
1245
|
+
authorization
|
|
1246
|
+
);
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
return Promise.reject(error);
|
|
1400
1249
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
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
|
+
});
|
|
1408
1266
|
}
|
|
1409
|
-
|
|
1267
|
+
return authorizationRequest.then(() => void 0);
|
|
1410
1268
|
};
|
|
1411
1269
|
try {
|
|
1412
1270
|
if (this.onAuthorizationRequired) {
|
|
1413
|
-
await
|
|
1271
|
+
await waitForAbortable(
|
|
1272
|
+
Promise.resolve(this.onAuthorizationRequired(authorizationType, submit)),
|
|
1273
|
+
this.signal
|
|
1274
|
+
);
|
|
1414
1275
|
} else {
|
|
1415
1276
|
return {
|
|
1416
1277
|
ok: false,
|
|
@@ -1421,6 +1282,20 @@ var CheckoutResolver = class {
|
|
|
1421
1282
|
)
|
|
1422
1283
|
};
|
|
1423
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
|
+
};
|
|
1424
1299
|
} catch (error) {
|
|
1425
1300
|
if (isAbortError(error)) {
|
|
1426
1301
|
return {
|
|
@@ -1434,12 +1309,12 @@ var CheckoutResolver = class {
|
|
|
1434
1309
|
result: this.fail("AUTHORIZATION_FAILED", message, true)
|
|
1435
1310
|
};
|
|
1436
1311
|
}
|
|
1437
|
-
return {
|
|
1438
|
-
ok: true,
|
|
1439
|
-
value: authorizationResult
|
|
1440
|
-
};
|
|
1441
1312
|
}
|
|
1442
1313
|
async finalizeSuccess(checkoutResult) {
|
|
1314
|
+
const currency = this.getOrderCurrency(checkoutResult);
|
|
1315
|
+
if (!currency) {
|
|
1316
|
+
return this.fail("INVALID_CURRENCY", "Payment currency is missing or unsupported.", false);
|
|
1317
|
+
}
|
|
1443
1318
|
this.emit("finalizing", {
|
|
1444
1319
|
order_id: checkoutResult.order_id,
|
|
1445
1320
|
order_number: checkoutResult.order_number
|
|
@@ -1456,7 +1331,7 @@ var CheckoutResolver = class {
|
|
|
1456
1331
|
order_number: checkoutResult.order_number || checkoutResult.order_id,
|
|
1457
1332
|
status: checkoutResult.payment_status || "paid",
|
|
1458
1333
|
total: this.getOrderTotal(checkoutResult),
|
|
1459
|
-
currency
|
|
1334
|
+
currency
|
|
1460
1335
|
},
|
|
1461
1336
|
enrolled_in_link: enrolledInLink
|
|
1462
1337
|
};
|
|
@@ -1508,7 +1383,7 @@ var CheckoutResolver = class {
|
|
|
1508
1383
|
};
|
|
1509
1384
|
}
|
|
1510
1385
|
getEnrollmentMobileMoney() {
|
|
1511
|
-
if (this.paymentData?.type ===
|
|
1386
|
+
if (this.paymentData?.type === PAYMENT_METHOD.MOBILE_MONEY && this.paymentData.phone_number && this.paymentData.provider && isValidMobileMoneyProvider(this.paymentData.provider)) {
|
|
1512
1387
|
return {
|
|
1513
1388
|
phone_number: this.paymentData.phone_number,
|
|
1514
1389
|
provider: this.paymentData.provider,
|
|
@@ -1535,15 +1410,15 @@ var CheckoutResolver = class {
|
|
|
1535
1410
|
}
|
|
1536
1411
|
getOrderCurrency(checkoutResult) {
|
|
1537
1412
|
if (checkoutResult.fx?.pay_currency) {
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
if (this.cartCurrency) {
|
|
1541
|
-
return this.cartCurrency;
|
|
1413
|
+
const currency2 = checkoutResult.fx.pay_currency.trim().toUpperCase();
|
|
1414
|
+
return isSupportedCurrency(currency2) ? currency2 : void 0;
|
|
1542
1415
|
}
|
|
1543
1416
|
if (this.checkoutData.pay_currency) {
|
|
1544
|
-
|
|
1417
|
+
const currency2 = this.checkoutData.pay_currency.trim().toUpperCase();
|
|
1418
|
+
return isSupportedCurrency(currency2) ? currency2 : void 0;
|
|
1545
1419
|
}
|
|
1546
|
-
|
|
1420
|
+
const currency = this.cartCurrency.trim().toUpperCase();
|
|
1421
|
+
return isSupportedCurrency(currency) ? currency : void 0;
|
|
1547
1422
|
}
|
|
1548
1423
|
emit(status, context = {}) {
|
|
1549
1424
|
if (!this.onStatusChange) {
|
|
@@ -1554,6 +1429,12 @@ var CheckoutResolver = class {
|
|
|
1554
1429
|
} catch {
|
|
1555
1430
|
}
|
|
1556
1431
|
}
|
|
1432
|
+
notifyAuthorizationUiChange(open) {
|
|
1433
|
+
try {
|
|
1434
|
+
this.onAuthorizationUiChange?.(open);
|
|
1435
|
+
} catch {
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1557
1438
|
fail(code, message, recoverable) {
|
|
1558
1439
|
this.emit("failed", {
|
|
1559
1440
|
display_text: message
|
|
@@ -1618,6 +1499,43 @@ function isValidCustomerName(name) {
|
|
|
1618
1499
|
function isOfflinePaymentMethod(method) {
|
|
1619
1500
|
return method === "cash" || method === "offline";
|
|
1620
1501
|
}
|
|
1502
|
+
function notifyCheckoutStatus(observer, status, context = {}) {
|
|
1503
|
+
try {
|
|
1504
|
+
observer?.(status, context);
|
|
1505
|
+
} catch {
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
var MAX_STOREFRONT_COLLECTION_OPTIONS = 32;
|
|
1509
|
+
function normalizeCollectionOptions(value, expectedCurrency) {
|
|
1510
|
+
if (!value || typeof value !== "object") return null;
|
|
1511
|
+
const response = value;
|
|
1512
|
+
if (response.presentment_currency !== expectedCurrency || !Array.isArray(response.options)) {
|
|
1513
|
+
return null;
|
|
1514
|
+
}
|
|
1515
|
+
if (response.options.length > MAX_STOREFRONT_COLLECTION_OPTIONS) return null;
|
|
1516
|
+
const options = [];
|
|
1517
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1518
|
+
for (const candidate of response.options) {
|
|
1519
|
+
if (!candidate || typeof candidate !== "object") continue;
|
|
1520
|
+
const option = candidate;
|
|
1521
|
+
if (option.method === PAYMENT_METHOD.CARD && option.network == null) {
|
|
1522
|
+
if (!seen.has(PAYMENT_METHOD.CARD)) {
|
|
1523
|
+
seen.add(PAYMENT_METHOD.CARD);
|
|
1524
|
+
options.push({ method: PAYMENT_METHOD.CARD, network: null });
|
|
1525
|
+
}
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1528
|
+
if (option.method !== PAYMENT_METHOD.MOBILE_MONEY || typeof option.network !== "string" || !option.network.trim()) {
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1531
|
+
const network = option.network.trim().toLowerCase();
|
|
1532
|
+
const key = `${PAYMENT_METHOD.MOBILE_MONEY}:${network}`;
|
|
1533
|
+
if (seen.has(key)) continue;
|
|
1534
|
+
seen.add(key);
|
|
1535
|
+
options.push({ method: PAYMENT_METHOD.MOBILE_MONEY, network });
|
|
1536
|
+
}
|
|
1537
|
+
return { presentment_currency: expectedCurrency, options };
|
|
1538
|
+
}
|
|
1621
1539
|
var CheckoutService = class {
|
|
1622
1540
|
constructor(client) {
|
|
1623
1541
|
this.client = client;
|
|
@@ -1643,6 +1561,40 @@ var CheckoutService = class {
|
|
|
1643
1561
|
const token = this.client.getOrderToken(orderId);
|
|
1644
1562
|
return token ? `?token=${encodeURIComponent(token)}` : "";
|
|
1645
1563
|
}
|
|
1564
|
+
/** Available storefront collection methods for one presentment currency. */
|
|
1565
|
+
async collectionOptions(currency) {
|
|
1566
|
+
const normalized = currency.trim().toUpperCase();
|
|
1567
|
+
if (!isSupportedCurrency(normalized)) {
|
|
1568
|
+
return err(
|
|
1569
|
+
new CimplifyError(
|
|
1570
|
+
ErrorCode.UNSUPPORTED_CURRENCY,
|
|
1571
|
+
"No online payment method is available for this currency.",
|
|
1572
|
+
false
|
|
1573
|
+
)
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
const response = await safe(
|
|
1577
|
+
this.client.get(
|
|
1578
|
+
`/api/v1/payments/collection-options?currency=${encodeURIComponent(normalized)}`
|
|
1579
|
+
)
|
|
1580
|
+
);
|
|
1581
|
+
if (!response.ok) return response;
|
|
1582
|
+
const options = normalizeCollectionOptions(response.value, normalized);
|
|
1583
|
+
return options ? ok(options) : err(
|
|
1584
|
+
new CimplifyError(
|
|
1585
|
+
ErrorCode.PAYMENT_CAPABILITY_MISMATCH,
|
|
1586
|
+
"Payment methods could not be verified for this currency.",
|
|
1587
|
+
false
|
|
1588
|
+
)
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Run provider-owned UI for a backend `card_popup` action. A submitted
|
|
1593
|
+
* outcome is not proof of payment; callers must poll/verify with Cimplify.
|
|
1594
|
+
*/
|
|
1595
|
+
authorizeCardPopup(action, options) {
|
|
1596
|
+
return this.client.authorizationAdapters.authorize(action, options);
|
|
1597
|
+
}
|
|
1646
1598
|
async process(data) {
|
|
1647
1599
|
const invalid = this.validateCheckoutData(data);
|
|
1648
1600
|
if (invalid) {
|
|
@@ -1686,12 +1638,18 @@ var CheckoutService = class {
|
|
|
1686
1638
|
async pollPaymentStatus(orderId) {
|
|
1687
1639
|
const encodedId = encodeURIComponent(orderId);
|
|
1688
1640
|
const tokenParam = this.orderTokenParam(orderId);
|
|
1689
|
-
return safe(
|
|
1641
|
+
return safe(
|
|
1642
|
+
this.client.get(
|
|
1643
|
+
`/api/v1/orders/${encodedId}/payment-status${tokenParam}`
|
|
1644
|
+
)
|
|
1645
|
+
);
|
|
1690
1646
|
}
|
|
1691
1647
|
async updateOrderCustomer(orderId, customer) {
|
|
1692
1648
|
const encodedId = encodeURIComponent(orderId);
|
|
1693
1649
|
const tokenParam = this.orderTokenParam(orderId);
|
|
1694
|
-
return safe(
|
|
1650
|
+
return safe(
|
|
1651
|
+
this.client.post(`/api/v1/orders/${encodedId}/customer${tokenParam}`, customer)
|
|
1652
|
+
);
|
|
1695
1653
|
}
|
|
1696
1654
|
async verifyPayment(orderId) {
|
|
1697
1655
|
const encodedId = encodeURIComponent(orderId);
|
|
@@ -1699,7 +1657,7 @@ var CheckoutService = class {
|
|
|
1699
1657
|
return safe(this.client.post(`/api/v1/orders/${encodedId}/verify-payment${tokenParam}`));
|
|
1700
1658
|
}
|
|
1701
1659
|
async processAndResolve(data) {
|
|
1702
|
-
data.on_status_change
|
|
1660
|
+
notifyCheckoutStatus(data.on_status_change, "preparing");
|
|
1703
1661
|
if (!data.cart_id) {
|
|
1704
1662
|
return ok(
|
|
1705
1663
|
toTerminalFailure(
|
|
@@ -1711,13 +1669,7 @@ var CheckoutService = class {
|
|
|
1711
1669
|
}
|
|
1712
1670
|
const cartResult = await this.client.cart.get();
|
|
1713
1671
|
if (!cartResult.ok || !cartResult.value?.id) {
|
|
1714
|
-
return ok(
|
|
1715
|
-
toTerminalFailure(
|
|
1716
|
-
"INVALID_CART",
|
|
1717
|
-
"Unable to load cart for checkout.",
|
|
1718
|
-
false
|
|
1719
|
-
)
|
|
1720
|
-
);
|
|
1672
|
+
return ok(toTerminalFailure("INVALID_CART", "Unable to load cart for checkout.", false));
|
|
1721
1673
|
}
|
|
1722
1674
|
const cart = cartResult.value;
|
|
1723
1675
|
if (cart.id !== data.cart_id || cart.items.length === 0) {
|
|
@@ -1749,11 +1701,28 @@ var CheckoutService = class {
|
|
|
1749
1701
|
voucher_code: data.voucher_code,
|
|
1750
1702
|
pay_deposit: data.pay_deposit
|
|
1751
1703
|
};
|
|
1752
|
-
const
|
|
1753
|
-
|
|
1754
|
-
|
|
1704
|
+
const rawBaseCurrency = typeof cart.pricing.currency === "string" ? cart.pricing.currency.trim().toUpperCase() : "";
|
|
1705
|
+
if (!isSupportedCurrency(rawBaseCurrency)) {
|
|
1706
|
+
return ok(
|
|
1707
|
+
toTerminalFailure(
|
|
1708
|
+
"INVALID_CURRENCY",
|
|
1709
|
+
"Cart currency is missing or unsupported. Refresh the cart before checkout.",
|
|
1710
|
+
false
|
|
1711
|
+
)
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
const baseCurrency = rawBaseCurrency;
|
|
1755
1715
|
const payCurrency = data.pay_currency?.trim().toUpperCase();
|
|
1756
|
-
|
|
1716
|
+
if (payCurrency && !isSupportedCurrency(payCurrency)) {
|
|
1717
|
+
return ok(
|
|
1718
|
+
toTerminalFailure(
|
|
1719
|
+
"INVALID_CURRENCY",
|
|
1720
|
+
"Payment currency is unsupported for checkout.",
|
|
1721
|
+
false
|
|
1722
|
+
)
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
const payCurrencyCode = payCurrency && isSupportedCurrency(payCurrency) ? payCurrency : void 0;
|
|
1757
1726
|
const cartTotalAmount = typeof cart.pricing.total_price === "number" ? cart.pricing.total_price : Number.parseFloat(cart.pricing.total_price || "0");
|
|
1758
1727
|
if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
|
|
1759
1728
|
const fxQuoteResult = await this.client.fx.lockQuote({
|
|
@@ -1773,7 +1742,7 @@ var CheckoutService = class {
|
|
|
1773
1742
|
checkoutData.pay_currency = payCurrencyCode;
|
|
1774
1743
|
checkoutData.fx_quote_id = fxQuoteResult.value.id;
|
|
1775
1744
|
}
|
|
1776
|
-
data.on_status_change
|
|
1745
|
+
notifyCheckoutStatus(data.on_status_change, "processing");
|
|
1777
1746
|
const processResult = await this.process(checkoutData);
|
|
1778
1747
|
if (!processResult.ok) {
|
|
1779
1748
|
return ok(
|
|
@@ -1790,12 +1759,13 @@ var CheckoutService = class {
|
|
|
1790
1759
|
pollIntervalMs: data.poll_interval_ms,
|
|
1791
1760
|
maxPollAttempts: data.max_poll_attempts,
|
|
1792
1761
|
onStatusChange: data.on_status_change,
|
|
1762
|
+
onAuthorizationUiChange: data.on_authorization_ui_change,
|
|
1793
1763
|
onAuthorizationRequired: data.on_authorization_required,
|
|
1794
1764
|
signal: data.signal,
|
|
1795
1765
|
returnUrl: data.return_url,
|
|
1796
1766
|
enrollInLink: data.enroll_in_link,
|
|
1797
1767
|
cartTotal: cart.pricing.total_price,
|
|
1798
|
-
cartCurrency: checkoutData.pay_currency ||
|
|
1768
|
+
cartCurrency: checkoutData.pay_currency || baseCurrency,
|
|
1799
1769
|
orderType: checkoutData.order_type,
|
|
1800
1770
|
allowPopups: data.allow_popups
|
|
1801
1771
|
});
|
|
@@ -3643,4 +3613,4 @@ function createElements(client, businessId, options) {
|
|
|
3643
3613
|
return new CimplifyElements(client, businessId, options);
|
|
3644
3614
|
}
|
|
3645
3615
|
|
|
3646
|
-
export {
|
|
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 };
|