UNPKG

@foxy.io/sdk

Version:

Universal SDK for a full server-side and a limited in-browser access to Foxy hAPI.

167 lines (166 loc) 6.91 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; /** * A convenience wrapper for the payment card embed iframe. You don't have to use * this class to embed the payment card iframe, but it provides a more convenient * way to interact with the iframe and listen to its events. * * @example * const embed = new PaymentCardEmbed({ * url: 'https://embed.foxy.io/v1.html?template_set_id=123' * }); * * await embed.mount(document.body); * console.log('Token:', await embed.tokenize()); */ export class PaymentCardEmbed { constructor(_a) { var { url } = _a, config = __rest(_a, ["url"]); /** * An event handler that is triggered when Enter is pressed in the card form. * This feature is not available for template sets configured with the `stripe_connect` * hosted payment gateway due to the limitations of Stripe.js. */ this.onsubmit = null; this.__tokenizationRequests = []; this.__iframeMessageHandler = (evt) => { var _a, _b; const data = JSON.parse(evt.data); switch (data.type) { case 'tokenization_response': { const request = this.__tokenizationRequests.find(r => r.id === data.id); data.token ? request === null || request === void 0 ? void 0 : request.resolve(data.token) : request === null || request === void 0 ? void 0 : request.reject(); this.__tokenizationRequests = this.__tokenizationRequests.filter(r => r.id !== data.id); break; } case 'submit': { (_a = this.onsubmit) === null || _a === void 0 ? void 0 : _a.call(this); break; } case 'resize': { if (this.__iframe) this.__iframe.style.height = data.height; break; } case 'ready': { this.configure(this.__config); (_b = this.__mountingTask) === null || _b === void 0 ? void 0 : _b.resolve(); break; } } }; this.__iframeLoadHandler = (evt) => { if (this.__channel) { const contentWindow = evt.currentTarget.contentWindow; if (!contentWindow) throw new Error('Content window is not available.'); contentWindow.postMessage('connect', '*', [this.__channel.port2]); } }; this.__mountingTask = null; this.__channel = null; this.__iframe = null; this.__config = config; this.__url = url; } /** * Updates the configuration of the payment card embed. * You can change style, translations, language and interactivity settings. * To change the URL of the payment card embed, you need to create a new instance. * You are not required to provide the full configuration object, only the properties you want to change. * * @param config - The new configuration. */ configure(config) { var _a; this.__config = config; const message = Object.assign({ type: 'config' }, config); (_a = this.__channel) === null || _a === void 0 ? void 0 : _a.port1.postMessage(JSON.stringify(message)); } /** * Requests the tokenization of the card data. * * @returns A promise that resolves with the tokenized card data. */ tokenize() { return new Promise((resolve, reject) => { if (this.__channel) { const id = this._createId(); this.__tokenizationRequests.push({ id, reject, resolve }); this.__channel.port1.postMessage(JSON.stringify({ id, type: 'tokenization_request' })); } else { reject(); } }); } /** * Safely removes the embed iframe from the parent node, * closing the message channel and cleaning up event listeners. */ unmount() { var _a, _b, _c, _d, _e, _f; (_a = this.__channel) === null || _a === void 0 ? void 0 : _a.port1.removeEventListener('message', this.__iframeMessageHandler); (_b = this.__channel) === null || _b === void 0 ? void 0 : _b.port1.close(); (_c = this.__channel) === null || _c === void 0 ? void 0 : _c.port2.close(); this.__channel = null; (_d = this.__iframe) === null || _d === void 0 ? void 0 : _d.removeEventListener('load', this.__iframeLoadHandler); (_e = this.__iframe) === null || _e === void 0 ? void 0 : _e.remove(); this.__iframe = null; (_f = this.__mountingTask) === null || _f === void 0 ? void 0 : _f.reject(); this.__mountingTask = null; } /** * Mounts the payment card embed in the given root element. If the embed is already mounted, * it will be unmounted first. * * @param root - The root element to mount the embed in. * @returns A promise that resolves when the embed is mounted. */ mount(root) { this.unmount(); this.__channel = this._createMessageChannel(); this.__channel.port1.addEventListener('message', this.__iframeMessageHandler); this.__channel.port1.start(); this.__iframe = this._createIframe(root); this.__iframe.addEventListener('load', this.__iframeLoadHandler); this.__iframe.style.transition = 'height 0.15s ease'; this.__iframe.style.margin = '-2px'; this.__iframe.style.height = '100px'; this.__iframe.style.width = 'calc(100% + 4px)'; this.__iframe.src = this.__url; root.append(this.__iframe); return new Promise((resolve, reject) => { this.__mountingTask = { reject, resolve }; }); } /** * Clears the card data from the embed. * No-op if the embed is not mounted. */ clear() { var _a; (_a = this.__channel) === null || _a === void 0 ? void 0 : _a.port1.postMessage(JSON.stringify({ type: 'clear' })); } /* v8 ignore next */ _createMessageChannel() { return new MessageChannel(); } /* v8 ignore next */ _createIframe(root) { return root.ownerDocument.createElement('iframe'); } /* v8 ignore next */ _createId() { return `${Date.now()}${Math.random().toFixed(6).slice(2)}`; } }