diff --git a/CHANGES.md b/CHANGES.md index 780f0c9..286adc3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +#### 1.3.0 +- 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/README.md b/README.md index 0799ac7..ef94b0f 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ Hosted fields SDK is a toolkit that allows you generate a form/set of fields. It is published as a node-module to the public [npm registry](https://www.npmjs.com/package/hosted-fields-sdk). > [!CAUTION] -> **Migration and Deprecation Notice:** Starting from version 1.0.50, it is mandatory to use the `'https://card-fields.paymentiq.io/1.0.51/index.html'` as `hostedfieldsurl` value for **production** environments. Please update your configurations accordingly to avoid potential disruptions. +> **Migration and Deprecation Notice:** Starting from version 1.0.50, it is mandatory to use the `'https://card-fields.paymentiq.io/1.1.1/index.html'` as `hostedfieldsurl` value for **production** environments. Please update your configurations accordingly to avoid potential disruptions. > -> The old hostedfieldsurl: `'https://hostedpages.paymentiq.io/1.0.51/index.html'` and well as *all versions previous to 1.0.50* **will be deprecated starting 15 March 2025**. Please note that using older versions and the old domain may expose your system to potential risks. +> The old hostedfieldsurl: `'https://hostedpages.paymentiq.io/1.0.51/index.html'` and well as *all versions previous to 1.0.50* **will be deprecated starting 15 March 2025**. Please note that using older versions and the old domain may expose your system to potential risks. Starting with version 1.1.1, **hostedpages** domain will not be used anymore. > > This change is meant to enhance security and future compliance with the new requirements in the Payment Card Industry Data Security Standards. The old domain is scheduled for deprecation. We advise all users to transition to the new domain as soon as possible to maintain compliance and benefit from improved security measures. > @@ -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** @@ -87,7 +89,7 @@ Setup is the first function you will call. It takes a config-object as its only ````js { merchantId: 123456789, - hostedfieldsurl: 'https://card-fields.paymentiq.io/1.0.51/index.html', + hostedfieldsurl: 'https://card-fields.paymentiq.io/1.1.1/index.html', fields: my_fields, //fields you've generated using the Field-constructor renderMode: 'single', // defaults to 'multiple', separate iframes per field service: 'payment_method_service', // service of the payment method. Not mandatory (AstropayCard requires this) @@ -96,7 +98,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,13 +144,44 @@ 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). -**Possible values for hostedfieldsurl:** +**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. -For test environments use: `'https://test-hostedpages.paymentiq.io/1.0.51/index.html'`. +**onClickToPayCheckoutSuccessCallback** +The callback triggered when receive a success response after a ClickToPay checkout. -For production environments use `'https://card-fields.paymentiq.io/1.0.51/index.html'`. +**onClickToPayCheckoutErrorCallback** +The callback triggered when receive an error after a ClickToPay cehckout. -**Available versions for `'https://card-fields.paymentiq.io/1.0.51/index.html'`(where the part 1.0.51 represents the version number):** +**Possible values for hostedfieldsurl:** + +For test environments use: `'https://test-hostedpages.paymentiq.io/1.1.1/index.html'`. + +For production environments use `'https://card-fields.paymentiq.io/1.1.1/index.html'`. + +**Available versions for `'https://card-fields.paymentiq.io/1.1.1/index.html'`(where the part 1.1.1 represents the version number):** +- 1.1.1 +- 1.0.61 +- 1.0.60 +- 1.0.58 +- 1.0.57 +- 1.0.56 +- 1.0.55 +- 1.0.54 +- 1.0.53 +- 1.0.52 - 1.0.51 - 1.0.50 @@ -178,6 +225,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. @@ -240,7 +303,7 @@ let fields = fieldConfig.map(conf => { ````js HostedFields.setup({ merchantId: 123456789, - hostedfieldsurl: 'https://card-fields.paymentiq.io/1.0.51/index.html', + hostedfieldsurl: 'https://card-fields.paymentiq.io/1.1.1/index.html', fields: fields, service: 'some service', styles: '* .hosted-input-container .input-container input { color: green; }', @@ -387,7 +450,7 @@ let fields = fieldConfig.map(conf => { HostedFields.setup({ merchantId: 123456789, - hostedfieldsurl: 'https://card-fields.paymentiq.io/1.0.51/index.html', + hostedfieldsurl: 'https://card-fields.paymentiq.io/1.1.1/index.html', fields: fields, service: 'some service', styles: '.hosted-input-container .input-container input { color: red; }', @@ -413,7 +476,7 @@ Overview of Change: We have recently updated the domain used for our card fields to enhance security and compliance with current standards. The old domain, while PCI DSS compliant, is scheduled for deprecation. We advise all users to transition to the new domain as soon as possible to maintain compliance and benefit from improved security measures. #### New Domain: -Domain: `card-fields.paymentiq.io/1.0.51/` +Domain: `card-fields.paymentiq.io/1.1.1/` #### Compatibility Note: Supported Versions: This domain supports versions starting with 1.0.50 and above. Make sure your implementation is updated to at least this version to ensure compatibility. diff --git a/dist/esm/sdk/api/actions.js b/dist/esm/sdk/api/actions.js index c53e0c1..dc43429 100644 --- a/dist/esm/sdk/api/actions.js +++ b/dist/esm/sdk/api/actions.js @@ -1 +1 @@ -import _typeof from"@babel/runtime/helpers/typeof";var actions={get:"get",setupContent:"setupContent",setupSingleIframeContent:"setupSingleIframeContent",formData:"formData",formSubmit:"formSubmit",cardBrandChange:"cardBrandChange"};function _getGlobalObject(){try{if(global)return global}catch(_){try{if(window)return window}catch(_){return this}}}Object.freeze(_get__("actions"));var _RewireModuleId__=null;function _getRewireModuleId__(){var _;return null===_RewireModuleId__&&((_=_getGlobalObject()).__$$GLOBAL_REWIRE_NEXT_MODULE_ID__||(_.__$$GLOBAL_REWIRE_NEXT_MODULE_ID__=0),_RewireModuleId__=__$$GLOBAL_REWIRE_NEXT_MODULE_ID__++),_RewireModuleId__}function _getRewireRegistry__(){var _=_getGlobalObject();return _.__$$GLOBAL_REWIRE_REGISTRY__||(_.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)),_.__$$GLOBAL_REWIRE_REGISTRY__}function _getRewiredData__(){var _=_getRewireModuleId__(),e=_getRewireRegistry__(),t=e[_];return t||(e[_]=Object.create(null),t=e[_]),t}(()=>{var _=_getGlobalObject();_.__rewire_reset_all__||(_.__rewire_reset_all__=function(){_.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)})})();var INTENTIONAL_UNDEFINED="__INTENTIONAL_UNDEFINED__",_RewireAPI__={};function _get__(_){var e=_getRewiredData__();return void 0===e[_]?_get_original__(_):(e=e[_])!==INTENTIONAL_UNDEFINED?e:void 0}function _get_original__(_){if("actions"===_)return actions}function _assign__(_,e){var t=_getRewiredData__();return void 0===t[_]?_set_original__(_,e):t[_]=e}function _set_original__(_,e){_}function _update_operation__(_,e,t){var r=_get__(e),_="++"===_?r+1:r-1;return _assign__(e,_),t?_:r}function _set__(e,_){var t=_getRewiredData__();return"object"===_typeof(e)?(Object.keys(e).forEach(function(_){t[_]=e[_]}),function(){Object.keys(e).forEach(function(_){_reset__(e)})}):(t[e]=void 0===_?INTENTIONAL_UNDEFINED:_,function(){_reset__(e)})}function _reset__(_){var e=_getRewiredData__();delete e[_],0==Object.keys(e).length&&delete _getRewireRegistry__()[_getRewireModuleId__]}function _with__(e){var t=_getRewiredData__(),r=Object.keys(e),n={};function i(){r.forEach(function(_){t[_]=n[_]})}return function(_){r.forEach(function(_){n[_]=t[_],t[_]=e[_]});_=_();return _&&"function"==typeof _.then?_.then(i).catch(i):i(),_}}(()=>{function _(_,e){Object.defineProperty(_RewireAPI__,_,{value:e,enumerable:!1,configurable:!0})}_("__get__",_get__),_("__GetDependency__",_get__),_("__Rewire__",_set__),_("__set__",_set__),_("__reset__",_reset__),_("__ResetDependency__",_reset__),_("__with__",_with__)})();export default _RewireAPI__;export{actions,_get__ as __get__,_get__ as __GetDependency__,_set__ as __Rewire__,_set__ as __set__,_reset__ as __ResetDependency__,_RewireAPI__ as __RewireAPI__}; \ No newline at end of file +import _typeof from"@babel/runtime/helpers/typeof";var actions={get:"get",setupContent:"setupContent",setupSingleIframeContent:"setupSingleIframeContent",setupClickToPay:"setupClickToPay",formData:"formData",formSubmit:"formSubmit",cardBrandChange:"cardBrandChange",setClickToPayTransactionAmount:"setClickToPayTransactionAmount",clickToPayCheckout:"clickToPayCheckout",clickToPayCheckoutSuccess:"clickToPayCheckoutSuccess",clickToPayCheckoutError:"clickToPayCheckoutError",requestIframeExpand:"requestIframeExpand",cancelIframeExpand:"cancelIframeExpand"};function _getGlobalObject(){try{if(global)return global}catch(_){try{if(window)return window}catch(_){return this}}}Object.freeze(_get__("actions"));var _RewireModuleId__=null;function _getRewireModuleId__(){var _;return null===_RewireModuleId__&&((_=_getGlobalObject()).__$$GLOBAL_REWIRE_NEXT_MODULE_ID__||(_.__$$GLOBAL_REWIRE_NEXT_MODULE_ID__=0),_RewireModuleId__=__$$GLOBAL_REWIRE_NEXT_MODULE_ID__++),_RewireModuleId__}function _getRewireRegistry__(){var _=_getGlobalObject();return _.__$$GLOBAL_REWIRE_REGISTRY__||(_.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)),_.__$$GLOBAL_REWIRE_REGISTRY__}function _getRewiredData__(){var _=_getRewireModuleId__(),e=_getRewireRegistry__(),t=e[_];return t||(e[_]=Object.create(null),t=e[_]),t}(()=>{var _=_getGlobalObject();_.__rewire_reset_all__||(_.__rewire_reset_all__=function(){_.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)})})();var INTENTIONAL_UNDEFINED="__INTENTIONAL_UNDEFINED__",_RewireAPI__={};function _get__(_){var e=_getRewiredData__();return void 0===e[_]?_get_original__(_):(e=e[_])!==INTENTIONAL_UNDEFINED?e:void 0}function _get_original__(_){if("actions"===_)return actions}function _assign__(_,e){var t=_getRewiredData__();return void 0===t[_]?_set_original__(_,e):t[_]=e}function _set_original__(_,e){_}function _update_operation__(_,e,t){var r=_get__(e),_="++"===_?r+1:r-1;return _assign__(e,_),t?_:r}function _set__(e,_){var t=_getRewiredData__();return"object"===_typeof(e)?(Object.keys(e).forEach(function(_){t[_]=e[_]}),function(){Object.keys(e).forEach(function(_){_reset__(e)})}):(t[e]=void 0===_?INTENTIONAL_UNDEFINED:_,function(){_reset__(e)})}function _reset__(_){var e=_getRewiredData__();delete e[_],0==Object.keys(e).length&&delete _getRewireRegistry__()[_getRewireModuleId__]}function _with__(e){var t=_getRewiredData__(),r=Object.keys(e),n={};function a(){r.forEach(function(_){t[_]=n[_]})}return function(_){r.forEach(function(_){n[_]=t[_],t[_]=e[_]});_=_();return _&&"function"==typeof _.then?_.then(a).catch(a):a(),_}}(()=>{function _(_,e){Object.defineProperty(_RewireAPI__,_,{value:e,enumerable:!1,configurable:!0})}_("__get__",_get__),_("__GetDependency__",_get__),_("__Rewire__",_set__),_("__set__",_set__),_("__reset__",_reset__),_("__ResetDependency__",_reset__),_("__with__",_with__)})();export default _RewireAPI__;export{actions,_get__ as __get__,_get__ as __GetDependency__,_set__ as __Rewire__,_set__ as __set__,_reset__ as __ResetDependency__,_RewireAPI__ as __RewireAPI__}; \ No newline at end of file diff --git a/dist/esm/sdk/api/index.js b/dist/esm/sdk/api/index.js index 85eb803..f60992b 100644 --- a/dist/esm/sdk/api/index.js +++ b/dist/esm/sdk/api/index.js @@ -1 +1 @@ -import _typeof from"@babel/runtime/helpers/typeof";import _defineProperty from"@babel/runtime/helpers/defineProperty";import _objectWithoutProperties from"@babel/runtime/helpers/objectWithoutProperties";var merchantId,renderMode,fields,hostedfieldsurl,service,styles,_excluded=["errors"],_excluded2=["errors"];function ownKeys(t,e){var r,_=Object.keys(t);return Object.getOwnPropertySymbols&&(r=Object.getOwnPropertySymbols(t),e&&(r=r.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),_.push.apply(_,r)),_}function _objectSpread(t){for(var e=1;e{var e=_getGlobalObject();e.__rewire_reset_all__||(e.__rewire_reset_all__=function(){e.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)})})();var INTENTIONAL_UNDEFINED="__INTENTIONAL_UNDEFINED__",_RewireAPI__={};function _get__(e){var t=_getRewiredData__();return void 0===t[e]?_get_original__(e):(t=t[e])!==INTENTIONAL_UNDEFINED?t:void 0}function _get_original__(e){switch(e){case"merchantId":return merchantId;case"renderMode":return renderMode;case"hostedfieldsurl":return hostedfieldsurl;case"fields":return fields;case"service":return service;case"styles":return styles;case"callback":return callback;case"onLoadCallback":return onLoadCallback;case"onCardBrandChangeCallback":return onCardBrandChangeCallback;case"autoFocusNext":return autoFocusNext;case"el":return el;case"registerSingleIframe":return registerSingleIframe;case"registerIframes":return registerIframes;case"targets":return targets;case"actions":return actions;case"initIframe":return initIframe;case"initSingleIframe":return initSingleIframe;case"validateOrigin":return validateOrigin;case"responses":return responses;case"sendCallback":return sendCallback;case"get":return get;case"createIframeProxy":return createIframeProxy;case"window":return window;case"eventHandler":return eventHandler;case"onLoadCounter":return onLoadCounter;case"createSingleIframeProxy":return createSingleIframeProxy;case"setup":return setup;case"reset":return reset}}function _assign__(e,t){var r=_getRewiredData__();return void 0===r[e]?_set_original__(e,t):r[e]=t}function _set_original__(e,t){switch(e){case"merchantId":return merchantId=t;case"renderMode":return renderMode=t;case"hostedfieldsurl":return hostedfieldsurl=t;case"fields":return fields=t;case"service":return service=t;case"styles":return styles=t;case"callback":return callback=t;case"onLoadCallback":return onLoadCallback=t;case"onCardBrandChangeCallback":return onCardBrandChangeCallback=t;case"autoFocusNext":return autoFocusNext=t;case"el":return el=t;case"targets":return targets=t;case"responses":return responses=t;case"onLoadCounter":return onLoadCounter=t}}function _update_operation__(e,t,r){var _=_get__(t),e="++"===e?_+1:_-1;return _assign__(t,e),r?e:_}function _set__(t,e){var r=_getRewiredData__();return"object"===_typeof(t)?(Object.keys(t).forEach(function(e){r[e]=t[e]}),function(){Object.keys(t).forEach(function(e){_reset__(t)})}):(r[t]=void 0===e?INTENTIONAL_UNDEFINED:e,function(){_reset__(t)})}function _reset__(e){var t=_getRewiredData__();delete t[e],0==Object.keys(t).length&&delete _getRewireRegistry__()[_getRewireModuleId__]}function _with__(t){var r=_getRewiredData__(),_=Object.keys(t),n={};function a(){_.forEach(function(e){r[e]=n[e]})}return function(e){_.forEach(function(e){n[e]=r[e],r[e]=t[e]});e=e();return e&&"function"==typeof e.then?e.then(a).catch(a):a(),e}}(()=>{function e(e,t){Object.defineProperty(_RewireAPI__,e,{value:t,enumerable:!1,configurable:!0})}e("__get__",_get__),e("__GetDependency__",_get__),e("__Rewire__",_set__),e("__set__",_set__),e("__reset__",_reset__),e("__ResetDependency__",_reset__),e("__with__",_with__)})();export default _RewireAPI__;export{HostedFields,_get__ as __get__,_get__ as __GetDependency__,_set__ as __Rewire__,_set__ as __set__,_reset__ as __ResetDependency__,_RewireAPI__ as __RewireAPI__}; \ No newline at end of file +import _typeof from"@babel/runtime/helpers/typeof";import _defineProperty from"@babel/runtime/helpers/defineProperty";import _objectWithoutProperties from"@babel/runtime/helpers/objectWithoutProperties";var merchantId,renderMode,fields,hostedfieldsurl,service,styles,_excluded=["errors"],_excluded2=["errors"];function ownKeys(t,e){var a,r=Object.keys(t);return Object.getOwnPropertySymbols&&(a=Object.getOwnPropertySymbols(t),e&&(a=a.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,a)),r}function _objectSpread(t){for(var e=1;e{var e=_getGlobalObject();e.__rewire_reset_all__||(e.__rewire_reset_all__=function(){e.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)})})();var INTENTIONAL_UNDEFINED="__INTENTIONAL_UNDEFINED__",_RewireAPI__={};function _get__(e){var t=_getRewiredData__();return void 0===t[e]?_get_original__(e):(t=t[e])!==INTENTIONAL_UNDEFINED?t:void 0}function _get_original__(e){switch(e){case"merchantId":return merchantId;case"renderMode":return renderMode;case"hostedfieldsurl":return hostedfieldsurl;case"fields":return fields;case"service":return service;case"styles":return styles;case"callback":return callback;case"onLoadCallback":return onLoadCallback;case"onCardBrandChangeCallback":return onCardBrandChangeCallback;case"onRequestIframeExpandCallback":return onRequestIframeExpandCallback;case"onCancelIframeExpandCallback":return onCancelIframeExpandCallback;case"onClickToPayCheckoutSuccessCallback":return onClickToPayCheckoutSuccessCallback;case"onClickToPayCheckoutErrorCallback":return onClickToPayCheckoutErrorCallback;case"autoFocusNext":return autoFocusNext;case"el":return el;case"clickToPayConfig":return clickToPayConfig;case"clickToPayAttributes":return clickToPayAttributes;case"registerSingleIframe":return registerSingleIframe;case"registerIframes":return registerIframes;case"targets":return targets;case"actions":return actions;case"clickToPayIframe":return clickToPayIframe;case"assertClickToPayIsSet":return assertClickToPayIsSet;case"initIframe":return initIframe;case"initSingleIframe":return initSingleIframe;case"validateOrigin":return validateOrigin;case"responses":return responses;case"sendCallback":return sendCallback;case"recommendedExpandedHeight":return recommendedExpandedHeight;case"get":return get;case"iframeAllowPermissions":return iframeAllowPermissions;case"createIframeProxy":return createIframeProxy;case"handleOnLoad":return handleOnLoad;case"window":return window;case"eventHandler":return eventHandler;case"onLoadCounter":return onLoadCounter;case"createSingleIframeProxy":return createSingleIframeProxy;case"setup":return setup;case"reset":return reset;case"setClickToPayTransactionAmount":return setClickToPayTransactionAmount;case"clickToPayCheckout":return clickToPayCheckout}}function _assign__(e,t){var a=_getRewiredData__();return void 0===a[e]?_set_original__(e,t):a[e]=t}function _set_original__(e,t){switch(e){case"merchantId":return merchantId=t;case"renderMode":return renderMode=t;case"hostedfieldsurl":return hostedfieldsurl=t;case"fields":return fields=t;case"service":return service=t;case"styles":return styles=t;case"callback":return callback=t;case"onLoadCallback":return onLoadCallback=t;case"onCardBrandChangeCallback":return onCardBrandChangeCallback=t;case"onRequestIframeExpandCallback":return onRequestIframeExpandCallback=t;case"onCancelIframeExpandCallback":return onCancelIframeExpandCallback=t;case"onClickToPayCheckoutSuccessCallback":return onClickToPayCheckoutSuccessCallback=t;case"onClickToPayCheckoutErrorCallback":return onClickToPayCheckoutErrorCallback=t;case"autoFocusNext":return autoFocusNext=t;case"el":return el=t;case"clickToPayConfig":return clickToPayConfig=t;case"clickToPayAttributes":return clickToPayAttributes=t;case"targets":return targets=t;case"clickToPayIframe":return clickToPayIframe=t;case"responses":return responses=t;case"onLoadCounter":return onLoadCounter=t}}function _update_operation__(e,t,a){var r=_get__(t),e="++"===e?r+1:r-1;return _assign__(t,e),a?e:r}function _set__(t,e){var a=_getRewiredData__();return"object"===_typeof(t)?(Object.keys(t).forEach(function(e){a[e]=t[e]}),function(){Object.keys(t).forEach(function(e){_reset__(t)})}):(a[t]=void 0===e?INTENTIONAL_UNDEFINED:e,function(){_reset__(t)})}function _reset__(e){var t=_getRewiredData__();delete t[e],0==Object.keys(t).length&&delete _getRewireRegistry__()[_getRewireModuleId__]}function _with__(t){var a=_getRewiredData__(),r=Object.keys(t),n={};function _(){r.forEach(function(e){a[e]=n[e]})}return function(e){r.forEach(function(e){n[e]=a[e],a[e]=t[e]});e=e();return e&&"function"==typeof e.then?e.then(_).catch(_):_(),e}}(()=>{function e(e,t){Object.defineProperty(_RewireAPI__,e,{value:t,enumerable:!1,configurable:!0})}e("__get__",_get__),e("__GetDependency__",_get__),e("__Rewire__",_set__),e("__set__",_set__),e("__reset__",_reset__),e("__ResetDependency__",_reset__),e("__with__",_with__)})();export default _RewireAPI__;export{HostedFields,_get__ as __get__,_get__ as __GetDependency__,_set__ as __Rewire__,_set__ as __set__,_reset__ as __ResetDependency__,_RewireAPI__ as __RewireAPI__}; \ No newline at end of file diff --git a/dist/sdk/api/actions.js b/dist/sdk/api/actions.js index 79e8db8..c58ea1e 100644 --- a/dist/sdk/api/actions.js +++ b/dist/sdk/api/actions.js @@ -1 +1 @@ -var _typeof=require("@babel/runtime/helpers/typeof"),actions=(Object.defineProperty(exports,"__esModule",{value:!0}),exports.__ResetDependency__=_reset__,exports.__RewireAPI__=void 0,exports.__set__=exports.__Rewire__=_set__,exports.__GetDependency__=exports.__get__=_get__,exports.default=exports.actions=void 0,exports.actions={get:"get",setupContent:"setupContent",setupSingleIframeContent:"setupSingleIframeContent",formData:"formData",formSubmit:"formSubmit",cardBrandChange:"cardBrandChange"});function _getGlobalObject(){try{if(global)return global}catch(_){try{if(window)return window}catch(_){return this}}}Object.freeze(_get__("actions"));var _RewireModuleId__=null;function _getRewireModuleId__(){var _;return null===_RewireModuleId__&&((_=_getGlobalObject()).__$$GLOBAL_REWIRE_NEXT_MODULE_ID__||(_.__$$GLOBAL_REWIRE_NEXT_MODULE_ID__=0),_RewireModuleId__=__$$GLOBAL_REWIRE_NEXT_MODULE_ID__++),_RewireModuleId__}function _getRewireRegistry__(){var _=_getGlobalObject();return _.__$$GLOBAL_REWIRE_REGISTRY__||(_.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)),_.__$$GLOBAL_REWIRE_REGISTRY__}function _getRewiredData__(){var _=_getRewireModuleId__(),e=_getRewireRegistry__(),t=e[_];return t||(e[_]=Object.create(null),t=e[_]),t}(()=>{var _=_getGlobalObject();_.__rewire_reset_all__||(_.__rewire_reset_all__=function(){_.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)})})();var INTENTIONAL_UNDEFINED="__INTENTIONAL_UNDEFINED__",_RewireAPI__=exports.__RewireAPI__={};function _get__(_){var e=_getRewiredData__();return void 0===e[_]?_get_original__(_):(e=e[_])!==INTENTIONAL_UNDEFINED?e:void 0}function _get_original__(_){if("actions"===_)return actions}function _assign__(_,e){var t=_getRewiredData__();return void 0===t[_]?_set_original__(_,e):t[_]=e}function _set_original__(_,e){_}function _update_operation__(_,e,t){var r=_get__(e),_="++"===_?r+1:r-1;return _assign__(e,_),t?_:r}function _set__(e,_){var t=_getRewiredData__();return"object"===_typeof(e)?(Object.keys(e).forEach(function(_){t[_]=e[_]}),function(){Object.keys(e).forEach(function(_){_reset__(e)})}):(t[e]=void 0===_?INTENTIONAL_UNDEFINED:_,function(){_reset__(e)})}function _reset__(_){var e=_getRewiredData__();delete e[_],0==Object.keys(e).length&&delete _getRewireRegistry__()[_getRewireModuleId__]}function _with__(e){var t=_getRewiredData__(),r=Object.keys(e),n={};function i(){r.forEach(function(_){t[_]=n[_]})}return function(_){r.forEach(function(_){n[_]=t[_],t[_]=e[_]});_=_();return _&&"function"==typeof _.then?_.then(i).catch(i):i(),_}}(()=>{function _(_,e){Object.defineProperty(_RewireAPI__,_,{value:e,enumerable:!1,configurable:!0})}_("__get__",_get__),_("__GetDependency__",_get__),_("__Rewire__",_set__),_("__set__",_set__),_("__reset__",_reset__),_("__ResetDependency__",_reset__),_("__with__",_with__)})();var _default=exports.default=_RewireAPI__; \ No newline at end of file +var _typeof=require("@babel/runtime/helpers/typeof"),actions=(Object.defineProperty(exports,"__esModule",{value:!0}),exports.__ResetDependency__=_reset__,exports.__RewireAPI__=void 0,exports.__set__=exports.__Rewire__=_set__,exports.__GetDependency__=exports.__get__=_get__,exports.default=exports.actions=void 0,exports.actions={get:"get",setupContent:"setupContent",setupSingleIframeContent:"setupSingleIframeContent",setupClickToPay:"setupClickToPay",formData:"formData",formSubmit:"formSubmit",cardBrandChange:"cardBrandChange",setClickToPayTransactionAmount:"setClickToPayTransactionAmount",clickToPayCheckout:"clickToPayCheckout",clickToPayCheckoutSuccess:"clickToPayCheckoutSuccess",clickToPayCheckoutError:"clickToPayCheckoutError",requestIframeExpand:"requestIframeExpand",cancelIframeExpand:"cancelIframeExpand"});function _getGlobalObject(){try{if(global)return global}catch(_){try{if(window)return window}catch(_){return this}}}Object.freeze(_get__("actions"));var _RewireModuleId__=null;function _getRewireModuleId__(){var _;return null===_RewireModuleId__&&((_=_getGlobalObject()).__$$GLOBAL_REWIRE_NEXT_MODULE_ID__||(_.__$$GLOBAL_REWIRE_NEXT_MODULE_ID__=0),_RewireModuleId__=__$$GLOBAL_REWIRE_NEXT_MODULE_ID__++),_RewireModuleId__}function _getRewireRegistry__(){var _=_getGlobalObject();return _.__$$GLOBAL_REWIRE_REGISTRY__||(_.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)),_.__$$GLOBAL_REWIRE_REGISTRY__}function _getRewiredData__(){var _=_getRewireModuleId__(),e=_getRewireRegistry__(),t=e[_];return t||(e[_]=Object.create(null),t=e[_]),t}(()=>{var _=_getGlobalObject();_.__rewire_reset_all__||(_.__rewire_reset_all__=function(){_.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)})})();var INTENTIONAL_UNDEFINED="__INTENTIONAL_UNDEFINED__",_RewireAPI__=exports.__RewireAPI__={};function _get__(_){var e=_getRewiredData__();return void 0===e[_]?_get_original__(_):(e=e[_])!==INTENTIONAL_UNDEFINED?e:void 0}function _get_original__(_){if("actions"===_)return actions}function _assign__(_,e){var t=_getRewiredData__();return void 0===t[_]?_set_original__(_,e):t[_]=e}function _set_original__(_,e){_}function _update_operation__(_,e,t){var r=_get__(e),_="++"===_?r+1:r-1;return _assign__(e,_),t?_:r}function _set__(e,_){var t=_getRewiredData__();return"object"===_typeof(e)?(Object.keys(e).forEach(function(_){t[_]=e[_]}),function(){Object.keys(e).forEach(function(_){_reset__(e)})}):(t[e]=void 0===_?INTENTIONAL_UNDEFINED:_,function(){_reset__(e)})}function _reset__(_){var e=_getRewiredData__();delete e[_],0==Object.keys(e).length&&delete _getRewireRegistry__()[_getRewireModuleId__]}function _with__(e){var t=_getRewiredData__(),r=Object.keys(e),n={};function o(){r.forEach(function(_){t[_]=n[_]})}return function(_){r.forEach(function(_){n[_]=t[_],t[_]=e[_]});_=_();return _&&"function"==typeof _.then?_.then(o).catch(o):o(),_}}(()=>{function _(_,e){Object.defineProperty(_RewireAPI__,_,{value:e,enumerable:!1,configurable:!0})}_("__get__",_get__),_("__GetDependency__",_get__),_("__Rewire__",_set__),_("__set__",_set__),_("__reset__",_reset__),_("__ResetDependency__",_reset__),_("__with__",_with__)})();var _default=exports.default=_RewireAPI__; \ No newline at end of file diff --git a/dist/sdk/api/index.js b/dist/sdk/api/index.js index f7b5cd3..318a7a1 100644 --- a/dist/sdk/api/index.js +++ b/dist/sdk/api/index.js @@ -1 +1 @@ -var merchantId,renderMode,fields,hostedfieldsurl,service,styles,_interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault"),_typeof=require("@babel/runtime/helpers/typeof"),_defineProperty2=(Object.defineProperty(exports,"__esModule",{value:!0}),exports.HostedFields=void 0,exports.__ResetDependency__=_reset__,exports.__RewireAPI__=void 0,exports.__set__=exports.__Rewire__=_set__,exports.__GetDependency__=exports.__get__=_get__,exports.default=void 0,_interopRequireDefault(require("@babel/runtime/helpers/defineProperty"))),_objectWithoutProperties2=_interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")),_actions=require("./actions"),_excluded=["errors"],_excluded2=["errors"];function ownKeys(t,e){var r,_=Object.keys(t);return Object.getOwnPropertySymbols&&(r=Object.getOwnPropertySymbols(t),e&&(r=r.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),_.push.apply(_,r)),_}function _objectSpread(t){for(var e=1;e{var e=_getGlobalObject();e.__rewire_reset_all__||(e.__rewire_reset_all__=function(){e.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)})})();var INTENTIONAL_UNDEFINED="__INTENTIONAL_UNDEFINED__",_RewireAPI__=exports.__RewireAPI__={};function _get__(e){var t=_getRewiredData__();return void 0===t[e]?_get_original__(e):(t=t[e])!==INTENTIONAL_UNDEFINED?t:void 0}function _get_original__(e){switch(e){case"merchantId":return merchantId;case"renderMode":return renderMode;case"hostedfieldsurl":return hostedfieldsurl;case"fields":return fields;case"service":return service;case"styles":return styles;case"callback":return callback;case"onLoadCallback":return onLoadCallback;case"onCardBrandChangeCallback":return onCardBrandChangeCallback;case"autoFocusNext":return autoFocusNext;case"el":return el;case"registerSingleIframe":return registerSingleIframe;case"registerIframes":return registerIframes;case"targets":return targets;case"actions":return _actions.actions;case"initIframe":return initIframe;case"initSingleIframe":return initSingleIframe;case"validateOrigin":return validateOrigin;case"responses":return responses;case"sendCallback":return sendCallback;case"get":return get;case"createIframeProxy":return createIframeProxy;case"window":return window;case"eventHandler":return eventHandler;case"onLoadCounter":return onLoadCounter;case"createSingleIframeProxy":return createSingleIframeProxy;case"setup":return setup;case"reset":return reset}}function _assign__(e,t){var r=_getRewiredData__();return void 0===r[e]?_set_original__(e,t):r[e]=t}function _set_original__(e,t){switch(e){case"merchantId":return merchantId=t;case"renderMode":return renderMode=t;case"hostedfieldsurl":return hostedfieldsurl=t;case"fields":return fields=t;case"service":return service=t;case"styles":return styles=t;case"callback":return callback=t;case"onLoadCallback":return onLoadCallback=t;case"onCardBrandChangeCallback":return onCardBrandChangeCallback=t;case"autoFocusNext":return autoFocusNext=t;case"el":return el=t;case"targets":return targets=t;case"responses":return responses=t;case"onLoadCounter":return onLoadCounter=t}}function _update_operation__(e,t,r){var _=_get__(t),e="++"===e?_+1:_-1;return _assign__(t,e),r?e:_}function _set__(t,e){var r=_getRewiredData__();return"object"===_typeof(t)?(Object.keys(t).forEach(function(e){r[e]=t[e]}),function(){Object.keys(t).forEach(function(e){_reset__(t)})}):(r[t]=void 0===e?INTENTIONAL_UNDEFINED:e,function(){_reset__(t)})}function _reset__(e){var t=_getRewiredData__();delete t[e],0==Object.keys(t).length&&delete _getRewireRegistry__()[_getRewireModuleId__]}function _with__(t){var r=_getRewiredData__(),_=Object.keys(t),n={};function a(){_.forEach(function(e){r[e]=n[e]})}return function(e){_.forEach(function(e){n[e]=r[e],r[e]=t[e]});e=e();return e&&"function"==typeof e.then?e.then(a).catch(a):a(),e}}(()=>{function e(e,t){Object.defineProperty(_RewireAPI__,e,{value:t,enumerable:!1,configurable:!0})}e("__get__",_get__),e("__GetDependency__",_get__),e("__Rewire__",_set__),e("__set__",_set__),e("__reset__",_reset__),e("__ResetDependency__",_reset__),e("__with__",_with__)})();var _default=exports.default=_RewireAPI__; \ No newline at end of file +var merchantId,renderMode,fields,hostedfieldsurl,service,styles,_interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault"),_typeof=require("@babel/runtime/helpers/typeof"),_defineProperty2=(Object.defineProperty(exports,"__esModule",{value:!0}),exports.HostedFields=void 0,exports.__ResetDependency__=_reset__,exports.__RewireAPI__=void 0,exports.__set__=exports.__Rewire__=_set__,exports.__GetDependency__=exports.__get__=_get__,exports.default=void 0,_interopRequireDefault(require("@babel/runtime/helpers/defineProperty"))),_objectWithoutProperties2=_interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")),_actions=require("./actions"),_excluded=["errors"],_excluded2=["errors"];function ownKeys(t,e){var a,r=Object.keys(t);return Object.getOwnPropertySymbols&&(a=Object.getOwnPropertySymbols(t),e&&(a=a.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,a)),r}function _objectSpread(t){for(var e=1;e{var e=_getGlobalObject();e.__rewire_reset_all__||(e.__rewire_reset_all__=function(){e.__$$GLOBAL_REWIRE_REGISTRY__=Object.create(null)})})();var INTENTIONAL_UNDEFINED="__INTENTIONAL_UNDEFINED__",_RewireAPI__=exports.__RewireAPI__={};function _get__(e){var t=_getRewiredData__();return void 0===t[e]?_get_original__(e):(t=t[e])!==INTENTIONAL_UNDEFINED?t:void 0}function _get_original__(e){switch(e){case"merchantId":return merchantId;case"renderMode":return renderMode;case"hostedfieldsurl":return hostedfieldsurl;case"fields":return fields;case"service":return service;case"styles":return styles;case"callback":return callback;case"onLoadCallback":return onLoadCallback;case"onCardBrandChangeCallback":return onCardBrandChangeCallback;case"onRequestIframeExpandCallback":return onRequestIframeExpandCallback;case"onCancelIframeExpandCallback":return onCancelIframeExpandCallback;case"onClickToPayCheckoutSuccessCallback":return onClickToPayCheckoutSuccessCallback;case"onClickToPayCheckoutErrorCallback":return onClickToPayCheckoutErrorCallback;case"autoFocusNext":return autoFocusNext;case"el":return el;case"clickToPayConfig":return clickToPayConfig;case"clickToPayAttributes":return clickToPayAttributes;case"registerSingleIframe":return registerSingleIframe;case"registerIframes":return registerIframes;case"targets":return targets;case"actions":return _actions.actions;case"clickToPayIframe":return clickToPayIframe;case"assertClickToPayIsSet":return assertClickToPayIsSet;case"initIframe":return initIframe;case"initSingleIframe":return initSingleIframe;case"validateOrigin":return validateOrigin;case"responses":return responses;case"sendCallback":return sendCallback;case"recommendedExpandedHeight":return recommendedExpandedHeight;case"get":return get;case"iframeAllowPermissions":return iframeAllowPermissions;case"createIframeProxy":return createIframeProxy;case"handleOnLoad":return handleOnLoad;case"window":return window;case"eventHandler":return eventHandler;case"onLoadCounter":return onLoadCounter;case"createSingleIframeProxy":return createSingleIframeProxy;case"setup":return setup;case"reset":return reset;case"setClickToPayTransactionAmount":return setClickToPayTransactionAmount;case"clickToPayCheckout":return clickToPayCheckout}}function _assign__(e,t){var a=_getRewiredData__();return void 0===a[e]?_set_original__(e,t):a[e]=t}function _set_original__(e,t){switch(e){case"merchantId":return merchantId=t;case"renderMode":return renderMode=t;case"hostedfieldsurl":return hostedfieldsurl=t;case"fields":return fields=t;case"service":return service=t;case"styles":return styles=t;case"callback":return callback=t;case"onLoadCallback":return onLoadCallback=t;case"onCardBrandChangeCallback":return onCardBrandChangeCallback=t;case"onRequestIframeExpandCallback":return onRequestIframeExpandCallback=t;case"onCancelIframeExpandCallback":return onCancelIframeExpandCallback=t;case"onClickToPayCheckoutSuccessCallback":return onClickToPayCheckoutSuccessCallback=t;case"onClickToPayCheckoutErrorCallback":return onClickToPayCheckoutErrorCallback=t;case"autoFocusNext":return autoFocusNext=t;case"el":return el=t;case"clickToPayConfig":return clickToPayConfig=t;case"clickToPayAttributes":return clickToPayAttributes=t;case"targets":return targets=t;case"clickToPayIframe":return clickToPayIframe=t;case"responses":return responses=t;case"onLoadCounter":return onLoadCounter=t}}function _update_operation__(e,t,a){var r=_get__(t),e="++"===e?r+1:r-1;return _assign__(t,e),a?e:r}function _set__(t,e){var a=_getRewiredData__();return"object"===_typeof(t)?(Object.keys(t).forEach(function(e){a[e]=t[e]}),function(){Object.keys(t).forEach(function(e){_reset__(t)})}):(a[t]=void 0===e?INTENTIONAL_UNDEFINED:e,function(){_reset__(t)})}function _reset__(e){var t=_getRewiredData__();delete t[e],0==Object.keys(t).length&&delete _getRewireRegistry__()[_getRewireModuleId__]}function _with__(t){var a=_getRewiredData__(),r=Object.keys(t),n={};function _(){r.forEach(function(e){a[e]=n[e]})}return function(e){r.forEach(function(e){n[e]=a[e],a[e]=t[e]});e=e();return e&&"function"==typeof e.then?e.then(_).catch(_):_(),e}}(()=>{function e(e,t){Object.defineProperty(_RewireAPI__,e,{value:t,enumerable:!1,configurable:!0})}e("__get__",_get__),e("__GetDependency__",_get__),e("__Rewire__",_set__),e("__set__",_set__),e("__reset__",_reset__),e("__ResetDependency__",_reset__),e("__with__",_with__)})();var _default=exports.default=_RewireAPI__; \ No newline at end of file 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-lock.json b/package-lock.json index 6793981..77922c9 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" @@ -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 8faeca9..dde3ded 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "hosted-fields-sdk", - "version": "1.2.8", + "version": "1.3.0", "description": "Hosted fields sdk", "main": "dist/index.js", + "types": "index.d.ts", "module": "dist/esm/index.js", "exports": { ".": { @@ -55,5 +56,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" } } 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..8648580 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, };