From e7ddc14acbd0ffdff4af280e4a65b6d03a5e33b9 Mon Sep 17 00:00:00 2001 From: Seras3 Date: Mon, 20 Oct 2025 12:30:43 +0300 Subject: [PATCH 1/5] Click To Pay integration --- README.md | 37 +++++++++++- package-lock.json | 4 +- package.json | 2 +- src/sdk/api/actions.js | 14 +++++ src/sdk/api/index.js | 129 ++++++++++++++++++++++++++++++++++++----- 5 files changed, 167 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 0799ac7..bb8d644 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,21 @@ Setup is the first function you will call. It takes a config-object as its only autoFocusNext: true, onLoadCallback: () => someFunction, onCardBrandChangeCallback: ({ cardBrand: string }) => unknown, - el = A domElement to render the hosted fields in + el: A domElement to render the hosted fields in, + // Set the config from below if need to integrate ClickToPay + // The full list of attributes can be seen here: https://srci-docs.prod.srci.cloud.netcetera.com/sdk-config-properties + // You can override only these attributes in Hosted Fields. These are the default values: + clickToPayAttributes: { + locale: "en_US", + sandbox: "true", // Make sure to set it "false" in production + }, + // See the full config here: https://srci-docs.prod.srci.cloud.netcetera.com/sdk-config-guide#scheme-specific-configuration + clickToPayConfig: "", + onRequestIframeExpandCallback: (recommendedHeight: number) => unknown, + onCancelIframeExpandCallback: () => unkown, + // Response type here: https://srci-docs.prod.srci.cloud.netcetera.com/sdk-checkout-respons + onClickToPayCheckoutSuccessCallback: (response) => uknown, + onClickToPayCheckoutErrorCallback: (error) => unkown, } ```` @@ -128,6 +142,27 @@ and will return the detected **cardBrand**(e.g. visa, mastercard, etc.). This is usually used for dynamic credit card flow handling based on the card brand(e.g. for ClickToPay). +**clickToPayAattributes** +The attributes passed to the ClickToPay instance. + +**clickToPayConfig** +The config passed to the ClickToPay instance. + +**onRequestIframeExpandCallback** +This callback is triggered when the clickToPayCheckout call is performed to inform the integrator that +the iframe height should be changed to the **recommendedHeight** and hide all the other visible UI elements +to make room for the ClickToPay iframe to expand. + +**onCancelIframeExpandCallback** +This callback is triggered when the ClickToPay checkout has finised to inform the integrator that +the iframe height should be restored and show all the other previously hidden UI elements. + +**onClickToPayCheckoutSuccessCallback** +The callback triggered when receive a success response after a ClickToPay checkout. + +**onClickToPayCheckoutErrorCallback** +The callback triggered when receive an error after a ClickToPay cehckout. + **Possible values for hostedfieldsurl:** For test environments use: `'https://test-hostedpages.paymentiq.io/1.0.51/index.html'`. diff --git a/package-lock.json b/package-lock.json index 6793981..5f087e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hosted-fields-sdk", - "version": "1.2.8", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hosted-fields-sdk", - "version": "1.2.8", + "version": "1.3.0", "license": "ISC", "dependencies": { "@babel/runtime": "^7.27.0" diff --git a/package.json b/package.json index 8faeca9..7bba643 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hosted-fields-sdk", - "version": "1.2.8", + "version": "1.3.0", "description": "Hosted fields sdk", "main": "dist/index.js", "module": "dist/esm/index.js", diff --git a/src/sdk/api/actions.js b/src/sdk/api/actions.js index 71b6b13..d93057b 100644 --- a/src/sdk/api/actions.js +++ b/src/sdk/api/actions.js @@ -6,11 +6,25 @@ export const actions = { setupContent: 'setupContent', // Event is emitted when the iframe is created and the content will be rendered. setupSingleIframeContent: 'setupSingleIframeContent', + // Setup Click To Pay. + setupClickToPay: 'setupClickToPay', // Received when with the hosted field data. formData: 'formData', // Received when enter was pressed in the hosted fields to submit the form. formSubmit: 'formSubmit', // Received when the hosted fields credit card number changed and returs the detected card brand. cardBrandChange: 'cardBrandChange', + // Set click to pay transaction amount acordin to: https://srci-docs.prod.srci.cloud.netcetera.com/sdk-config-guide + setClickToPayTransactionAmount: 'setClickToPayTransactionAmount', + // ClickToPay checkout: https://srci-docs.prod.srci.cloud.netcetera.com/sdk-checkout-api + clickToPayCheckout: 'clickToPayCheckout', + // ClickToPay checkout success response + clickToPayCheckoutSuccess: 'clickToPayCheckoutSuccess', + // ClickToPay checkout error response + clickToPayCheckoutError: 'clickToPayCheckoutError', + // Request iframe to expand + requestIframeExpand: 'requestIframeExpand', + // Cancel iframe expand + cancelIframeExpand: 'cancelIframeExpand', } Object.freeze(actions) diff --git a/src/sdk/api/index.js b/src/sdk/api/index.js index 868aa59..8c2dea4 100644 --- a/src/sdk/api/index.js +++ b/src/sdk/api/index.js @@ -14,6 +14,8 @@ var service; var styles; // The hosted fields. var targets = []; +// The ClickToPay target iframe +var clickToPayIframe // Responses gotten from the hosted fields. var responses = []; // Element to render the hosted fields on. @@ -27,12 +29,32 @@ var onLoadCallback; // Method to call when card brand(e.g. visa, mastercard, etc.) has changed // based on the credit card nunmber that was entered. var onCardBrandChangeCallback; +// Method to call when iframe requests to expand (used for ClickToPay when 3DS window is shown) +var onRequestIframeExpandCallback; +// Method to call when iframe requests to cancel expand +var onCancelIframeExpandCallback; +// Method to call when receive a successful ClickToPay checkout response +var onClickToPayCheckoutSuccessCallback; +// Method to call when receive an error on the ClickToPay checkout +var onClickToPayCheckoutErrorCallback; // Boolean - should the next field be focused when a valid value has been set var autoFocusNext; // Keep track of number of loaded fields var onLoadCounter = 0; // This window. var window = document.parentWindow || document.defaultView; +/** + * ClickToPay attributes for the ClickToPay instance + * @param locale default: "en_US" + * @param sandbox default: "true" (set it to "false" in production) + */ +var clickToPayAttributes; +// ClickToPay config according to: https://srci-docs.prod.srci.cloud.netcetera.com/sdk-config-guide +var clickToPayConfig; + + +const recommendedExpandedHeight = 850; // in px +const iframeAllowPermissions = 'payment' function setup (config) { merchantId = config.merchantId; @@ -44,8 +66,14 @@ function setup (config) { callback = config.callback; onLoadCallback = config.onLoadCallback; onCardBrandChangeCallback = config.onCardBrandChangeCallback; + onRequestIframeExpandCallback = config.onRequestIframeExpandCallback; + onCancelIframeExpandCallback = config.onCancelIframeExpandCallback; + onClickToPayCheckoutSuccessCallback = config.onClickToPayCheckoutSuccessCallback; + onClickToPayCheckoutErrorCallback = config.onClickToPayCheckoutErrorCallback; autoFocusNext = config.autoFocusNext || false el = config.el; + clickToPayConfig = config.clickToPayConfig; + clickToPayAttributes = config.clickToPayAttributes; // Create a single iframe for all the fields (single) or create an iframe per field (multiple) if (renderMode && renderMode === 'single') { @@ -75,6 +103,39 @@ function get () { function reset () { targets = [] + clickToPayIframe = undefined +} + +function assertClickToPayIsSet () { + if(!clickToPayConfig) { + console.error('ClickToPay config not set') + return false + } + + if(!clickToPayIframe) { + console.error('ClickToPay target not set internally') + return false + } + + return true +} + +function setClickToPayTransactionAmount (transactionAmount) { + if(!assertClickToPayIsSet()) return; + + clickToPayIframe.contentWindow.postMessage({ + action: actions.setClickToPayTransactionAmount, + transactionAmount + }, hostedfieldsurl) +} + +function clickToPayCheckout (payload) { + if(!assertClickToPayIsSet()) return; + + clickToPayIframe.contentWindow.postMessage({ + action: actions.clickToPayCheckout, + payload + }, hostedfieldsurl) } @@ -98,6 +159,20 @@ function eventHandler ($event) { case actions.cardBrandChange: onCardBrandChangeCallback?.({cardBrand: $event.data.cardBrand }) break; + case actions.requestIframeExpand: + onRequestIframeExpandCallback?.(recommendedExpandedHeight) + clickToPayIframe.classList.add('expanded') + break; + case actions.clickToPayCheckoutSuccess: + onClickToPayCheckoutSuccessCallback?.($event.data.response) + break; + case actions.clickToPayCheckoutError: + onClickToPayCheckoutErrorCallback?.($event.data.error) + break; + case actions.cancelIframeExpand: + onCancelIframeExpandCallback?.() + clickToPayIframe.classList.remove('expanded') + break; case actions.formSubmit: get() break; @@ -135,11 +210,28 @@ function sendCallback () { } } +function handleOnLoad(iframe) { + onLoadCallback()(); + + if (!iframe) return; + clickToPayIframe = iframe; + + if (clickToPayConfig) { + clickToPayIframe.contentWindow.postMessage({ + action: actions.setupClickToPay, + attributes: clickToPayAttributes, + config: clickToPayConfig + }, hostedfieldsurl); + } +} + + // Sets up a single iframe for each field function initIframe (field) { var iframe = document.createElement('iframe'); iframe.id = 'hosted-field-' + field.id; iframe.name = 'hosted-field-' + field.id; + iframe.allow = iframeAllowPermissions; // iframe.tabIndex = '-1'; // This disabled the possibility to set focus on the frame and any of its contents. // This is hostedfieldsurl @@ -155,24 +247,25 @@ function initIframe (field) { container.appendChild(iframeContainerEl); // Get the target window... - var target = document.querySelector('#'+iframe.id).contentWindow; + var targetIframe = document.querySelector('#'+iframe.id); // Attach onload event listener to iframe so we can send the // setupContent event when iframe is fully loaded. - iframe.onload = createIframeProxy.bind(this, field, target) + iframe.onload = createIframeProxy.bind(this, field, targetIframe) return { - id: iframe.id, target + id: iframe.id, + target: targetIframe.contentWindow } } catch (err) { console.log(err) - onLoadCallback()() + handleOnLoad(null) } } -function createIframeProxy (field, target) { +function createIframeProxy (field, iframe) { var fieldsObj = {}; fieldsObj[field.name] = field; window.addEventListener("message", eventHandler, false) - target.postMessage({ + iframe.contentWindow.postMessage({ action: actions.setupContent, styles: styles, fields: fieldsObj, @@ -181,7 +274,7 @@ function createIframeProxy (field, target) { onLoadCounter++ if (onLoadCounter === fields.length && onLoadCallback) { - onLoadCallback()() + handleOnLoad(iframe) onLoadCounter = 0 } } @@ -192,6 +285,7 @@ function initSingleIframe () { iframe.id = 'hosted-field-single-iframe'; iframe.name = 'hosted-field-single-iframe'; // iframe.tabIndex = '-1'; // This disabled the possibility to set focus on the frame and any of its contents. + iframe.allow = iframeAllowPermissions; // This is hostedfieldsurl iframe.src = hostedfieldsurl + '?mid=' + merchantId; @@ -206,28 +300,29 @@ function initSingleIframe () { container.appendChild(iframeContainerEl); // Get the target window... - var target = document.querySelector('#hosted-field-single-iframe').contentWindow; + var targetIframe = document.querySelector('#hosted-field-single-iframe'); // Attach onload event listener to iframe so we can send the // setupContent event when iframe is fully loaded. - iframe.onload = createSingleIframeProxy.bind(this, fields, target) + iframe.onload = createSingleIframeProxy.bind(this, fields, targetIframe) return [{ - id: iframe.id, target + id: iframe.id, + target: targetIframe.contentWindow }] } catch (err) { console.log(err) - onLoadCallback()() + handleOnLoad(null) } } -function createSingleIframeProxy (fields, target) { +function createSingleIframeProxy (fields, iframe) { var fieldsObj = {}; fields.forEach(function (field) { fieldsObj[field.name] = field; }) window.addEventListener("message", eventHandler, false) - target.postMessage({ + iframe.contentWindow.postMessage({ action: actions.setupSingleIframeContent, styles: styles, fields: fieldsObj, @@ -237,7 +332,7 @@ function createSingleIframeProxy (fields, target) { } }, hostedfieldsurl); - onLoadCallback()() + handleOnLoad(iframe) } export const HostedFields = { @@ -246,5 +341,9 @@ export const HostedFields = { // Get the data from the hosted fields. get, // reset the current targets - reset + reset, + // Set Click To Pay transaction amount according to: https://srci-docs.prod.srci.cloud.netcetera.com/sdk-config-guide + setClickToPayTransactionAmount, + // Perform ClickToPay checkout according to: https://srci-docs.prod.srci.cloud.netcetera.com/sdk-checkout-api + clickToPayCheckout, }; From c037604c45ae412d0320f6d1c5d0c1dd669ba5ab Mon Sep 17 00:00:00 2001 From: Seras3 Date: Fri, 24 Oct 2025 11:05:16 +0300 Subject: [PATCH 2/5] Bumps to fix critical vulnerabilities --- package-lock.json | 99 ++++++++++++++++++++++++++++++++++++++++------- package.json | 4 ++ 2 files changed, 90 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f087e4..77922c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4274,21 +4274,53 @@ } }, "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz", + "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==", "dev": true, "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "create-hash": "~1.1.3", + "create-hmac": "^1.1.7", + "ripemd160": "=2.0.1", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.11", + "to-buffer": "^1.2.0" }, "engines": { "node": ">=0.12" } }, + "node_modules/pbkdf2/node_modules/create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "sha.js": "^2.4.0" + } + }, + "node_modules/pbkdf2/node_modules/hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1" + } + }, + "node_modules/pbkdf2/node_modules/ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==", + "dev": true, + "dependencies": { + "hash-base": "^2.0.0", + "inherits": "^2.0.1" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4767,16 +4799,23 @@ } }, "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/shasum-object": { @@ -5110,6 +5149,26 @@ "node": ">=0.6.0" } }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "dev": true, + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5152,6 +5211,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", diff --git a/package.json b/package.json index 7bba643..b377b65 100644 --- a/package.json +++ b/package.json @@ -55,5 +55,9 @@ "homepage": "https://github.com/devcode-git/hosted-fields-sdk#readme", "dependencies": { "@babel/runtime": "^7.27.0" + }, + "overrides": { + "pbkdf2": "3.1.3", + "sha.js": "2.4.12" } } From 205f770c02a08a17a912f9b08bc6d25173fea8c7 Mon Sep 17 00:00:00 2001 From: Seras3 Date: Fri, 24 Oct 2025 11:06:18 +0300 Subject: [PATCH 3/5] PIQ-2907: Update CHANGES.md + README.md --- CHANGES.md | 3 +++ README.md | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 780f0c9..17427c9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +#### 1.3.0 +- Add ClickToPay integration + #### 1.2.8 - Add onCardBrandChangeCallback diff --git a/README.md b/README.md index bb8d644..e349f4f 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,12 @@ Expiry: `cc-exp` #### HostedFields -HostedFields in turn will expose three functions +HostedFields in turn will expose these functions * setup * get * reset +* setClickToPayTransactionAmount +* clickToPayCheckout **setup** @@ -213,6 +215,22 @@ var container = document.querySelector(el); ```` Lastly eventListener are registered to the iframe so that it picks up postMessage events. + +**setClickToPayTransactionAmount** *(used for ClickToPay integration)* + +It's used when the transaction amount should be dynamically changed. + +Set ClickToPay transaction amount according to: +https://srci-docs.prod.srci.cloud.netcetera.com/sdk-config-guide + + +**clickToPayCheckout** *(used for ClickToPay integration)* + +Perform ClickToPay checkout according to: +https://srci-docs.prod.srci.cloud.netcetera.com/sdk-checkout-api + + + ## Styling Styling will mainly be handled buy the application using the hosted-fields. Each field will have some basic styling but the layout will have to be supplied. From 21686ba3e0f012377b4a7ff03cbb27a0f780f0c0 Mon Sep 17 00:00:00 2001 From: Seras3 Date: Fri, 24 Oct 2025 11:29:33 +0300 Subject: [PATCH 4/5] PIQ-2907: Make onLoadCallback optional --- src/sdk/api/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdk/api/index.js b/src/sdk/api/index.js index 8c2dea4..04bf65c 100644 --- a/src/sdk/api/index.js +++ b/src/sdk/api/index.js @@ -211,7 +211,7 @@ function sendCallback () { } function handleOnLoad(iframe) { - onLoadCallback()(); + onLoadCallback?.()(); if (!iframe) return; clickToPayIframe = iframe; From 069bb2cb42c8f1f7687a02cb87c053e08a88d4ac Mon Sep 17 00:00:00 2001 From: Seras3 Date: Mon, 27 Oct 2025 11:41:43 +0200 Subject: [PATCH 5/5] Issue-21: Add high-level types with index.d.ts --- CHANGES.md | 4 +- index.d.ts | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 index.d.ts diff --git a/CHANGES.md b/CHANGES.md index 17427c9..286adc3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,7 @@ #### 1.3.0 -- Add ClickToPay integration +- Add ClickToPay integration support +- Provide high-level types with index.d.ts ([Issue 21](https://github.com/devcode-git/hosted-fields-sdk/issues/21)) +- Make onLoadCallback optional ([Issue 22](https://github.com/devcode-git/hosted-fields-sdk/issues/21)) #### 1.2.8 - Add onCardBrandChangeCallback diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..873317d --- /dev/null +++ b/index.d.ts @@ -0,0 +1,134 @@ +declare module "hosted-fields-sdk" { + export interface HostedFieldsConfig { + [key: string]: any; + } + + export interface ClickToPayPayload { + [key: string]: any; + } + + export interface ClickToPayTransactionAmount { + /** + * The transaction amount as a string.(e.g., "10") + */ + amount: string; + /** + * The currency code in ISO 4217 format. (e.g., "USD") + */ + currencyCode: string; + } + + export interface HostedFields { + /** + * Initializes hosted fields with the provided configuration. + */ + setup(config: HostedFieldsConfig): void; + + /** + * If you want to get the encrypted values from the fields you can call this method. + * This will trigger the supplied callback-function registered in HostedFields.setup() to be called with the values for each field. + */ + get(): void; + + /** + * If you wish to reset the currently rendered iframes (fields) you can call HostedFields.reset() before running a new setup(). + * This can be required if your page that contains the fields gets re-rendered. In that case you will have registered duplicates of the fields. + * So it's a good idea to call HostedFields.reset() on a beforeDestroy-hook if you are using Vue or React. + */ + reset(): void; + + /** + * Updates the Click to Pay transaction amount. + */ + setClickToPayTransactionAmount(transactionAmount: any): void; + + /** + * Initiates a Click to Pay checkout flow. + */ + clickToPayCheckout(payload: ClickToPayPayload): void; + } + + /** + * Main HostedFields singleton/object exposed by the SDK. + */ + export const HostedFields: HostedFields; + + /** + * The supported field types. + */ + export const FieldTypes: { + readonly TEXT: "TEXT"; + readonly NUMBER: "NUMBER"; + readonly CVV: "CVV"; + readonly CREDITCARD_NUMBER: "CREDITCARD_NUMBER"; + readonly EXPIRY_MM_YYYY: "EXPIRY_MM_YYYY"; + }; + + /** + * Represents a single hosted field configuration. + */ + export class Field { + /** + * The type of the field (e.g., TEXT, NUMBER, etc.). + */ + type: keyof typeof FieldTypes; + + /** + * The HTML id of the field. + */ + id: string; + + /** + * The name of the field (used as a key when retrieving hosted field data). + */ + name: string; + + /** + * The label text of the field. + */ + label: string; + + /** + * Placeholder text to display inside the field. + */ + helpKey: string; + + /** + * Error message to display if the field has validation issues. + */ + error: string; + + /** + * Whether the field should be visible (default: true). + */ + visible: boolean; + + /** + * Whether the field is required (default: true). + */ + required: boolean; + + /** + * For backward compatibility: set to true to enable autofill (disables value formatting). + */ + noAttributeValueFormatting: boolean; + + /** + * What autofill value the field should use (e.g., "cc-number", "cc-csc", "cc-exp"). + */ + autocomplete: string; + + constructor( + type: keyof typeof FieldTypes, + id: string, + name: string, + label: string, + helpKey?: string, + errorKey?: string, + visible?: boolean, + required?: boolean, + noAttributeValueFormatting?: boolean, + autocomplete?: string + ); + } +} \ No newline at end of file diff --git a/package.json b/package.json index b377b65..dde3ded 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.3.0", "description": "Hosted fields sdk", "main": "dist/index.js", + "types": "index.d.ts", "module": "dist/esm/index.js", "exports": { ".": {