@comgate/checkout-js
Version:
Loading wrapper for Comgate Checkout library.
681 lines (680 loc) • 22.9 kB
JavaScript
/*
* @comgate/checkout-js v2.0.11 (https://www.npmjs.com/package/@comgate/checkout-js)
* Copyright 2025 - Comgate a.s.
* Licensed under EULA (https://www.unpkg.com/@comgate/checkout-js@2.0.11/LICENSE)
*/
const VERSION_DEV = "dev";
const VERSION_1 = "1";
const VERSION_2 = "2";
const ENVIRONMENT_DEV = "dev";
const ENVIRONMENT_TEST = "test";
const ENVIRONMENT_PROD = "prod";
const ENVIRONMENT_PREFERRED = ENVIRONMENT_PROD;
const ENVIRONMENT_DEV_CDN_URL = "https://localhost:8787/sdk/";
const ENVIRONMENT_TEST_CDN_URL = "https://checkout.comgate-test.cz/sdk/";
const ENVIRONMENT_PROD_CDN_URL = "https://checkout.comgate.cz/sdk/";
const ENVIRONMENT_DEV_API_URL = "https://payments_web/";
const ENVIRONMENT_TEST_API_URL = "https://payments.comgate-test.cz/";
const ENVIRONMENT_PROD_API_URL = "https://payments.comgate.cz/";
const TIMEOUT_LOAD_DEFAULT = 1e4;
const TIMEOUT_LOAD_MIN = 500;
const TIMEOUT_LOAD_MAX = 3e4;
const TIMEOUT_FETCH_DEFAULT = 5e3;
const TIMEOUT_FETCH_MIN = 500;
const TIMEOUT_FETCH_MAX = 15e3;
const checkoutEnvironments = [ENVIRONMENT_DEV, ENVIRONMENT_TEST, ENVIRONMENT_PROD];
const VERSION_PREFERRED = VERSION_2;
const checkoutVersions = [VERSION_1, VERSION_2, VERSION_DEV];
const checkoutIdRegex = /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i;
const versionRegex = new RegExp(`^([12]\.[0-9]+\.[0-9]+|${checkoutVersions.join("|")})$`, "i");
const semverRegex = new RegExp(`^([12]\.[0-9]+\.[0-9]+)$`, "i");
const MODULE_LOADER = "loader";
const MODULE_CORE = "core";
const FILE_MODULE_CORE = "checkout-core.min.js";
const moduleCore = {
boundName: "comgateCheckoutCore",
name: MODULE_CORE,
events: {
ready: "ComgateCheckoutCoreLoaded"
},
mountPoint: ["comgate", "checkout", "core"],
file: FILE_MODULE_CORE
};
const MODULE_APPLEPAY = "applepay";
const FILE_MODULE_APPLEPAY = "checkout-apple-pay.min.js";
const moduleApplePay = {
boundName: "comgateCheckoutApplePay",
name: MODULE_APPLEPAY,
events: {
ready: "ComgateCheckoutApplePayLoaded"
},
mountPoint: ["comgate", "checkout", "applepay"],
file: FILE_MODULE_APPLEPAY
};
const MODULE_GOOGLEPAY = "googlepay";
const FILE_MODULE_GOOGLEPAY = "checkout-google-pay.min.js";
const moduleGooglePay = {
boundName: "comgateCheckoutGooglePay",
name: MODULE_GOOGLEPAY,
events: {
ready: "ComgateCheckoutGooglePayLoaded"
},
mountPoint: ["comgate", "checkout", "googlepay"],
file: FILE_MODULE_GOOGLEPAY
};
const EVENT_MODULES_LOADED = "comgate-checkout-js-module-loaded";
const MODULE_LOAD_SUCCESSFULLY = "true";
const MODULE_LOAD_FAILED = "false";
const createPreloadLinkElement = (preloadId, version2, baseUrl, file) => {
const preload2 = document.createElement("link");
preload2.id = preloadId;
preload2.rel = "preload";
preload2.as = "script";
preload2.href = resolveFileUrl(baseUrl, version2, file);
return preload2;
};
const createScriptElement = (loadId, version2, isAsync = true, isDeferred = false, baseUrl, file) => {
const script = document.createElement("script");
script.id = loadId;
script.src = resolveFileUrl(baseUrl, version2, file);
script.async = isAsync;
script.defer = isDeferred;
return script;
};
const setElementData = (element, name, value) => {
try {
if (element && element instanceof HTMLElement && "dataset" in element) {
element.dataset[name] = value;
return true;
}
} catch (error) {
}
return false;
};
const getElementData = (element, name) => {
try {
if (element && element instanceof HTMLElement && "dataset" in element) {
return element.dataset[name];
}
} catch (error) {
}
return void 0;
};
const doesModuleAlreadyExist = (moduleConfig) => {
return moduleConfig.boundName in window;
};
const getModuleMountObject = (moduleConfig) => {
const moduleObject = moduleConfig.mountPoint.reduce((accumulator, currentValue) => accumulator == null ? void 0 : accumulator[currentValue], window);
if (validateModuleObject(moduleObject)) {
return moduleObject;
}
return void 0;
};
const validateModuleObject = (object) => object && typeof object == "object" && "create" in object && typeof object.create == "function" && "version" in object && typeof object.version == "function";
const resolveDebug = (config) => {
return config.debug === true;
};
const resolveEnvironment = (config) => {
return ("environment" in config ? config.environment : ENVIRONMENT_PREFERRED) || ENVIRONMENT_PREFERRED;
};
const resolveFileUrl = (cdnBaseUrl, version2, file) => {
return `${cdnBaseUrl + version2.version}${resolveFileName(version2, file)}`;
};
const resolveFileName = (version2, file) => {
if (file && file.length > 0) {
return "/" + expandFileName(file, version2.hash);
}
return "";
};
const expandFileName = (file, hash) => {
if (hash) {
const lastDot = file.lastIndexOf(".");
if (lastDot >= 0) {
const name = file.slice(0, lastDot);
const ext = file.slice(lastDot);
return `${name}.${hash}${ext}`;
}
}
return file;
};
const resolveUrl = (config, type) => {
const env = resolveEnvironment(config);
if (type === "cdn") {
return {
[ENVIRONMENT_DEV]: ENVIRONMENT_DEV_CDN_URL,
[ENVIRONMENT_TEST]: ENVIRONMENT_TEST_CDN_URL,
[ENVIRONMENT_PROD]: ENVIRONMENT_PROD_CDN_URL
}[env];
}
return {
[ENVIRONMENT_DEV]: ENVIRONMENT_DEV_API_URL,
[ENVIRONMENT_TEST]: ENVIRONMENT_TEST_API_URL,
[ENVIRONMENT_PROD]: ENVIRONMENT_PROD_API_URL
}[env];
};
let serverVersions;
const semverPrefix = "v/";
const majorPrefix = "@";
const resolveVersion = async (config, cdnBaseUrl) => {
try {
if (!serverVersions) {
await safeFetch(cdnBaseUrl + "versions.json").then((res) => {
if (res.ok && res.isJson) {
serverVersions = res.data;
} else if (resolveDebug(config)) {
console.warn("Checkout SDK: can't fetch versions.json, using cdn fallback.", { details: res == null ? void 0 : res.error });
}
}).catch((error) => {
if (resolveDebug(config)) {
console.warn("Checkout SDK: can't fetch versions.json, using cdn fallback.", { details: error });
}
});
}
return getExpandedVersion(config.version, serverVersions);
} catch (error) {
return {
version: majorPrefix + VERSION_PREFERRED
};
}
};
function getExpandedVersion(custom, server) {
if (!server) {
if (custom && semverRegex.test(custom)) {
return { version: semverPrefix + custom };
}
if (custom && checkoutVersions.includes(custom)) {
return { version: majorPrefix + custom };
}
return { version: majorPrefix + VERSION_PREFERRED };
}
if (custom && semverRegex.test(custom)) {
const major = custom.split(".")[0];
if (server.majors[major]) {
return {
hash: server.majors[major].hash,
version: semverPrefix + custom
};
}
}
if (custom && custom in server.majors) {
return {
hash: server.majors[custom].hash,
version: semverPrefix + server.majors[custom].latest
};
}
if (VERSION_PREFERRED in server.majors) {
return {
hash: server.majors[VERSION_PREFERRED].hash,
version: semverPrefix + server.majors[VERSION_PREFERRED].latest
};
}
return {
version: majorPrefix + VERSION_PREFERRED
};
}
async function safeFetch(url, options = {}, timeout = 1e4) {
const timeoutMs = validateNumber(timeout, TIMEOUT_FETCH_MIN, TIMEOUT_FETCH_MAX, TIMEOUT_FETCH_DEFAULT);
let controller;
let timer;
if (AbortSignal !== void 0 && typeof AbortSignal.timeout === "function") {
options.signal = AbortSignal.timeout(timeoutMs);
} else if (AbortController !== void 0) {
controller = new AbortController();
timer = setTimeout(() => controller.abort(), timeoutMs);
options.signal = controller.signal;
}
try {
const response = await fetch(url, options);
if (timer) {
clearTimeout(timer);
}
const headers = headersToObject(response.headers);
if (!response.ok) {
let rawText2 = null;
try {
rawText2 = await response.text();
} catch {
}
return {
status: response.status,
ok: false,
isJson: false,
headers,
data: null,
raw: () => rawText2,
error: `HTTP error: ${response.status} ${response.statusText}`.trim()
};
}
const rawText = await response.text();
try {
const data = JSON.parse(rawText);
return {
status: response.status,
ok: true,
isJson: true,
headers,
data,
raw: () => rawText
};
} catch {
return {
status: response.status,
ok: true,
isJson: false,
headers,
data: null,
raw: () => rawText,
error: "Response is not valid JSON."
};
}
} catch (error_) {
if (timer)
clearTimeout(timer);
return {
status: 0,
ok: false,
isJson: false,
headers: {},
data: null,
raw: () => null,
error: (error_ == null ? void 0 : error_.name) === "AbortError" ? "Request timed out." : `Unexpected error: ${(error_ == null ? void 0 : error_.message) || String(error_)}`
};
}
}
function headersToObject(headers) {
const result = {};
headers.forEach((value, key) => {
result[key] = value;
});
return result;
}
function validateNumber(value, min, max, defaultValue) {
if (value && typeof value === "number") {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
return defaultValue;
}
function baseConfigValidator(config) {
if (!window) {
return "Window object is not available. Please ensure you are running this in a valid browser environment.";
}
if (typeof window !== "object") {
return "Invalid window type. An object is expected. Please ensure you are running this in a valid browser environment.";
}
if (!config || typeof config !== "object") {
return "Config: Invalid type. An object is expected.";
}
if (config.debug !== void 0 && typeof config.debug !== "boolean") {
return "Config: Invalid debug value. Boolean expected.";
}
if (!config.checkoutId || typeof config.checkoutId !== "string" || !checkoutIdRegex.test(config.checkoutId)) {
return "Config: Invalid checkoutId format. UUIDv4 expected.";
}
if ("timeout" in config && !Number.isInteger(config.timeout)) {
return "Config: Invalid timeout value. A number in milliseconds is expected.";
}
if ("version" in config && (!config.version || !versionRegex.test(config.version))) {
return `Config: Invalid version value '${config.version}'. Allowed values: ${versionRegex.toString()}`;
}
if (config.version && config.version === VERSION_1) {
return "Config: Please upgrade to the latest version. Version 1 is no longer supported.";
}
return true;
}
function preloadConfigValidator(config) {
const validResponse = baseConfigValidator(config);
if (validResponse !== true) {
return validResponse;
}
return true;
}
async function preloadComgateCheckout(config) {
const frozenConfig = Object.freeze(JSON.parse(JSON.stringify(config)));
const validationResponse = preloadConfigValidator(frozenConfig);
if (validationResponse !== true) {
return Promise.reject({
success: false,
checkoutId: frozenConfig == null ? void 0 : frozenConfig.checkoutId,
preloaded: [],
error: {
[MODULE_LOADER]: new TypeError(validationResponse)
}
});
}
const cdnBaseUrl = resolveUrl(frozenConfig, "cdn");
const resolvedVersion = await resolveVersion(frozenConfig, cdnBaseUrl);
return new Promise((resolve, reject) => {
const preloadResults = [];
if (frozenConfig.excludeCore !== true) {
preloadResults.push(preload({ config: frozenConfig, module: moduleCore, cdnBaseUrl, resolvedVersion }));
}
if (frozenConfig.modules !== void 0) {
if (frozenConfig.modules.includes(MODULE_APPLEPAY)) {
preloadResults.push(preload({ config: frozenConfig, module: moduleApplePay, cdnBaseUrl, resolvedVersion }));
}
if (frozenConfig.modules.includes(MODULE_GOOGLEPAY)) {
preloadResults.push(preload({ config: frozenConfig, module: moduleGooglePay, cdnBaseUrl, resolvedVersion }));
}
}
Promise.all(preloadResults).then((modulesStatus) => {
const preloadResponse = {
checkoutId: frozenConfig.checkoutId,
preloaded: [],
success: true
};
modulesStatus.forEach((module) => {
if (module.success) {
preloadResponse.preloaded.push(module.name);
} else {
preloadResponse.success = false;
preloadResponse.error = preloadResponse.error || {};
preloadResponse.error[module.name] = module.error;
}
});
if (frozenConfig.onPreload !== void 0 && typeof frozenConfig.onPreload === "function") {
frozenConfig.onPreload(preloadResponse);
}
if (preloadResponse.success) {
resolve(preloadResponse);
return;
}
reject(preloadResponse);
});
});
}
const preload = async (preloadConfig) => new Promise((resolve, reject) => {
let resolved = false;
let element = null;
try {
const preloadId = `cg-preload-checkout-${preloadConfig.module.name.toLowerCase()}`;
element = document.querySelector(`#${preloadId}`);
if (element && getElementData(element, "success") !== MODULE_LOAD_SUCCESSFULLY) {
element.remove();
element = null;
}
if (!element) {
const preload2 = createPreloadLinkElement(preloadId, preloadConfig.resolvedVersion, preloadConfig.cdnBaseUrl, preloadConfig.module.file);
preload2.addEventListener("error", () => {
setElementData(preload2, "success", MODULE_LOAD_FAILED);
});
document.head.append(preload2);
resolve({
success: true,
name: preloadConfig.module.name
});
resolved = true;
}
} catch (error) {
if (!resolved) {
setElementData(element, "success", MODULE_LOAD_FAILED);
reject({
success: false,
name: preloadConfig.module.name,
error: new Error("Unexpected error.", { cause: error })
});
}
}
});
function loadConfigValidator(config) {
const validResponse = baseConfigValidator(config);
if (validResponse !== true) {
return validResponse;
}
if ("environment" in config && (!config.environment || !checkoutEnvironments.includes(config.environment))) {
return `Config: Invalid environment value '${config.environment}'. Allowed values: ${checkoutEnvironments.join(", ")}`;
}
const isAsyncDefined = "async" in config;
const isDeferDefined = "defer" in config;
if (isAsyncDefined && typeof config.async !== "boolean") {
return "Config: Invalid async value. Boolean expected.";
}
if (isDeferDefined && typeof config.defer !== "boolean") {
return "Config: Invalid defer value. Boolean expected.";
}
if (config.async && config.defer) {
return "Config: Only one of async or defer can be true.";
}
return true;
}
async function loadComgateCheckout(config) {
var _a, _b;
const frozenConfig = Object.freeze(JSON.parse(JSON.stringify(config)));
const validationResponse = loadConfigValidator(frozenConfig);
if (validationResponse !== true) {
return Promise.reject({
success: false,
checkoutId: frozenConfig == null ? void 0 : frozenConfig.checkoutId,
error: {
[MODULE_LOADER]: new TypeError(validationResponse)
}
});
}
const env = resolveEnvironment(frozenConfig);
const cdnBaseUrl = resolveUrl(frozenConfig, "cdn");
const apiBaseUrl = resolveUrl(frozenConfig, "api");
(_a = window.comgate ?? (window.comgate = {})).checkout ?? (_a.checkout = {});
window.comgate.checkout.checkoutId = frozenConfig.checkoutId;
if (env !== ENVIRONMENT_PROD) {
(_b = window.comgate.checkout).config ?? (_b.config = {});
window.comgate.checkout.config.baseCdnUrl = cdnBaseUrl;
window.comgate.checkout.config.baseApiUrl = apiBaseUrl;
window.comgate.checkout.config.environment = env;
}
const resolvedVersion = await resolveVersion(frozenConfig, cdnBaseUrl);
return new Promise((resolve, reject) => {
let coreLoading = null;
if (doesModuleAlreadyExist(moduleCore)) {
coreLoading = Promise.resolve({
success: true,
name: moduleCore.name,
mount: getModuleMountObject(moduleCore)
});
handleCoreLoading({ event: null, resolve, reject, coreLoading, frozenConfig, cdnBaseUrl, resolvedVersion });
return;
}
document.addEventListener(moduleCore.events.ready, (event) => {
if (coreLoading) {
handleCoreLoading({ event, resolve, reject, coreLoading, frozenConfig, cdnBaseUrl, resolvedVersion });
} else {
reject({
checkoutId: frozenConfig.checkoutId,
success: false,
error: {
[MODULE_CORE]: new Error("Failed to handle Core module loading.")
}
});
}
});
coreLoading = load(frozenConfig, moduleCore, cdnBaseUrl, resolvedVersion);
preloadComgateCheckout({
...config,
excludeCore: true
// OnPreload: (payload: TPreloadComgateCheckoutResult) => {
// console.log('X', payload);
// },
});
});
}
const handleCoreLoading = async (parameters) => {
const loadResults = [parameters.coreLoading];
if (parameters.frozenConfig.modules !== void 0) {
if (parameters.frozenConfig.modules.includes(MODULE_APPLEPAY)) {
loadResults.push(load(parameters.frozenConfig, moduleApplePay, parameters.cdnBaseUrl, parameters.resolvedVersion));
}
if (parameters.frozenConfig.modules.includes(MODULE_GOOGLEPAY)) {
loadResults.push(load(parameters.frozenConfig, moduleGooglePay, parameters.cdnBaseUrl, parameters.resolvedVersion));
}
}
const modulesStatus = await Promise.all(loadResults);
const loadResponse = {
checkoutId: parameters.frozenConfig.checkoutId,
success: true
};
modulesStatus.forEach((module) => {
if (module.success) {
loadResponse[module.name] = module.mount;
} else {
loadResponse.success = false;
loadResponse.error = loadResponse.error || {};
loadResponse.error[module.name] = module.error;
}
});
if (parameters.frozenConfig.onLoad !== void 0 && typeof parameters.frozenConfig.onLoad === "function") {
parameters.frozenConfig.onLoad(loadResponse);
}
window.dispatchEvent(new Event(EVENT_MODULES_LOADED));
if (loadResponse.success) {
parameters.resolve(loadResponse);
} else {
parameters.reject(loadResponse);
}
};
const load = async (config, module, cdnBaseUrl, resolvedVersion) => new Promise((resolve, reject) => {
let resolved = false;
let timeoutId;
let isAsync = true;
let isDeferred = false;
let element = null;
const isAsyncDefined = "async" in config;
const isDeferDefined = "defer" in config;
try {
if (isAsyncDefined || isDeferDefined) {
if (config.async === true) {
isAsync = true;
isDeferred = false;
} else if (config.defer === true) {
isAsync = false;
isDeferred = true;
} else {
if (isAsyncDefined) {
isAsync = Boolean(config.async);
}
if (isDeferDefined) {
isDeferred = Boolean(config.defer);
}
}
}
const loadId = `cg-load-checkout-${module.name.toLowerCase()}`;
element = document.querySelector(`#${loadId}`);
if (element) {
if (getElementData(element, "success") === MODULE_LOAD_SUCCESSFULLY) {
setElementData(element, "success", resolveModuleLoad(resolve, module) ? MODULE_LOAD_SUCCESSFULLY : MODULE_LOAD_FAILED);
} else {
element.remove();
element = null;
}
}
if (!element) {
const script = createScriptElement(loadId, resolvedVersion, isAsync, isDeferred, cdnBaseUrl, module.file);
script.addEventListener("error", (error) => {
if (!resolved) {
resolved = true;
clearTimeout(timeoutId);
setElementData(script, "success", MODULE_LOAD_FAILED);
reject({
success: false,
name: module.name,
error: new Error("Failed to load script.", { cause: error })
});
}
});
const timeout = validateNumber(config.timeout, TIMEOUT_LOAD_MIN, TIMEOUT_LOAD_MAX, TIMEOUT_LOAD_DEFAULT);
timeoutId = window.setTimeout(() => {
if (!resolved) {
resolved = true;
clearTimeout(timeoutId);
const mountObject = getModuleMountObject(module);
if (!mountObject) {
setElementData(script, "success", MODULE_LOAD_FAILED);
reject({
success: false,
name: module.name,
error: new Error("Timeout reached.")
});
return;
}
setElementData(script, "success", MODULE_LOAD_SUCCESSFULLY);
resolve({
success: true,
name: module.name,
mount: mountObject
});
}
}, timeout);
document.addEventListener(module.events.ready, () => {
if (!resolved) {
resolved = true;
clearTimeout(timeoutId);
setElementData(script, "success", resolveModuleLoad(resolve, module) ? MODULE_LOAD_SUCCESSFULLY : MODULE_LOAD_FAILED);
}
});
document.head.append(script);
} else if (!resolved) {
resolved = true;
clearTimeout(timeoutId);
setElementData(element, "success", resolveModuleLoad(resolve, module) ? MODULE_LOAD_SUCCESSFULLY : MODULE_LOAD_FAILED);
}
} catch (error) {
if (!resolved) {
resolved = true;
setElementData(element, "success", MODULE_LOAD_FAILED);
reject({
success: false,
name: module.name,
error: new Error("Unexpected error.", { cause: error })
});
}
}
});
const resolveModuleLoad = (onResolve, module) => {
const mountObject = getModuleMountObject(module);
if (!mountObject) {
onResolve({
success: false,
name: module.name,
error: new Error(`The module does not mount correctly. Path window.${module.mountPoint.join(".")} is undefined.`)
});
return false;
}
onResolve({
success: true,
name: module.name,
mount: mountObject
});
return true;
};
const version = "2.0.11";
const revision = "b4b3507";
function getLoaderVersion() {
return {
version,
revision
};
}
export {
ENVIRONMENT_DEV,
ENVIRONMENT_PREFERRED,
ENVIRONMENT_PROD,
ENVIRONMENT_TEST,
MODULE_APPLEPAY,
MODULE_CORE,
MODULE_GOOGLEPAY,
MODULE_LOADER,
VERSION_1,
VERSION_2,
VERSION_DEV,
VERSION_PREFERRED,
checkoutIdRegex,
checkoutVersions,
getLoaderVersion,
loadComgateCheckout,
preloadComgateCheckout,
versionRegex
};
//# sourceMappingURL=comgate-checkout-js.es.js.map