Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

skyflow-react-js

Package Overview
Dependencies
Maintainers
0
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

skyflow-react-js

Skyflow React SDK

  • 1.17.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
2.1K
increased by42.71%
Maintainers
0
Weekly downloads
 
Created
Source

skyflow-react-js

A React wrapper for Skyflow JS SDK


Table of Contents


Including Skyflow-React

Requirements

  • The minimum supported version of React is v16.8.0. If you use an older version, upgrade React to use this library

Installation

Using npm

npm install --save skyflow-react-js

Initializing Skyflow-React

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:

  • The ranking of logging levels is as follows : DEBUG < INFO < WARN < ERROR
  • Since 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:

  • Since env is optional, by default the env will be PROD.
  • Use env option with caution, make sure the env is set to PROD when using skyflow-react-js in production.

Securely collecting data client-side

Using Skyflow Elements to collect data

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.

Step 1: Create a container

First create a container for the form elements using the useCollectContainer hook as shown below:

const container = useCollectContainer()

Step 2: Create a collect Element

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:

  • Use dot delimited strings to specify columns nested inside JSON fields (e.g. 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').
  allowedFileType: string[], // Optional, allowed extensions for the file to be uploaded.
}
  • 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.

  • allowedFileType parameter indicates the allowedFileType extensions to be uploaded.

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.

Step 3: Collect data from Elements

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)

End to end example of collecting data with Skyflow Elements

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",
      }
    }
  ]
}

Insert call example with upsert support

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"
           }
       }
   ]
}

Validations:

Skyflow-React which internally uses Skyflow-JS SDK provides two types of validations on Collect Elements

1. Default Validations:

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 digits
  • EXPIRATION_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 digits

2. Custom Validations:

Custom 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 value
const 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 value
const lengthMatchRule = {
  type: LENGTH_MATCH_RULE,
  params: {
    min: number, // Optional
    max: number, // Optional
    error: string, // Optional, default error is 'VALIDATION FAILED'.
  },
}
  • ELEMENT_VALUE_MATCH_RULE: You can use this rule to match the value of one element with the value of another element. elementId is the id of the collect element with which you want to compare values. Collect elements must have an id property to compare values.
const elementValueMatchRule = {
  type: ELEMENT_VALUE_MATCH_RULE,
  params: {
    elementId: string, // 'id' of element you want to compare values with
    error: string // Optional, default error is 'VALIDATION FAILED'.
  }
}
Example usage of custom validations

The sample code snippet for using custom validations:

/*
  A simple example that illustrates custom validations.
  Adding REGEX_MATCH_RULE, LENGTH_MATCH_RULE and ELEMENT_VALUE_MATCH_RULE to
  collect elements.
*/
import {
  CardNumberElement, 
  InputFieldElement as PasswordElement,
  LENGTH_MATCH_RULE,
  REGEX_MATCH_RULE,
  ELEMENT_VALUE_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'
  }
};

// This rule will compare values of passwords entered in InputField elements
const elementValueMatchRule = {
  type: ELEMENT_VALUE_MATCH_RULE,
  params: {
    elementId: 'collectPassword', // 'id' of Password (InputField) element
    error: 'passwords does not match' 
  }
}

const Form = (props) => {
  return (
    <>
      <CardNumberElement
        container='COLLECT CONTAINER'
        table='<TABLE_NAME>'
        column='<COLUMN_NAME>'
        validations={[alphabetsOnlyRegexRule, lengthRule]}
        ...props
      />

      <PasswordElement
        id={'collectPassword'} // required, same as passed in element match rule
        container='COLLECT CONTAINER'
        table='<TABLE_NAME>'
        column='<COLUMN_NAME>'
        ...props
      />

      <PasswordElement
        container='COLLECT CONTAINER'
        table='<TABLE_NAME>'
        column='<COLUMN_NAME>'
        validations={[elementValueMatchRule]} // pass rule to element
        ...props
      />
    </>
  );
};

export default Form;

Using Skyflow Elements to update data

You can update the data in a vault with Skyflow Elements. Use the following steps to securely update data.

Step 1: Create a container

First create a container for the form elements using the useCollectContainer hook as shown below:

const container = useCollectContainer()

Step 2: Create a collect Element

import { CardNumberElement} from 'skyflow-react-js';

<CardNumberElement
  table='<TABLE_NAME>'
  container='<CONTAINER>'
  column='<COLUMN_NAME>'
  skyflowID='<SKYFLOW_ID>'        // The skyflow_id of the record to be updated.
  ...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.
  skyflowID: 'string',          // The skyflow_id of the record to be updated.
}

