UNPKG

@comgate/checkout-js

Version:

Loading wrapper for Comgate Checkout library.

681 lines (680 loc) 22.9 kB
/* * @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