@gr4vy/secure-fields 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # v0.1.0 (Fri Aug 12 2022)
2
+
3
+ #### 🚀 Enhancement
4
+
5
+ - ci: add npm publish config [#22](https://github.com/gr4vy/secure-fields/pull/22) ([@douglaseggleton](https://github.com/douglaseggleton))
6
+
7
+ #### 🐛 Bug Fix
8
+
9
+ - ci: add npm release configuration [#16](https://github.com/gr4vy/secure-fields/pull/16) ([@douglaseggleton](https://github.com/douglaseggleton))
10
+ - fix: removing share constant [#15](https://github.com/gr4vy/secure-fields/pull/15) ([@douglaseggleton](https://github.com/douglaseggleton))
11
+ - feat: add field [#13](https://github.com/gr4vy/secure-fields/pull/13) ([@luca-gr4vy](https://github.com/luca-gr4vy))
12
+ - Ta 2262 secure fields frame [#8](https://github.com/gr4vy/secure-fields/pull/8) ([@douglaseggleton](https://github.com/douglaseggleton))
13
+ - feat: utility getters [#4](https://github.com/gr4vy/secure-fields/pull/4) ([@luca-gr4vy](https://github.com/luca-gr4vy))
14
+ - feat: main events class getter [#3](https://github.com/gr4vy/secure-fields/pull/3) ([@luca-gr4vy](https://github.com/luca-gr4vy))
15
+ - task: add base test / lint / scan CI jobs [#2](https://github.com/gr4vy/secure-fields/pull/2) ([@luca-gr4vy](https://github.com/luca-gr4vy))
16
+ - feat: project setup [#1](https://github.com/gr4vy/secure-fields/pull/1) ([@luca-gr4vy](https://github.com/luca-gr4vy))
17
+
18
+ #### Authors: 2
19
+
20
+ - Douglas Eggleton ([@douglaseggleton](https://github.com/douglaseggleton))
21
+ - Luca Allievi ([@luca-gr4vy](https://github.com/luca-gr4vy))
22
+
23
+ ---
24
+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Gr4vy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # Secure Fields
2
+
3
+ ![NPM Version](https://img.shields.io/npm/v/@gr4vy/secure-fields?color=green)
4
+ [![GitHub license](https://img.shields.io/github/license/Naereen/StrapDown.js.svg)](https://github.com/gr4vy/secure-fields/blob/main/LICENSE)
5
+
6
+ Load Secure Fields in your Node app.
7
+
8
+ Use [`@gr4vy/secure-fields-react`](../secure-fields-react) in a React application or [`@gr4vy/secure-fields-cdn`](../secure-fields-cdn) in a regular web application.
9
+
10
+ ## Usage
11
+
12
+ Via the command line, install this package as follows.
13
+
14
+ ```bash
15
+ npm install @gr4vy/secure-fields --save-prod
16
+ # yarn add @gr4vy/secure-fields --save
17
+ ```
18
+
19
+ ## Get started
20
+
21
+ To use Secure Fields, you first need a form with placeholder elements that will be replaced by secure fields (`iframe`s), each with a specific `id` attribute. Get a session id by making a request to the Session API, passing one or more accepted `targetOrigins` (urls that host your form).
22
+
23
+ ```html
24
+ <form id="cc-form">
25
+ <label for="cc-holder-name">Name</label>
26
+ <input
27
+ id="cc-holder-name"
28
+ class="form-control"
29
+ placeholder="Name on the card"
30
+ />
31
+ <label for="cc-number">Card Number</label>
32
+ <input id="cc-number" class="form-control" />
33
+ <label for="cc-security-code">Security Code</label>
34
+ <input id="cc-security-code" class="form-control" />
35
+ <label for="cc-expiration-date">Expiration date</label>
36
+ <input id="cc-expiration-date" class="form-control" />
37
+ <button id="pay-button">Pay now</button>
38
+ </form>
39
+ ```
40
+
41
+ Import the library and call the class constructor to create an instance. Pass the session id returned by the Session API.
42
+
43
+ ```js
44
+ const { SecureFields } = require(`@gr4vy/secure-fields`)
45
+ // import { SecureFields } from (`@gr4vy/secure-fields`)
46
+
47
+ const secureFields = new SecureFields([SESSION_ID])
48
+
49
+ > **Note**: Replace `[SESSION_ID]` with the session id.
50
+ ```
51
+
52
+ Call methods to add fields, set flags, and attach event listeners. Call `submit` to capture the data and send it to Gr4vy, passing an optional callback to handle other UI interactions.
53
+
54
+ ```js
55
+ secureFields.setDebug(true)
56
+
57
+ const cardNumber = secureFields.addCardNumber('#cc-number', {
58
+ placeholder: 'Enter card number',
59
+ styles: {
60
+ base: {
61
+ color: 'black',
62
+ fontSize: '18px',
63
+ },
64
+ },
65
+ })
66
+
67
+ secureFields.addEventListener(
68
+ SecureFields.Events.CARD_VAULT_SUCCESS,
69
+ (data) => {
70
+ console.log(data) // data.card.number, data.card.type...
71
+ }
72
+ )
73
+
74
+ const form = document.getElementById('cc-form')
75
+ form.addEventListener('submit', (e) => {
76
+ e.preventDefault()
77
+ secureFields.submit((err, data) => {
78
+ if (err) {
79
+ throw new Error(err)
80
+ }
81
+
82
+ console.log(`Got tokenized data`, data)
83
+ })
84
+ })
85
+ ```
86
+
87
+ ### Styles
88
+
89
+ The outer styling (any containers around fields, etc) is completely in the your control. SecureFields will also generate a set of so-called managed CSS classes and attach them to the outer iframe containers, so you can target them with your own CSS. Finally, an object of CSS rules can be passed to the `addField` options to style elements inside the iframe(s).
90
+
91
+ ```
92
+ const styles = {
93
+ // Default styles
94
+ base: { ... },
95
+ // Rules applied when the field has its value autofilled by a browser / extension
96
+ autofill: { ... },
97
+ // Rules applied when the field is hovered
98
+ hover: { ... },
99
+ // Rules applied when the field is focused
100
+ focus: { ... },
101
+ // Rules applied when the field is disabled
102
+ disabled: { ... },
103
+ // Rules applied when the field is valid
104
+ valid: { ... },
105
+ // Rules applied when the field is invalid
106
+ invalid: { ... },
107
+ // Rules applied to the field placeholder
108
+ placeholder: { ... },
109
+ }
110
+ ```
111
+
112
+ We only allow the following CSS properties:
113
+
114
+ ```
115
+ backgroundColor
116
+ caretColor
117
+ color
118
+ colorScheme
119
+ cursor
120
+ font
121
+ fontFamily
122
+ fontFeatureSettings
123
+ fontKerning
124
+ fontSize
125
+ fontSizeAdjust
126
+ fontStretch
127
+ fontStyle
128
+ fontVariant
129
+ fontVariantAlternates
130
+ fontVariantCaps
131
+ fontVariantEastAsian
132
+ fontVariantLigatures
133
+ fontVariantNumeric
134
+ fontVariationSettings
135
+ fontWeight
136
+ letterSpacing
137
+ lineHeight
138
+ opacity
139
+ textAlign
140
+ textShadow
141
+ textRendering
142
+ transition
143
+ MozOsxFontSmoothing
144
+ WebkitFontSmoothing
145
+ ```
146
+
147
+ Here are the managed classes generated by Secure Fields:
148
+
149
+ ```
150
+ /* Applied to the (form) container */
151
+ .secure-fields {}
152
+ /* Applied to each field */
153
+ .secure-fields__field {}
154
+ /* Applied to a disabled field */
155
+ .secure-fields__field--disabled {}
156
+ /* Applied to a focused field */
157
+ .secure-fields__field--focused {}
158
+ /* Applied to a valid field */
159
+ .secure-fields__field--valid {}
160
+ /* Applied to an invalid field */
161
+ .secure-fields__field--invalid {}
162
+ /* Applied to a autofilled field */
163
+ .secure-fields__field--autofilled {}
164
+ ```
165
+
166
+ ### Events
167
+
168
+ You can call the `addEventListener` on a Secure Fields instance or on fields, passing one of the supported events and a callback. Here are the events you can listen to on an instance:
169
+
170
+ | Name | Description |
171
+ | -------------------- | --------------------------------------------------------------- |
172
+ | `CARD_VAULT_SUCCESS` | Triggered when the card is successfully vaulted. |
173
+ | `CARD_VAULT_FAILURE` | Triggered when the card vaulting fails. |
174
+ | `READY` | Triggered when the SecureFields is loaded and ready to be used. |
175
+
176
+ For actual fields, you can subscribe to default events and have access to more useful data (bin, validation status, etc). For example, the change event on a card number input might include `{ valid: true, complete: true, ... }`. You can expect to be able to listen to events such as `blur`, `focus`, `change`, `input`...
177
+
178
+ ### API reference
179
+
180
+ #### Getters
181
+
182
+ | Name | Description |
183
+ | --------- | ------------------------------------------------------------------------------- |
184
+ | `Events` | Gives access to the supported events. <br /><br />`SecureFields.Events.READY` |
185
+ | `version` | Returns the current version of the instance. <br /><br />`SecureFields.version` |
186
+
187
+ #### Methods
188
+
189
+ | Name | Description |
190
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
191
+ | `constructor` | Instantiates SecureFields with a session id. <br /><br />`new SecureFields('...')` |
192
+ | `addCardNumberField` | Injects a secure field of type `cardNumber`. <br /><br />`secureFields.addCardNumberField('#cc-number', { placeholder: 'Enter card number', ... })` |
193
+ | `addSecurityCodeField` | Injects a secure field of type `securityCode`. <br /><br />`secureFields.addSecurityCodeField('#cc-security-code', { placeholder: 'Enter security code', ... })` |
194
+ | `addExpirationDateField` | Injects a secure field of type `expirationDate`. <br /><br />`secureFields.addExpirationDateField('#cc-expiration-date', { placeholder: 'Enter expiration date', ... })` |
195
+ | `addEventListener` | Attaches an event handler to the SecureFields instance or to a secure field in order to listen to specific events. Requires one of the events supported and a callback. <br /><br />`secureFields.addEventListener(SecureFields.Events.READY, (data) => { ... })` <br /><br />`cardNumber.element.addEventListener(SecureFields.Events.CHANGE, (data) => { ... })` |
196
+ | `removeEventListener` | Removes a previously attached event handler. |
197
+ | `submit` | Calls the Vault API to tokenize and store the card data. Returns an error with invalid fields or the tokenized data in case everything went fine. <br /><br />`secureFields.submit((err, data) => { ... }` |
198
+ | `setDebug` | Enable / disable debug mode. When the debug mode is enabled, SecureFields logs information to the console. <br /><br />`secureFields.setDebug(true)` |
199
+ | `isFormValid` | Return the state of the card form validation at any time. <br /><br />`secureFields.isFormValid()` |
package/esm/index.js ADDED
@@ -0,0 +1 @@
1
+ export { SecureFields } from '../lib/index.js'
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,12 @@
1
+ export declare enum Events {
2
+ CARD_VAULT_SUCCESS = "card-vault-success",
3
+ CARD_VAULT_FAILURE = "card-vault-failure",
4
+ READY = "ready"
5
+ }
6
+ export declare enum FieldAttributes {
7
+ AUTOFILLED = "data-secure-fields-autofilled",
8
+ DISABLED = "data-secure-fields-disabled",
9
+ FOCUSED = "data-secure-fields-focused",
10
+ VALID = "data-secure-fields-valid"
11
+ }
12
+ export declare const MESSAGE_CHANNEL = "secure-fields";
package/lib/index.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ import { Events } from './constants';
2
+ import type { Config, Field } from './types';
3
+ declare class SecureFields {
4
+ config: Config;
5
+ controller: HTMLIFrameElement;
6
+ frameUrl: string;
7
+ apiUrl: string;
8
+ parentOrigin: string;
9
+ /**
10
+ * CARD_VAULT_SUCCESS: Triggered when the card is successfully vaulted.
11
+ *
12
+ * CARD_VAULT_FAILURE: Triggered when the card vaulting fails.
13
+ *
14
+ * READY: Triggered when the SecureFields is loaded and ready to be used.
15
+ */
16
+ static get Events(): typeof Events;
17
+ static get version(): string;
18
+ constructor(config: Config);
19
+ private _addField;
20
+ addCardNumberField(element: string | HTMLElement, options: Omit<Field, 'element' | 'type'>): {
21
+ element: HTMLElement;
22
+ type: "number" | "securityCode" | "expirationDate";
23
+ placeholder?: string;
24
+ styles?: import("./types").Styles;
25
+ };
26
+ addSecurityCodeField(element: string | HTMLElement, options: Omit<Field, 'element' | 'type'>): {
27
+ element: HTMLElement;
28
+ type: "number" | "securityCode" | "expirationDate";
29
+ placeholder?: string;
30
+ styles?: import("./types").Styles;
31
+ };
32
+ addExpirationDateField(element: string | HTMLElement, options: Omit<Field, 'element' | 'type'>): {
33
+ element: HTMLElement;
34
+ type: "number" | "securityCode" | "expirationDate";
35
+ placeholder?: string;
36
+ styles?: import("./types").Styles;
37
+ };
38
+ addEventListener(event: keyof typeof Events, callback: () => void): void;
39
+ removeEventListener(event: keyof typeof Events, callback: () => void): void;
40
+ submit(callback: (error: string) => void): void;
41
+ setDebug(debug: boolean): void;
42
+ isFormValid(): void;
43
+ }
44
+ export { SecureFields };
45
+ export type { Field } from './types';
package/lib/index.js ADDED
@@ -0,0 +1 @@
1
+ !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var r in n)("object"==typeof exports?exports:e)[r]=n[r]}}(this,(()=>(()=>{"use strict";var e,t,n={d:(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},r={};function o(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter((function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable})))),r.forEach((function(t){i(e,t,n[t])}))}return e}function a(e,t){return t=null!=t?t:{},Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):function(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n.push.apply(n,r)}return n}(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))})),e}n.r(r),n.d(r,{SecureFields:()=>s}),function(e){e.CARD_VAULT_SUCCESS="card-vault-success",e.CARD_VAULT_FAILURE="card-vault-failure",e.READY="ready"}(e||(e={})),function(e){e.AUTOFILLED="data-secure-fields-autofilled",e.DISABLED="data-secure-fields-disabled",e.FOCUSED="data-secure-fields-focused",e.VALID="data-secure-fields-valid"}(t||(t={}));var s=function(){function n(e){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),this.config=c({environment:"production"},e);var t="sandbox"===this.config.environment?"sandbox.":"";this.frameUrl=process.env.SECURE_FIELDS_FRAME_URL||"https://secure-fields.".concat(t).concat(this.config.gr4vyId,".gr4vy.app"),this.parentOrigin=window.location.origin,this.controller=document.createElement("iframe"),this.controller.src="".concat(this.frameUrl,"/controller.html?parentOrigin=").concat(this.parentOrigin,"&sessionId=").concat(this.config.sessionId,"&gr4vyId=").concat(this.config.gr4vyId,"&environment=").concat(this.config.environment),this.controller.style.position="absolute",this.controller.style.left="-9999999px",document.body.appendChild(this.controller)}var r,i,s=n.prototype;return s._addField=function(e,n){var r=this;e="string"==typeof e?document.querySelector(e):e;var o=document.createElement("iframe");o.id=n.type,o.src="".concat(this.frameUrl,"/input.html?parentOrigin=").concat(this.parentOrigin,"&placeholder=").concat(n.placeholder,"&type=").concat(n.type),o.style.height="48px",o.style.border="none",o.style.width="100%";var i=document.createElement("div");return i.appendChild(o),e.parentNode.replaceChild(i,e),window.addEventListener("message",(function(e){if(e.origin===r.frameUrl&&e.data.data.id===n.type)switch(e.data.type){case"blur":i.removeAttribute(t.FOCUSED);break;case"focus":i.setAttribute(t.FOCUSED,"")}})),a(c({},n),{element:e})},s.addCardNumberField=function(e,t){return this._addField(e,a(c({},t),{type:"number"}))},s.addSecurityCodeField=function(e,t){return this._addField(e,a(c({},t),{type:"securityCode"}))},s.addExpirationDateField=function(e,t){return this._addField(e,a(c({},t),{type:"expirationDate"}))},s.addEventListener=function(e,t){console.log(e,t)},s.removeEventListener=function(e,t){console.log(e,t)},s.submit=function(e){var t=this,n=function(r){if(r.origin===t.frameUrl&&"secure-fields"===r.data.channel){switch(r.data.type){case"success":e(null);break;case"error":e("Failed to update checkout session")}window.removeEventListener("message",n)}};window.addEventListener("message",n),this.controller.contentWindow.postMessage("submit",this.frameUrl)},s.setDebug=function(e){console.log(e)},s.isFormValid=function(){},r=n,i=[{key:"Events",get:function(){return e}},{key:"version",get:function(){}}],null&&o(r.prototype,null),i&&o(r,i),n}();return r})()));
package/lib/types.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type * as CSS from 'csstype';
2
+ export declare type Config = {
3
+ environment?: 'sandbox' | 'production';
4
+ gr4vyId: string;
5
+ sessionId: string;
6
+ };
7
+ export declare type Field = {
8
+ type: 'number' | 'securityCode' | 'expirationDate';
9
+ placeholder?: string;
10
+ styles?: Styles;
11
+ };
12
+ export declare type Styles = {
13
+ [key in 'base' | 'autofill' | 'hover' | 'focus' | 'disabled' | 'valid' | 'invalid' | 'placeholder']: Pick<CSS.Properties, 'backgroundColor' | 'caretColor' | 'color' | 'colorScheme' | 'cursor' | 'font' | 'fontFamily' | 'fontFeatureSettings' | 'fontKerning' | 'fontSize' | 'fontSizeAdjust' | 'fontStretch' | 'fontStyle' | 'fontVariant' | 'fontVariantAlternates' | 'fontVariantCaps' | 'fontVariantEastAsian' | 'fontVariantLigatures' | 'fontVariantNumeric' | 'fontVariationSettings' | 'fontWeight' | 'letterSpacing' | 'lineHeight' | 'opacity' | 'textAlign' | 'textShadow' | 'textRendering' | 'transition' | 'MozOsxFontSmoothing' | 'WebkitFontSmoothing'>;
14
+ };
15
+ export declare type VaultSessionResponse = {
16
+ type: 'vault-session';
17
+ id: string;
18
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@gr4vy/secure-fields",
3
+ "version": "0.1.0",
4
+ "description": "Gr4vy-hosted secure fields offering advanced theming, PCI compliance, event handling, and more.",
5
+ "main": "lib/index",
6
+ "types": "lib/index",
7
+ "author": "Gr4vy <code@gr4vy.com>",
8
+ "license": "MIT",
9
+ "repository": "https://github.com/gr4vy/secure-fields/tree/main/packages/secure-fields",
10
+ "homepage": "https://gr4vy.com",
11
+ "exports": {
12
+ "require": "./lib/index.js",
13
+ "import": "./esm/index.js"
14
+ },
15
+ "files": [
16
+ "esm",
17
+ "lib",
18
+ "LICENSE",
19
+ "README",
20
+ "CHANGELOG.md"
21
+ ],
22
+ "nx": {
23
+ "targets": {
24
+ "build": {
25
+ "outputs": [
26
+ "{projectRoot}/lib"
27
+ ]
28
+ }
29
+ }
30
+ },
31
+ "scripts": {
32
+ "build": "tsc && webpack --config webpack.prod.js",
33
+ "clean": "rm -rf lib *.tgz",
34
+ "dev": "webpack serve --config webpack.dev.js",
35
+ "docs": "typedoc --plugin typedoc-plugin-missing-exports",
36
+ "lint": "eslint src --ext .ts",
37
+ "prebuild": "yarn clean",
38
+ "prepack": "yarn build",
39
+ "test": "jest --colors"
40
+ },
41
+ "devDependencies": {
42
+ "csstype": "^3.1.0",
43
+ "typedoc": "^0.23.5",
44
+ "typedoc-plugin-missing-exports": "^0.23.0"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "gitHead": "c8e8a5d43d547fe325b18e2e294862ae1b29f6e1"
50
+ }