The table and column fields indicate which table and column in the vault the Element corresponds to.

skyflowID indicates the record that you want to update.

Note:

  • Use dot delimited strings to specify columns nested inside JSON fields (e.g. address.street.line1)

Step 3: Update data from Elements

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.
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
          skyflowID: "value",     // The skyflow_id of the record to be updated.
          // ...additional fields here
        },
      },
      // ...additional records here
    ],
  }, // Optional
}

container.collect(options)

Note: skyflowID is required if you want to update the data using skyflow id. If skyflowID isn't specified, the collect(options?) method creates a new record in the vault.

End to end example of updating data with Skyflow Elements

import React from 'react'
import { CardNumberElement, useCollectContainer, useMakeSkyflowStyles, CardHolderNameElement } 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 nonPCIRecords = {
          records: [
            {
              table: 'cards',
              fields: {
              gender: 'MALE',
              skyflowID:  '431eaa6c-5c15-4513-aa15-29f50babe882',
              },
            },
          ],
      };
    const response = container.collect({
          tokens: true,
          additionalFields: nonPCIRecords,
    });
    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}
          skyflowID={'431eaa6c-5c15-4513-aa15-29f50babe882'}
        />
        <CardHolderNameElement
        container={container}
        table={'cards'}
        column={'name'}
        label={'card holder name'}
        skyflowID={'431eaa6c-5c15-4513-aa15-29f50babe882'}
      />
        <button onClick={handleCollect}>Collect</button>
      </header>
    </div>
  )
}

export default App

Sample Response :

{
 "records": [
   {
     "table": "cards",
     "fields": {
       "skyflow_id": "431eaa6c-5c15-4513-aa15-29f50babe882",
       "cardNumber": "f3907186-e7e2-466f-91e5-48e12c2bcbc1",
       "first_name": "131e70dc-6f76-4319-bdd3-96281e051051",
       "gender": "12f670af-6c7d-4837-83fb-30365fbc0b1e"
     }
   }
 ]
}

Event Listener on Collect Elements

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.

Example Usage of Event Listener on Collect Elements

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
Sample Element state object when 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',
};
Sample Element state object when env is PROD
{
  elementType: 'CARDHOLDER_NAME',
  isEmpty: false,
  isFocused: true,
  isValid: true,
  value: '',
};
{
  elementType: 'CARD_NUMBER',
  isEmpty: false,
  isFocused: true,
  isValid: true,
  value: '41111111XXXXXXXX',
};

Using Skyflow File Input Element to upload a file

You can upload binary files to a vault using the Skyflow File Input Element. Use the following steps to securely upload a file.

Step 1: Create a container

First create a container for the form elements using the useCollectContainer hook as shown below:

const container = useCollectContainer()

Step 2: Create a File Input Element

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 element
  • Use period-delimited strings to specify columns nested inside JSON fields (e.g. address.street.line1).

Step 3: Collect data from elements

When the file is ready to be uploaded, call the uploadFiles() method on the container object.

container.uploadFiles(options);

File upload limitations:

  • Only non-executable file are allowed to be uploaded.
  • Files must have a maximum size of 32 MB
  • File columns can't enable tokenization, redaction, or arrays.
  • Re-uploading a file overwrites previously uploaded data.
  • Partial uploads or resuming a previous upload isn't supported.

End-to-end file upload

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"
        }
    ]
}
File upload with allowedFileType option
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 options = {
    allowedFileType: [".pdf",".png"]
  };
  
  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'}
        options={options}
      />

      <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"
        }
    ]
}
File upload with additional elements
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"
        }
    ]
}

Securely collecting data client-side using Composable Elements

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.

Step 1: Create a composable container

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")',
        }        
    },
};

Step 2: Create Composable Elements

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 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

An 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.

  • requiredAsterisk: styles applied for the Asterisk symbol in the label.

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

Step 3: Collect data from Elements

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
};

End to end example of collecting data with Composable Elements:

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;

Sample Response:

