Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
skyflow-react-js
Advanced tools
A React wrapper for Skyflow JS SDK
Using npm
npm install --save skyflow-react-js
React components are wrapped in Skyflow provider which takes in config object and SDK internally initializes a Skyflow client.
import {SkyflowElements, LogLevel, Env} from 'skyflow-react-js';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
const config = {
vaultID: 'string', // Id of the vault that the client should connect to.
vaultURL: 'string', // URL of the vault that the client should connect to.
getBearerToken: helperFunc, // Helper function that retrieves a Skyflow bearer token from your backend.
options: {
logLevel: LogLevel.DEBUG, // Optional, if not specified default is ERROR.
env: Env.DEV, // Optional, if not specified default is PROD.
},
};
root.render(
<SkyflowElements config={config}>
<App />
</SkyflowElements>
);
For the getBearerToken
parameter, pass in a helper function that retrieves a Skyflow bearer token from your backend. This function will be invoked when the SDK needs to insert or retrieve data from the vault. A sample implementation is shown below:
For example, if the response of the consumer tokenAPI is in the below format
{
'accessToken': string,
'tokenType': string
}
then, your getBearerToken Implementation should be as below
const getBearerToken = () => {
return new Promise((resolve, reject) => {
const Http = new XMLHttpRequest()
Http.onreadystatechange = () => {
if (Http.readyState === 4) {
if (Http.status === 200) {
const response = JSON.parse(Http.responseText)
resolve(response.accessToken)
} else {
reject('Error occured')
}
}
}
Http.onerror = (error) => {
reject('Error occured')
}
const url = 'https://api.acmecorp.com/skyflowToken'
Http.open('GET', url)
Http.send()
})
}
For logLevel
parameter, there are 4 accepted values in LogLevel
DEBUG
When LogLevel.DEBUG
is passed, all level of logs will be printed(DEBUG, INFO, WARN, ERROR).
INFO
When LogLevel.INFO
is passed, INFO logs for every event that has occurred during the SDK flow execution will be printed along with WARN and ERROR logs.
WARN
When LogLevel.WARN
is passed, WARN and ERROR logs will be printed.
ERROR
When LogLevel.ERROR
is passed, only ERROR logs will be printed.
Note
:
logLevel
is optional, by default the logLevel will be ERROR
.For env
parameter, there are 2 accepted values in Env
PROD
DEV
In Event Listeners, actual value of element can only be accessed inside the handler when the env
is set to DEV
.
Note
:
env
is optional, by default the env will be PROD
.env
option with caution, make sure the env is set to PROD
when using skyflow-react-js
in production.Skyflow Elements provide developers with pre-built form elements to securely collect sensitive data client-side. These elements are hosted by Skyflow and injected into your web page as iFrames. This reduces your PCI compliance scope by not exposing your front-end application to sensitive data. Follow the steps below to securely collect data with Skyflow Elements.
First create a container for the form elements using the useCollectContainer
hook as shown below:
const container = useCollectContainer()
import { CardNumberElement} from 'skyflow-react-js';
<CardNumberElement
table='<TABLE_NAME>'
container='<CONTAINER>'
column='<COLUMN_NAME>'
… props
/>
The following props
can be passed to Skyflow collect Element:
{
container: 'CollectContainer' // Required, the collect container.
table: 'string', // Required, the table this data belongs to.
column: 'string', // Required, the column into which this data should be inserted.
id: string, // Optional, id that can passed to the element.
classes: {}, // Optional, styles that should be applied to the element.
label: 'string', // Optional, label for the form element.
placeholder: 'string', // Optional, placeholder for the form element.
validations: [], // Optional, array of validation rules.
options: {}, // Optional, options that can be passed to an element.
onChange: Function, // Optional, function that is passed to trigger the onChange event.
onFocus: Function, // Optional, function that is passed to trigger the onFocus event.
onBlur: Function, // Optional, function that is passed to trigger the onBlur event.
onReady: Function, // Optional, function that is passed to trigger the onReady event.
}
The table
and column
fields indicate which table and column in the vault the Element corresponds to.
Note:
address.street.line1
)All elements can be styled using JSS syntax.
An example of styling an element with makeSkyflowStyles
hook :
import { useMakeSkyflowStyles } from 'skyflow-react-js'
const useSkyflowStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
color: '#013370',
fontFamily: '"Roboto", sans-serif'
// ...otherStyles
},
complete: {
color: '#4caf50',
},
empty: {},
focus: {},
invalid: {},
cardIcon: {
position: 'absolute',
left: '8px',
bottom: 'calc(50% - 12px)',
},
copyIcon: {
position: 'absolute',
right: '8px',
},
global: {
'@import': 'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
}
},
labelStyles: {
base: {
color: '#0D4370',
// ...otherStyles
},
},
errorTextStyles: {
base: {
color: '#f44336',
// ...otherStyles
},
},
})
The inputStyles
field accepts a style object which consists of CSS properties that should be applied to the form element in the following states:
base
: all other variants inherit from these styles.complete
: applied when the Element has valid input.empty
: applied when the Element has no input.focus
: applied when the Element has focus.invalid
: applied when the Element has invalid input.cardIcon
: applied to the card type icon in CARD_NUMBER
Element.copyIcon
: applied to copy icon in Elements when enableCopy
option is true.global
: used for global styles like font-family.The states that are available for labelStyles
are base
, focus
, global
and requiredAsterisk
.
requiredAsterisk
: styles applied for the Asterisk symbol in the label.An example of a labelStyles object:
labelStyles: {
base: {
fontSize: '12px',
fontWeight: 'bold'
},
focus: {
color: '#1d1d1d'
},
global: {
'@import' :'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
},
requiredAsterisk:{
color: 'red'
}
}
The state that is available for errorTextStyles
are base
and global
, it shows up when there is some error in the collect element.
An example of a errorTextStyles object:
errorTextStyles: {
base: {
color: '#f44336',
fontFamily: '"Roboto", sans-serif'
},
global: {
'@import': 'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
}
}
We support the following collect elements in the react SDK:
CardHolderNameElement
CardNumberElement
ExpirationDateElement
CVVElement
PinElement
ExpirationDateElement
ExpirationMonthElement
ExpirationYearElement
InputFieldElement
FileInputElement
The InputFieldElement type is a custom UI element without any built-in validations. See the section on validations for more information on validations.
Along with Collect Element we can define other options which takes a object of optional parameters as described below:
const options = {
required: false, // Optional, indicates whether the field is marked as required. Defaults to 'false'.
enableCardIcon: true, // Optional, indicates whether card icon should be enabled (only applicable for CARD_NUMBER ElementType).
format: String, // Optional, format for the element (only applicable currently for EXPIRATION_DATE ElementType).
enableCopy: false, // Optional, enables the copy icon in collect and reveal elements to copy text to clipboard. Defaults to 'false').
}
required
parameter indicates whether the field is marked as required or not. If not provided, it defaults to false
enableCardIcon
parameter indicates whether the icon is visible for the CARD_NUMBER element, defaults to true
format
parameter takes string value and indicates the format pattern applicable to the element type, It's currently only applicable to EXPIRATION_DATE
and EXPIRATION_YEAR
element types.
enableCopy
parameter indicates whether the copy icon is visible in collect and reveal elements.
The values that are accepted for EXPIRATION_DATE
are
MM/YY
(default)MM/YYYY
YY/MM
YYYY/MM
The values that are accepted for EXPIRATION_YEAR
are
YY
(default)YYYY
NOTE
: If not specified or invalid value is passed to the format
then it takes default value.
When the form is ready to be submitted, call the collect(options?)
method on the container object. The options
parameter takes a object of optional parameters as shown below:
tokens
: indicates whether tokens for the collected data should be returned or not. Defaults to 'true'additionalFields
: Non-PCI elements data to be inserted into the vault which should be in the records
object format.upsert
: To support upsert operations while collecting data from Skyflow elements, pass the table and column marked as unique in the table.const options = {
tokens: true, // Optional, indicates whether tokens for the collected data should be returned. Defaults to 'true'
additionalFields: {
records: [
{
table: 'string', // Table into which record should be inserted
fields: {
column1: 'value', // Column names should match vault column names
// ...additional fields here
},
},
// ...additional records here
],
}, // Optional
upsert: [
// Upsert operations support in the vault
{
table: 'string', // Table name
column: 'value', // Unique column in the table
},
], //Optional
}
container.collect(options)
import React from 'react'
import { CardNumberElement, useCollectContainer, useMakeSkyflowStyles } from 'skyflow-react-js'
const App = () => {
const container = useCollectContainer()
const useStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
border: '1px solid black',
borderRadius: '4px',
color: '#1d1d1d',
padding: '10px 16px',
},
complete: {
color: '#4caf50',
},
empty: {},
focus: {},
invalid: {
color: '#f44336',
},
cardIcon: {
position: 'absolute',
left: '8px',
bottom: 'calc(50% - 12px)',
},
},
labelStyles: {
base: {
fontSize: '16px',
fontWeight: 'bold',
},
},
errorTextStyles: {
base: {
color: 'blue',
},
},
})
const options = {
enableCopy: true,
}
const classes = useStyles()
const handleCollect = () => {
const response = container.collect()
response
.then((res: unknown) => {
console.log(JSON.stringify(res))
})
.catch((e: unknown) => {
console.log(e)
})
}
return (
<div className='App'>
<header className='App-header'>
<CardNumberElement
container={container}
table={'cards'}
classes={classes}
column={'cardNumber'}
label={'Collect Card Number'}
options={options}
/>
<button onClick={handleCollect}>Collect</button>
</header>
</div>
)
}
export default App
Sample Response :
{
"records": [
{
"table": "cards",
"fields": {
"cardNumber": "f3907186-e7e2-466f-91e5-48e12c2bcbc1",
}
}
]
}
import React from 'react'
import {
CardNumberElement,
CVVElement,
useCollectContainer,
useMakeSkyflowStyles,
} from 'skyflow-react-js'
function App() {
const container = useCollectContainer()
const useStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
border: '1px solid black',
borderRadius: '4px',
color: '#1d1d1d',
padding: '10px 16px',
},
complete: {
color: '#4caf50',
},
empty: {},
focus: {},
invalid: {
color: '#f44336',
},
},
labelStyles: {
base: {
fontSize: '16px',
fontWeight: 'bold',
},
},
errorTextStyles: {
base: {
color: 'blue',
},
},
})
const classes = useStyles()
const handleCollect = () => {
const options = {
tokens: true,
upsert: [
{
table: 'cards',
column: 'cardNumber',
},
],
}
const response = container.collect(options)
response
.then((res: any) => {
console.log(JSON.stringify(res))
})
.catch((e: any) => {
console.log(e)
})
}
return (
<div className='App'>
<header className='App-header'>
<CardNumberElement
container={container}
table={'cards'}
classes={classes}
column={'cardNumber'}
label={'Collect Card Number'}
options={options}
/>
<CVVElement
container={container}
table={'cards'}
classes={classes}
column={'cvv'}
label={'Collect CVV'}
options={options}
/>
<button onClick={handleCollect}>Collect</button>
</header>
</div>
)
}
export default App
Skyflow returns tokens for the record you just inserted.
{
"records": [
{
"table": "cards",
"fields": {
"cardNumber": "f3907186-e7e2-466f-91e5-48e12c2bcbc1",
"cvv": "l4907186-e7e2-466f-91e5-985e12c2bcbc1"
}
}
]
}
Skyflow-React which internally uses Skyflow-JS SDK provides two types of validations on Collect Elements
Every Collect Element except of type InputFieldElement
has a set of default validations listed below:
CARD_NUMBER
: Card number validation with checkSum algorithm(Luhn algorithm).
Available card lengths for defined card types are [12, 13, 14, 15, 16, 17, 18, 19].
A valid 16 digit card number will be in the format - XXXX XXXX XXXX XXXX
CARD_HOLDER_NAME
: Name should be 2 or more symbols, valid characters should match pattern - ^([a-zA-Z\\ \\,\\.\\-\\']{2,})$
CVV
: Card CVV can have 3-4 digitsEXPIRATION_DATE
: Any date starting from current month. By default valid expiration date should be in short year format - MM/YY
PIN
: Can have 4-12 digitsCustom validations can be added to any element which will be checked after the default validations have passed. The following Custom validation rules are currently supported:
REGEX_MATCH_RULE
: You can use this rule to specify any Regular Expression to be matched with the input field valueconst regexMatchRule = {
type: REGEX_MATCH_RULE,
params: {
regex: RegExp,
error: string, // Optional, default error is 'VALIDATION FAILED'.
},
}
LENGTH_MATCH_RULE
: You can use this rule to set the minimum and maximum permissible length of the input field valueconst lengthMatchRule = {
type: LENGTH_MATCH_RULE,
params: {
min: number, // Optional
max: number, // Optional
error: string, // Optional, default error is 'VALIDATION FAILED'.
},
}
The Sample for using custom validations:
/*
A simple example that illustrates custom validations.
Adding REGEX_MATCH_RULE , LENGTH_MATCH_RULE to collect element.
*/
import {CardNumberElement, REGEX_MATCH_RULE, LENGTH_MATCH_RULE} from 'skyflow-react-js';
// This rule allows 1 or more alphabets.
const alphabetsOnlyRegexRule = {
type: REGEX_MATCH_RULE,
params: {
regex: /^[A-Za-z]+$/,
error: 'Only alphabets are allowed'
}
};
// This rule allows input length between 4 and 6 characters.
const lengthRule = {
type: LENGTH_MATCH_RULE,
params: {
min: 4,
max: 6,
error: 'Must be between 4 and 6 alphabets'
}
};
const Form = (props) => {
return (
<CardNumberElement
container='COLLECT CONTAINER'
table='<TABLE_NAME>'
column='<COLUMN_NAME>'
validations={[alphabetsOnlyRegexRule, lengthRule]}
...props
/>
);
};
Helps to communicate with Skyflow elements / iframes by listening to an event. Event listeners can be triggered by passing the handler methods as props to the Element components.
There are 4 events which SDK supports:
CHANGE
Change event is triggered when the Element's value changes.
READY
Ready event is triggered when the Element is fully rendered
FOCUS
Focus event is triggered when the Element gains focus
BLUR
Blur event is triggered when the Element loses focus.
The handler function(state) => void
is a callback function you provide, that will be called when the event is fired with the state object as shown below.
state: {
elementType: Skyflow.ElementType
isEmpty: boolean
isFocused: boolean
isValid: boolean
value: string
}
Note:
Values of SkyflowElements will be returned in element state object only when env
is DEV
, else it is empty string i.e, '', but in case of CARD_NUMBER type element when the env
is PROD
for all the card types except AMEX, it will return first eight digits, for AMEX it will return first six digits and rest all digits in masked format.
import React from 'react'
import { CardNumberElement, CardHolderNameElement, useCollectContainer } from 'skyflow-react-js'
const App = () => {
const container = useCollectContainer()
const handleCollect = () => {
const response = container.collect()
response
.then((res: unknown) => {
console.log(JSON.stringify(res))
})
.catch((e: unknown) => {
console.log(e)
})
}
const handleOnChange = (changeState: unknown) => {
console.log('Change', changeState)
}
const handleOnBlur = (changeState: unknown) => {
console.log('Blur', changeState)
}
return (
<div className='App'>
<header className='App-header'>
<CardNumberElement
container={container}
table={'table1'}
column={'card_number'}
label={'Collect Card Number'}
onChange={handleOnChange}
onBlur={handleOnBlur}
/>
<CardHolderNameElement
container={container}
table={'table1'}
column={'first_name'}
label={'Collect Card Holder Name'}
onChange={handleOnChange}
onBlur={handleOnBlur}
/>
<button onClick={handleCollect}>Collect</button>
</header>
</div>
)
}
export default App
env
is DEV
{
elementType: 'CARDHOLDER_NAME',
isEmpty: false,
isFocused: true,
isValid: true,
value: 'John',
};
{
elementType: 'CARD_NUMBER',
isEmpty: false,
isFocused: true,
isValid: true,
value: '4111111111111111',
};
env
is PROD
{
elementType: 'CARDHOLDER_NAME',
isEmpty: false,
isFocused: true,
isValid: true,
value: '',
};
{
elementType: 'CARD_NUMBER',
isEmpty: false,
isFocused: true,
isValid: true,
value: '41111111XXXXXXXX',
};
You can upload binary files to a vault using the Skyflow File Input Element. Use the following steps to securely upload a file.
First create a container for the form elements using the useCollectContainer
hook as shown below:
const container = useCollectContainer()
import { FileInputElement} from 'skyflow-react-js';
<FileInputElement
table='<TABLE_NAME>'
column='<COLUMN_NAME>'
skyflowID='<SKYFLOW_ID>'
... props
/>
The following props
can be passed to file input element:
{
container: 'CollectContainer' // Required, the collect container.
table: 'string', // Required, the table this data belongs to.
column: 'string', // Required, the column into which this data should be inserted.
skyflowID: 'string', //Required, skyflowID of the record that stores the file.
id: 'string', // Optional, id that can passed to the element.
classes: {}, // Optional, styles that should be applied to the element.
label: 'string', // Optional, label for the form element.
validations: [], // Optional, array of validation rules.
options: {}, // Optional, options that can be passed to an element.
}
The table
and column
fields indicate which table and column the Element corresponds to.
skyflowID
indicates the record that stores the file.
Notes:
skyflowID
is required while creating File elementaddress.street.line1
).When the file is ready to be uploaded, call the uploadFiles()
method on the container object.
container.uploadFiles(options);
import React from 'react'
import { FileInputElement , useCollectContainer, useMakeSkyflowStyles } from 'skyflow-react-js'
const App = () => {
const container = useCollectContainer()
const useStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
border: '1px solid black',
borderRadius: '4px',
color: '#1d1d1d',
padding: '10px 16px',
},
complete: {
color: '#4caf50',
},
empty: {},
focus: {},
invalid: {
color: '#f44336',
},
cardIcon: {
position: 'absolute',
left: '8px',
bottom: 'calc(50% - 12px)',
},
},
labelStyles: {
base: {
fontSize: '16px',
fontWeight: 'bold',
},
},
errorTextStyles: {
base: {
color: 'blue',
},
},
})
const classes = useStyles()
const handleUpload = () => {
const response = container.uploadFiles({})
response
.then((res: unknown) => {
console.log(JSON.stringify(res))
})
.catch((e: unknown) => {
console.log(e)
})
}
return (
<div className='App'>
<header className='App-header'>
<FileInputElement
container={container}
table={'newTable'}
skyflowID={'431eaa6c-5c15-4513-aa15-29f50babe882'}
column={'file_input'}
label={'File Input'}
classes={classes}
/>
<button onClick={handleUpload}>upload File</button>
</header>
</div>
)
}
export default App
Sample Response :
{
fileUploadResponse: [
{
"skyflow_id": "431eaa6c-5c15-4513-aa15-29f50babe882"
}
]
}
import React from 'react';
import {
CardNumberElement,
useCollectContainer,
useMakeSkyflowStyles,
FileInputElement,
} from 'skyflow-react-js';
const CollectElements = () => {
const container = useCollectContainer();
const handleCollect = () => {
const response = container.collect();
response
.then((res: unknown) => {
console.log(JSON.stringify(res));
})
.catch((e: unknown) => {
console.log(e);
});
};
const handleFile = () => {
const response = container.uploadFiles({});
response
.then((res: unknown) => {
console.log(JSON.stringify(res));
})
.catch((e: unknown) => {
console.log(e);
});
};
const useStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
border: '1px solid black',
borderRadius: '4px',
color: '#1d1d1d',
padding: '10px 16px',
},
complete: {
color: '#4caf50',
},
empty: {},
focus: {},
invalid: {
color: '#f44336',
},
},
labelStyles: {
base: {
fontSize: '16px',
fontWeight: 'bold',
},
},
errorTextStyles: {
base: {
color: 'red',
},
},
});
const classes = useStyles();
return (
<div className='CollectElements' style={{width: '300px'}}>
<CardNumberElement
id={'collectCardNumber'}
container={container}
table={'newTable'}
classes={classes}
column={'card_number'}
label={'Collect Card Number'}
/>
<FileInputElement
id='file-input'
container={container}
classes={classes}
table={'newTable'}
column={'file_input'}
label={'File Input'}
skyflowID={'431eaa6c-5c15-4513-aa15-29f50babe882'}
/>
<button onClick={handleFile}>Submit file</button>
<button onClick={handleCollect}>Collect</button>
</div>
);
};
export default CollectElements;
Sample Response for collect():
{
"records": [
{
"table": "newTable",
"fields": {
"card_number": "f3907186-e7e2-466f-91e5-48e12c2bcbc1",
}
}
]
}
Sample Response for file uploadFiles() :
{
"fileUploadResponse": [
{
"skyflow_id": "431eaa6c-5c15-4513-aa15-29f50babe882"
}
]
}
Composable Elements combine multiple Skyflow Elements in a single iframe, letting you create multiple Skyflow Elements in a single row. The following steps create a composable element and securely collect data through it.
Create a container for the composable element using the useComposableContainer hook of the Skyflow client:
const container = useComposableContainer(containerOptions)
The container requires an options object that contains the following keys:
layout
: An array that indicates the number of rows in the container and the number of elements in each row. The index value of the array defines the number of rows, and each value in the array represents the number of elements in that row, in order.
For example: [2,1]
means the container has two rows, with two elements in the first row and one element in the second row.
Note
: The sum of values in the layout array should be equal to the number of elements created
styles
: CSS styles to apply to the composable container.
errorTextStyles
: CSS styles to apply if an error is encountered.
const options = {
layout: [2, 1], // Required
styles: { // Optional
base: {
border: '1px solid #DFE3EB',
padding: '8px',
borderRadius: '4px',
margin: '12px 2px',
},
},
errorTextStyles: { // Optional
base: {
color: 'red',
fontFamily: '"Roboto", sans-serif'
},
global: {
'@import': 'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
}
},
};
Composable Elements use the following format. Create other elements within the Composable Element.
import {
ComposableContainer,
CardHolderNameElement,
CardNumberElement,
} from "skyflow-react-js";
<ComposableContainer id="<ID>" container="<CONTAINER>">
<CardHolderNameElement
id="<ID>"
table="<TABLE_NAME>"
container="<CONTAINER>"
column="<COLUMN_NAME>"
... props
/>
<CardNumberElement
id="<ID>"
table="<TABLE_NAME>"
container="<CONTAINER>"
column="<COLUMN_NAME>"
... props
/>
</ComposableContainer>
The following props
can be passed to Skyflow Composable Element:
{
container: 'ComposableContainer' // Required, the Composable Container.
table: 'string', // Required, the table this data belongs to.
column: 'string', // Required, the column into which this data should be inserted.
id: string, // Optional, id that can passed to the element.
classes: {}, // Optional, styles that should be applied to the element.
label: 'string', // Optional, label for the form element.
placeholder: 'string', // Optional, placeholder for the form element.
validations: [], // Optional, array of validation rules.
options: {}, // Optional, options that can be passed to an element.
onChange: Function, // Optional, function that is passed to trigger the onChange event.
onFocus: Function, // Optional, function that is passed to trigger the onFocus event.
onBlur: Function, // Optional, function that is passed to trigger the onBlur event.
onReady: Function, // Optional, function that is passed to trigger the onReady event.
}
The table
and column
fields indicate which table and column in the vault the Element correspond to.
Note: Use dot-delimited strings to specify columns nested inside JSON fields (for example, address.street.line1
).
All elements can be styled with JSS syntax.
An example of styling an element with makeSkyflowStyles
hook:
const useSkyflowStyles = makeSkyflowStyles({
inputStyles: {
base: {
color: "#013370",
// ...otherStyles
},
complete: {
color: "#4caf50",
},
empty: {},
focus: {},
invalid: {},
cardIcon: {
position: "absolute",
left: "8px",
bottom: "calc(50% - 12px)"
},
copyIcon: {
position: "absolute",
right: "8px",
}
},
labelStyles: {
base: {
color: "#0D4370",
// ...otherStyles
}
},
errorTextStyles: {
base: {
color: "#f44336",
// ...otherStyles
}
}
})
The inputStyles
field accepts an object of CSS properties to apply to the form element in the following states:
base
: all variants inherit from these stylescomplete
: applied when the Element has valid inputempty
: applied when the Element has no inputfocus
: applied when the Element has focusinvalid
: applied when the Element has invalid inputcardIcon
: applied to the card type icon in CARD_NUMBER ElementcopyIcon
: applied to copy icon in Elements when enableCopy option is trueglobal
: used for global styles like font-familyAn example of an inputStyles
object:
inputStyles: {
base: {
border: '1px solid #eae8ee',
padding: '10px 16px',
borderRadius: '4px',
color: '#1d1d1d',
},
complete: {
color: '#4caf50',
},
empty: {},
focus: {},
invalid: {
color: '#f44336',
},
cardIcon: {
position: 'absolute',
left: '8px',
bottom: 'calc(50% - 12px)',
},
copyIcon: {
position: 'absolute',
right: '8px',
},
global: {
'@import': 'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
}
}
The labelStyles
field supports the base
, focus
, global
.
An example labelStyles
object:
labelStyles: {
base: {
fontSize: '12px',
fontWeight: 'bold'
},
focus: {
color: '#1d1d1d'
},
global: {
'@import' :'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
}
}
The React SDK supports the following composable elements:
CardHolderNameElement
CardNumberElement
ExpirationDateElement
CVVElement
PinElement
ExpirationDateElement
ExpirationMonthElement
ExpirationYearElement
InputFieldElement
Note
: Only when the entered value in the below composable elements is valid, the focus shifts automatically. The element types are:
CardNumberElement
ExpirationDateElement
ExpirationMonthElement
ExpirationYearElement
The InputFieldElement
type is a custom UI element without any built-in validations. For information on validations, see validations.
Along with the Composable Element definition, you can define additional options for the element:
const options = {
required: false, // Optional, indicates whether the field is marked as required. Defaults to 'false'
enableCardIcon: true, // Optional, indicates whether card icon should be enabled (only applicable for CARD_NUMBER ElementType)
format: String, // Optional, format for the element (only applicable currently for EXPIRATION_DATE ElementType),
enableCopy: false // Optional, enables the copy icon in collect and reveal elements to copy text to clipboard. Defaults to 'false')
}
required
: Whether or not the field is marked as required. Defaults to false
.enableCardIcon
: Whether or not the icon is visible for the CARD_NUMBER element. Defaults to true
.format
: Format pattern for the element. Only applicable to EXPIRATION_DATE and EXPIRATION_YEAR element types.enableCopy
: Whether or not the copy icon is visible in collect and reveal elements. Defaults to false
.The accepted EXPIRATION_DATE
values are
MM/YY
(default)MM/YYYY
YY/MM
YYYY/MM
The accepted EXPIRATION_YEAR
values are
YY
(default)YYYY
When the form is ready to be submitted, call the collect(options?)
method on the container object. The options parameter takes an object of optional parameters as follows:
tokens
: Whether or not tokens for the collected data are returned. Defaults to 'true'additionalFields
: Non-PCI elements data to insert into the vault, specified in the records object format.upsert
: To support upsert operations, the table containing the data and a column marked as unique in that table.const options = {
tokens: true, // Optional, indicates whether tokens for the collected data should be returned. Defaults to 'true'.
additionalFields: {
records: [
{
table: 'string', // Table into which record should be inserted.
fields: {
column1: 'value', // Column names should match vault column names.
// ...additional fields here.
},
},
// ...additional records here.
],
}, // Optional
upsert: [ // Upsert operations support in the vault
{
table: 'string', // Table name
column: 'value', // Unique column in the table
},
], // Optional
};
import React from 'react';
import {
CardNumberElement,
CVVElement,
useMakeSkyflowStyles,
useComposableContainer,
ComposableContainer,
CardHolderNameElement,
} from 'skyflow-react-js';
const CollectElements = () => {
const useStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
fontFamily: 'Inter',
fontStyle: 'normal',
fontWeight: 400,
fontSize: '14px',
lineHeight: '21px',
color: '#1d1d1d',
padding: '0px 16px'
},
complete: {
color: '#4caf50',
}
},
empty: {
},
focus: {
},
invalid: {
color: '#f44336',
},
});
const useCVVStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
fontFamily: 'Inter',
fontStyle: 'normal',
fontWeight: 400,
fontSize: '14px',
lineHeight: '21px',
},
complete: {
color: '#4caf50',
},
empty: {},
focus: {},
invalid: {
color: '#f44336',
},
},
labelStyles: {
},
errorTextStyles: {
base: {
display: 'none'
},
},
})
const classes = useStyles();
const cvvClasses = useCVVStyles();
const containerOptions = {
layout: [1, 2],
styles: {
base: {
border: '1px solid #DFE3EB',
padding: '8px',
borderRadius: '4px',
margin: '12px 2px',
}
},
errorTextSyles: {
base: {
color: '#f44336'
}
}
}
const container = useComposableContainer(containerOptions);
const handleCollect = () => {
const options = {
tokens: true
}
const response = container?.collect(options);
response
?.then((res: any) => {
console.log(JSON.stringify(res));
})
.catch((e: any) => {
console.log(e);
});
};
return (
<div className='CollectElements' >
<ComposableContainer
id='composecontainer'
container={container}
>
<CardHolderNameElement
id='collectCardHolderName'
container={container}
table='pii_fields'
classes={classes}
placeholder='Cardholder Name'
column='first_name'
/>
<CardNumberElement
id='collectCardNumber'
container={container}
table='pii_fields'
classes={classes}
placeholder='XXXX XXXX XXXX XXXX'
column='card_number'
/>
<CVVElement
id='cvv'
container={container}
table='pii_fields'
classes={cvvClasses}
placeholder='CVC'
column='cvv'
/>
</ComposableContainer >
<button onClick={handleCollect}>Collect</button>
</div>
);
};
export default CollectElements;
{
"records": [
{
"table": "pii_fields",
"fields": {
"first_name": "63b5eeee-3624-493f-825e-137a9336f882",
"card_number": "f3907186-e7e2-466f-91e5-48e12c2bcbc1",
"cvv": "7baf5bda-aa22-4587-a5c5-412f6f783a19",
}
}
]
}
For information on validations, see validations.
You can communicate with Skyflow Elements by listening to element events:
The SDK supports four events:
CHANGE
: Triggered when the Element's value changes.READY
: Triggered when the Element is fully rendered.FOCUS
: Triggered when the Element gains focus.BLUR
: Triggered when the Element loses focus.The handler function(state) => void
is a callback function you provide that's called when the event is fired with a state object that uses the following schema:
state : {
elementType: Skyflow.ElementType
isEmpty: boolean
isFocused: boolean
isValid: boolean
value: string
}
Note
: Events only include element values when in the state object when env is DEV
. By default, value is an empty string.
import React from 'react';
import {
CardNumberElement,
CVVElement,
useMakeSkyflowStyles,
useComposableContainer,
ComposableContainer,
CardHolderNameElement,
} from 'skyflow-react-js';
const CollectElements = () => {
const useStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
fontFamily: 'Inter',
fontStyle: 'normal',
fontWeight: 400,
fontSize: '14px',
lineHeight: '21px',
color: '#1d1d1d',
padding: '0px 16px'
},
complete: {},
empty: {},
focus: {},
invalid: {},
},
labelStyles: {},
errorTextStyles: {
base: {
display: 'none'
},
},
});
const classes = useStyles();
const handleOnChange = (changeState: any) => {
console.log('Value', changeState);
};
const handleOnBlur = (changeState: any) => {
console.log('Blur', changeState);
};
const handleOnFocus = (changeState: any) => {
console.log('Focus', changeState);
};
const handleOnReady = (changeState: any) => {
console.log('Ready', changeState);
}
const containerOptions = {
layout: [1, 2],
styles: {
base: {
border: '1px solid #DFE3EB',
padding: '8px',
borderRadius: '4px',
margin: '12px 2px',
}
}
}
const container = useComposableContainer(containerOptions);
const handleCollect = () => {
const response = container?.collect();
response
?.then((res: any) => {
console.log(JSON.stringify(res));
})
.catch((e: any) => {
console.log(e);
});
};
return (
<div className='CollectElements' >
<ComposableContainer
id='composecontainer'
container={container}
>
<CardHolderNameElement
id='collectCardHolderName'
container={container}
table='pii_fields'
classes={classes}
placeholder='Cardholder Name'
column='first_name'
onChange={handleOnChange}
onBlur={handleOnBlur}
onFocus={handleOnFocus}
onReady={handleOnReady}
/>
<CardNumberElement
id='collectCardNumber'
container={container}
table='pii_fields'
classes={classes}
placeholder='XXXX XXXX XXXX XXXX'
column='card_number'
onChange={handleOnChange}
onBlur={handleOnBlur}
onFocus={handleOnFocus}
onReady={handleOnReady}
/>
<CVVElement
id='cvv'
container={container}
table='pii_fields'
classes={classes}
placeholder='CVC'
column='cvv'
onChange={handleOnChange}
onBlur={handleOnBlur}
onFocus={handleOnFocus}
onReady={handleOnReady}
/>
</ComposableContainer >
<button onClick={handleCollect}>Collect</button>
</div>
);
};
export default CollectElements;
Currently, the SDK supports one event:
SUBMIT
: Triggered when the Enter
key is pressed in any container element.The handler function(void) => void
is a callback function you provide that's called when the `SUBMIT' event fires.
import React from 'react';
import {
CardNumberElement,
CVVElement,
useMakeSkyflowStyles,
useComposableContainer,
ComposableContainer,
CardHolderNameElement,
} from 'skyflow-react-js';
const ComposableElements = () => {
const useStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
color: '#1d1d1d',
},
},
labelStyles: {},
errorTextStyles: {
base: {
display: 'none'
},
},
});
const classes = useStyles();
const handleOnSubmit = () => {
// Your implementation when the SUBMIT(enter) event occurs.
console.log('Submit Event Listener is being Triggered.');
};
const containerOptions = {
layout: [1, 2],
styles: {
base: {
border: '1px solid #DFE3EB',
padding: '8px',
borderRadius: '4px',
margin: '12px 2px',
}
}
}
const container = useComposableContainer(containerOptions);
const handleCollect = () => {
const response = container?.collect();
response
?.then((res: any) => {
console.log(JSON.stringify(res));
})
.catch((e: any) => {
console.log(e);
});
};
return (
<div className='CollectElements' >
<ComposableContainer
id='composecontainer'
container={container}
onSubmit={handleOnSubmit} // Pass onSubmit handler.
>
<CardHolderNameElement
id='collectCardHolderName'
container={container}
table='pii_fields'
classes={classes}
placeholder='Cardholder Name'
column='first_name'
/>
<CardNumberElement
id='collectCardNumber'
container={container}
table='pii_fields'
classes={classes}
placeholder='XXXX XXXX XXXX XXXX'
column='card_number'
/>
<CVVElement
id='cvv'
container={container}
table='pii_fields'
classes={classes}
placeholder='CVC'
column='cvv'
/>
</ComposableContainer >
<button onClick={handleCollect}>Collect</button>
</div>
);
};
export default ComposableElements;
Skyflow Elements can be used to securely reveal data in a browser without exposing your front end to the sensitive data. This is great for use cases like card issuance where you may want to reveal the card number to a user without increasing your PCI compliance scope.
To start, create a container using the useRevealContainer()
method of the Skyflow client as shown below.
const revealContainer = useRevealContainer()
import {RevealElement} from 'skyflow-react-js';
import Skyflow from 'skyflow-js';
<RevealElement
token='<DATA_TOKEN>'
container='<CONTAINER>'
...props
/>
The following props
can be passed to Skyflow reveal element:
{
container: 'RevealContainer', // Required, the reveal container.
token:'string', // Required, the actual data token.
id: string, // Optional, id that can passed to the element.
classes: {}, // Optional, styles that should be applied to the element.
label: 'string', // Optional, label for the form element.
redaction: Skyflow.RedactionType // Optional. Redaction to apply for retrieved data. E.g. RedactionType.MASKED
}
Note
:
inputStyles
, labelStyles
and errorTextStyles
parameters accepts a styles object as described in the previous section for collecting data. But for reveal element, inputStyles
accepts only base
variant and copyIcon
and global
style objects.redaction
defaults to RedactionType.PLAIN_TEXT
.There are four accepted values for RedactionType:
import React from 'react'
import { RevealElement, useRevealContainer, useMakeSkyflowStyles } from 'skyflow-react-js'
import Skyflow from 'skyflow-js';
const App = () => {
const revealContainer = useRevealContainer()
const useStyles = useMakeSkyflowStyles({
inputStyles: {
base: {
border: '1px solid black',
borderRadius: '4px',
color: '#1d1d1d',
padding: '10px 16px',
fontFamily: '"Roboto", sans-serif'
},
copyIcon: {
position: 'absolute',
right: '8px',
top: 'calc(50% - 10px)',
},
global: {
'@import' :'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
}
},
labelStyles: {
base: {
fontSize: '16px',
fontWeight: 'bold',
fontFamily: '"Roboto", sans-serif'
},
global: {
'@import' :'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
}
},
errorTextStyles: {
base: {
color: 'red',
fontFamily: '"Roboto", sans-serif'
},
global: {
'@import' :'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
}
},
})
const handleReveal = () => {
revealContainer
.reveal()
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
})
}
const classes = useStyles()
return (
<div className='App'>
<header className='App-header'>
<RevealElement
container={revealContainer}
token={'1404-8379-9069-7378'}
label={'Reveal Card Number'}
classes={classes}
redaction={Skyflow.RedactionType.MASKED}
/>
<RevealElement
container={revealContainer}
token={'89024714-6a26-4256-b9d4-55ad69aa4047'}
label={'Reveal Card Holder Name'}
classes={classes}
redaction={Skyflow.RedactionType.DEFAULT}
/>
<button onClick={handleReveal}>Reveal</button>
</header>
</div>
)
}
export default App
{
"success": [
{
"token": "1404-8379-9069-7378"
}
],
"errors": [
{
"token": "89024714-6a26-4256-b9d4-55ad69aa4047",
"error": {
"code": 404,
"description": "Tokens not found for 89024714-6a26-4256-b9d4-55ad69aa4047"
}
}
]
}
If you discover a potential security issue in this project, please reach out to us at security@skyflow.com. Please do not create public GitHub issues or Pull Requests, as malicious actors could potentially view them.
This project is licensed under the MIT license. See the LICENSE file for more info.
[1.8.1] - 2023-10-17
FAQs
Skyflow React SDK
The npm package skyflow-react-js receives a total of 1,872 weekly downloads. As such, skyflow-react-js popularity was classified as popular.
We found that skyflow-react-js demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.