{
    "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.

Set an event listener on Composable Elements:

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.

Example Usage of Event Listener on Composable Elements

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;

Set an event listener on a composable container

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.

Example usage of event listener on composable container

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;

Securely revealing data client-side

Retrieving data from the vault

For non-PCI use-cases, retrieving data from the vault and revealing it in the browser can be done either using the SkyflowID's, unique column values as described below

  • Using Skyflow ID's or Unique Column Values

    You can retrieve data from the vault with the get(records) method using either Skyflow IDs or unique column values.

    The records parameter accepts a JSON object that contains an array of either Skyflow IDs or unique column names and values.

    Note: You can use either Skyflow IDs or unique values to retrieve records. You can't use both at the same time.

    Skyflow.RedactionTypes accepts four values:

    • PLAIN_TEXT
    • MASKED
    • REDACTED
    • DEFAULT

    You must apply a redaction type to retrieve data.

Schema (Skyflow IDs)
data = {
 records: [
   {
     ids: ["SKYFLOW_ID_1", "SKYFLOW_ID_2"],      // List of skyflow_ids for the records to fetch.
     table: "NAME_OF_SKYFLOW_TABLE",             // Name of table holding the records in the vault.
     redaction: Skyflow.RedactionType,           // Redaction type to apply to retrieved data.
   },
 ],
};
Schema (Unique column values)
data = {
 records: [
   {
     table: "NAME_OF_SKYFLOW_TABLE",        // Name of table holding the records in the vault.
     columnName: "UNIQUE_COLUMN_NAME",      // Unique column name in the vault.
     columnValues: [                        // List of given unique column values. 
       "<COLUMN_VALUE_2>",
       "<COLUMN_VALUE_3>",
     ],                                     // Required when specifying a unique column
     redaction: Skyflow.RedactionType,      // Redaction type applies to retrieved data.
   },
 ],
};

Example usage (Skyflow IDs)

import React from 'react';
import {
    useSkyflow
} from 'skyflow-react-js';

const GetRecords = () => {

    const skyflow = useSkyflow()

    const handleGetMethod = () => {
        const response = skyflow.get({
            records: [{
                    ids: ['f8d8a622-b557-4c6b-a12c-c5ebe0b0bfd9'],
                    table: 'cards',
                    redaction: Skyflow.RedactionType.PLAIN_TEXT,
                },
                {
                    ids: ["da26de53-95d5-4bdb-99db-8d8c66a35ff9"],
                    table: "contacts",
                    redaction: Skyflow.RedactionType.PLAIN_TEXT,
                },
            ],
        });
        response.then((res: any) => {
                console.log(res)
            })
            .catch((e: any) => {
                console.log(e)
            });
    }
	return ( 
    <div id = 'get-div' >
		<button id = 'get-button' onClick = {callGet} > Get Records </button>  
    </div >
	)
}

Example response

{
   "records": [
       {
           "fields": {
              "card_number": "4111111111111111",
              "cvv": "127",
              "expiry_date": "11/2035",
              "fullname": "myname",
              "id": "f8d8a622-b557-4c6b-a12c-c5ebe0b0bfd9"
           },
           "table": "cards"
       }
   ],
   "errors": [
       {
           "error": {
              "code": "404",
              "description": "No Records Found"
           },
           "ids": ["da26de53-95d5-4bdb-99db-8d8c66a35ff9"]
       }
   ]
}

Example usage (Unique column values)

import React from 'react';
import {
	useSkyflow
} from 'skyflow-react-js';

const GetRecords = () => {

	const skyflow = useSkyflow()

	const handleGetMethod = () => {
		const response = skyflow.get({
			records: [{
				table: "cards",
				redaction: RedactionType.PLAIN_TEXT,
				columnName: "card_id",
				columnValues: ["123", "456"],
			}],
		});
		response.then((res: any) => {
				console.log(res)
			})
			.catch((e: any) => {
				console.log(e)
			});
	}
	return ( 
    <div id = 'get-div' >
		<button id = 'get-button' onClick = {callGet} > Get Records </button>  
    </div >
	)
}

Sample response:

{
   "records": [
       {
           "fields": {
               "card_id": "123",
               "expiry_date": "11/35",
               "fullname": "myname",
               "id": "f8d2-b557-4c6b-a12c-c5ebfd9"
           },
           "table": "cards"
       },
       {
           "fields": {
               "card_id": "456",
               "expiry_date": "10/23",
               "fullname": "sam",
               "id": "da53-95d5-4bdb-99db-8d8c5ff9"
           },
           "table": "cards"
       }
   ]
}

Using Skyflow Elements to reveal data

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.

Step 1: Create a container

To start, create a container using the useRevealContainer() method of the Skyflow client as shown below.

const revealContainer = useRevealContainer()

Step 2: Create a reveal element

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:

  • The 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.
Redaction Types

There are four accepted values for RedactionType:

  • PLAIN_TEXT
  • MASKED
  • REDACTED
  • DEFAULT

End to end example using Reveal Element

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

Sample Response

{
  "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"
      }
    }
  ]
}

Render a file with a File Element

You can render files using the Skyflow File Element. Use the following steps to securely render a file.

Step 1: Create a container

To start, create a container using the useRevealContainer() method of the Skyflow client as shown below.

const revealContainer = useRevealContainer()

Step 2: Create a File Element

Define a Skyflow Element to render the file as shown below.

import {FileRenderElement} from 'skyflow-react-js';
import Skyflow from 'skyflow-js';

<FileRenderElement
  id= 'string'        // Required, id that can passed to the element, it should be unique.
  skyflowID= 'string' // Required, skyflow id of the file to be render
  column= 'string'    // Required, column name of the file to be render
  table= 'string'     // Required, table name of the file to be render
  ...props
/>

The following props can be passed to Skyflow file render element:

{
  container: 'RevealContainer', // Required, the reveal container.
  id: 'string',                 // Required, id that can passed to the element, it should be unique.
  classes: {},                  // Optional, styles that should be applied to the element.
  altText: 'string',            // Optional, string that is shown before file render call
  skyflowID: 'string',          // Required, skyflow id of the file to be render
  column: 'string',             // Required, column name of the file to be render
  table: 'string',              // Required, table name of the file to be render
}

The inputStyles and errorTextStyles parameters accept a styles object as described in theprevious section for collecting data. But for render file elements, inputStyles accepts only base variant, global style objects.

An example of a inputStyles object:

inputStyles: {
  base: {
      height: '400px',
      width: '300px',
  },
  global: {
    '@import' :'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
  }
}

An example of a errorTextStyles object:

errorTextStyles: {
  base: {
    color: '#f44336',
  },
  global: {
    '@import' :'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
  }
}

Step 3: Render File

When the element is created and mounted, use useRenderFile(id) hook to pass the div id of file render element and call the renderFile() method on the file render element instance return by useRenderFile(id) as shown below:

const fileElement = useRenderFile('fileElement-1');

fileElement
  .renderFile()
  .then(data => {
    // Handle success.
  })
  .catch(err => {
    // Handle error.
  });

Note: The div id passed in the element should be unique, and the same div id should be passed in the useRenderFile('id') hook.

End to end example of file render

import React, { useEffect, useState } from 'react';
import {
  useMakeSkyflowStyles,
  useRevealContainer,
  useRenderFile,
  FileRenderElement,
} from 'skyflow-react-js';

const App = () => {
  const revealContainer = useRevealContainer();

  const [visible , setVisible] = useState(false);
  const [skyflowID, updateSkyflowID] = useState('');

  // REPLACE with your custom implementation to fetch skyflow_id from backend service.
  // Sample implementation
  useEffect(() => {
    fetch('<BACKEND_URL>')
      .then((response: any) => {

      // on successful fetch skyflow_id
      const skyflowID = response.skyflow_id;

      // set skyflow id
      updateSkyflowID(skyflowID);
      setVisible(true);

      }).catch((error) => {
      // failed to fetch skyflow_id
      console.log(error);
    });
  }, []);

  // pass file render element div id in useRenderFile hook
  const render = useRenderFile('fileElement-1');

  const handleRender = () => {
    // call render file method
    render?.renderFile().then((data) => console.log(data)).catch( err => console.log(err));
  }

  const useStyles = useMakeSkyflowStyles({
    inputStyles: {
      base: {
        height: '300px',
        width: '400px',
      },
    },
    errorTextStyles: {
      base: {
        color: 'red',
        fontFamily: '"Roboto", sans-serif'
      },
      global: {
        '@import' :'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
      },
    },
  });


  const classes = useStyles()

  return (
    <div className='App'>
      <header className='App-header'>
      <h4>Render PDF</h4>
      { visible &&  <FileRenderElement // create element, pass fetched skyflow id and other details here.
        id={'fileElement-1'}
        container={revealContainer}
        classes={classes}
        skyflowID={skyflowID}
        column={'file'}
        table={'credit_cards'}
        altText={'Image File'}
      /> }
      <button onClick={handleRender}>Reveal</button>
      </header>
    </div>
  )
}

export default App

Sample Success Response

{
  "success": [
     {
     "skyflow_id": "b63ec4e0-bbad-4e43-96e6-6bd50f483f75",
     "column": "file"
   },
  ]
}

Update Skyflow Elements Properties

You can dynamically update Skyflow Elements’ properties dynamically by using React’s useState hook. You can maintain the element properties via React state and update them at runtime as and when required. Skyflow's React SDK provides the following elements:

  • Collect Elements
  • Reveal Elements
  • Composable Elements
  • File Render Element

Note - You cannot update the type property for Collect and Composable Elements.

End-to-end example for updating properties in Collect and Reveal Elements

import React, { useState } from 'react'
import Skyflow from 'skyflow-js'
import {
  CardNumberElement,
  FileInputElement,
  FileRenderElement,
  RevealElement,
  useCollectContainer,
  useMakeSkyflowStyles,
  useRenderFile,
  useRevealContainer,
} from 'skyflow-react-js'

const App = () => {
  // Initialise states for properties to update
  const [table, setTable] = useState<string>('dummy-table')
  const [skyflowID, setSkyflowID] = useState<string>('dummy-id')
  const [cardNumberLabel, setCardNumberLabel] = useState<string>('card number')
  const [cardNumberToken, setCardNumberToken] = useState<string>('43wrf-23v-9vf-fdvjcknfsk')

  const container = useCollectContainer()
  const revealContainer = useRevealContainer()
  const fileElement = useRenderFile('fileRenderElement-1')

  const useStyles = useMakeSkyflowStyles({
    inputStyles: {
      base: {
        border: '1px solid black',
        borderRadius: '4px',
        color: '#1d1d1d',
        padding: '10px 16px',
        fontFamily: '"Roboto", sans-serif',
      },
      complete: {
        color: '#4caf50',
      },
      empty: {},
      focus: {},
      invalid: {
        color: '#f44336',
      },
      cardIcon: {
        position: 'absolute',
        left: '8px',
        bottom: 'calc(50% - 12px)',
      },
      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: 'blue',
        fontFamily: '"Roboto", sans-serif',
      },
      global: {
        '@import': 'url("https://fonts.googleapis.com/css2?family=Roboto&display=swap")',
      },
    },
  })

  const options = {
    enableCopy: true,
  }

  const classes = useStyles()
  
  // handler function for collecting data
  const handleCollect = () => {
    const response = container.collect({
      tokens: true,
    })
    response
      .then((res: unknown) => {
        console.log(JSON.stringify(res))
        const id = res.records[0].fields.skyflow_id 
        const token = res.records[0].fields.card_number
        setSkyflowID(id)  // updates skyflowID with received ID
        setCardNumberToken(token)  // updates token with received token
      })
      .catch((e: unknown) => {
        console.log(e)
      })
  }

  // handler function for revealing data
  const handleReveal = () => {
    revealContainer
      .reveal()
      .then((res: any) => {
        console.log(res)
      })
      .catch((err: any) => {
        console.log(err)
      })
  }
  
  // handler function for rendering file
  const renderFile = () => {
   fileElement?
      .renderFile()
      .then((res: any) => {
        console.log(res)
      })
      .catch((err: any) => {
        console.log(err)
      })
  }

  // uploads file after skyflowID is updated
  React.useEffect(() => {
    if (skyflowID !== 'dummy-id') {
      container
        .uploadFiles({})
        .then((res) => {
          console.log(res)
        })
        .catch((err) => {
          console.log(err)
        })
    }
  }, [skyflowID])

  // updates table property value
  const handleCollectUpdate = () => {
    setTable('pii_fields_upsert')
  }
  
  // updates card number label property value
  const handleRevealUpdate = () => {
    setCardNumberLabel('Reveal Card Number')
  }

  return (
    <div className='App'>
      <header className='App-header'>
        <CardNumberElement
          container={container}
          table={table}
          classes={classes}
          column={'card_number'}
          label={'Collect Card Number'}
          options={options}
        />

        <FileInputElement
          container={container}
          table={table}
          column={'file'}
          label={'Collect File'}
          classes={classes}
          skyflowID={skyflowID}
        />

        <RevealElement
          id={'dynamicRevealElement-1'}
          container={revealContainer}
          token={cardNumberToken}
          label={cardNumberLabel}
          classes={classes}
          redaction={Skyflow.RedactionType.PLAIN_TEXT}
          altText={'card number'}
        />

        <FileRenderElement
          container={revealContainer}
          id={'fileRenderElement-1'}
          skyflowID={skyflowID}
          altText={'image.png'}
          classes={classes}
          table={table}
          column={'file'}
        />

        <button onClick={handleCollect}>Collect</button>
        <button onClick={handleCollectUpdate}>Update Collect</button>
        <button onClick={handleReveal}>Reveal</button>
        <button onClick={handleRevealUpdate}>Update Reveal</button>
        <button onClick={renderFile}>Render File</button>
      </header>
    </div>
  )
}

export default App

Reporting a Vulnerability

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.

License

This project is licensed under the MIT license. See the LICENSE file for more info.

Keywords

FAQs

Package last updated on 03 Sep 2024

Did you know?

Socket

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc