Skip to main content

React Native with VGS Collect

Setting up DyScan

Prerequisite Credentials

The following credentials from Dyneti are necessary to install and use DyScan for React Native with VGS Collect. Please contact us for any missing credentials.

  1. An NPM token.
  2. A GitHub token.
  3. Nexus username and password for Dyneti's Maven repository.
  4. An API key from Dyneti.

NPM

After getting the access token for Dyneti's NPM repo access, go to your RN project root directory and set it up with the following terminal commands.

If you are using ~/.zshrc:

echo "export DYSCAN_NPM_TOKEN=accessTokenHere" >> ~/.zshrc
source ~/.zshrc
echo "@dyneti:registry=https://registry.npmjs.org/" >> .npmrc
echo "//registry.npmjs.org/:_authToken=\${DYSCAN_NPM_TOKEN}" >> .npmrc

If you are using ~/.bash_profile:

echo "export DYSCAN_NPM_TOKEN=accessTokenHere" >> ~/.bash_profile
source ~/.bash_profile
echo "@dyneti:registry=https://registry.npmjs.org/" >> .npmrc
echo "//registry.npmjs.org/:_authToken=\${DYSCAN_NPM_TOKEN}" >> .npmrc

Verify whether the terminal can see the value of DYSCAN_NPM_TOKEN

echo $DYSCAN_NPM_TOKEN

Check that the .npmrc file has these two lines

@dyneti:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${DYSCAN_NPM_TOKEN}

Install DyScan and the VGS integration with the following terminal commands

npm install @dyneti/react-native-dyscan @dyneti/react-native-dyscan-vgs @vgs/collect-react-native 

Yarn

Configure Yarn to use the token from Dyneti.

yarn config set npmScopes.dyneti.npmRegistryServer "https://registry.npmjs.org/"
yarn config set npmScopes.dyneti.npmAlwaysAuth true
yarn config set npmScopes.dyneti.npmAuthToken $DYSCAN_NPM_TOKEN

The commands above assume the token is set as a DYSCAN_NPM_TOKEN environment variable. If you prefer to use an environment variable in .yarnrc.yml, use ${DYSCAN_NPM_TOKEN}.

# .yarnrc.yml
npmScopes:
dyneti:
npmAlwaysAuth: true
npmAuthToken: ${DYSCAN_NPM_TOKEN}
npmRegistryServer: "https://registry.npmjs.org/"

Install DyScan and the VGS integration with Yarn using

yarn add @dyneti/react-native-dyscan @dyneti/react-native-dyscan-vgs @vgs/collect-react-native

Linking for Android

Modify android/build.gradle to include Dyneti's Maven repository.

buildscript {
...
}

allprojects {
repositories {
// Other repositories are here
maven {
credentials {
username = "nexusUsername"
password = "nexusPassword"
}
url "https://nexus.dyneti.com/repository/maven-releases/"
authentication {
basic(BasicAuthentication)
}
}
}
}
info

If you run into any build issues with Android, please see our React Native guide for troubleshooting steps, or contact us.

Linking for iOS

Add the following line to ios/Podfile under your app's target.

pod 'DyScan', :podspec => '../node_modules/@dyneti/react-native-dyscan/specs/DyScan.podspec'

Then install the pods. Inside the ios/ directory,

pod install # or bundle install && bundle exec pod install

If you have not previously asked for camera permissions in the app, you will need to add the “NSCameraUsageDescription” (Privacy - Camera Usage Description) to your Info.plist file. To do this in Xcode, navigate to Info.plist. When you hover over any of the fields in the file, a small plus icon should show up next to the field. Click on the plus sign and type in “NSCameraUsageDescription” into the new field. You should set the value to be the string a user sees when they are prompted for the camera permission. For example,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>Used for card scans</string>
<!-- other keys -->
</dict>
</plist>
info

If you run into any build issues with iOS, please see our React Native guide for troubleshooting steps, or contact us.

Interfacing DyScan

To use DyScan to collect credit card numbers and expiration dates, first create a DyScan client and a VGS collector.

import { createDyScanFormClient } from "@dyneti/react-native-dyscan-vgs";
import { VGSCollect } from "@vgs/collect-react-native";

const client = createDyScanFormClient(
"YOUR_API_KEY_HERE",
// All options are optional
// See below for complete documentation of options
{bgColor: "#0C2341",
bgOpacity: 0.7}
);
const collector = new VGSCollect("YOUR_VGS_VAULT_ID", "sandbox"); // or "production"

Then register the DyScan client and VGS collector with a form component. Dyneti provides DynetiVGSCardInput and DynetiVGSTextInput. For example, the following registers the client and the VGS collector with a card number collection component.

<DynetiVGSCardInput
collector={collector}
fieldName="card_number"
dyScanClient={client}
placeholder="4111 1111 1111 1111"
onStateChange={(state: any) => {
console.log("card_number state: ", state);
}}
iconPosition="right"
showCardBrandIcon={true}
showCameraButton={true}
cameraIconHeight={32}
/>

See below for complete documentation of props.

Supported Input Fields

Dyneti's library supports collecting credit card numbers and expiration dates. For credit card numbers, use DynetiVGSCardInput, and for expiration dates, use DynetiVGSTextInput and pass "expDate" to the type prop.

Camera Icons on Card Input

A pressble camera icon can be shown in the card input text field. Dyneti's default icons follow the user's light or dark theme. You can modify the apperance with the cameraIconWidth, cameraIconHeight, and cameraIconStyle props. A custom icon can also be provided through the customCameraIcon prop. For example,

const customCameraIcon = require("myCustomCameraIcon.png");
<DynetiVGSCardInput
collector={collector}
fieldName="card_number"
dyScanClient={client}
placeholder="4111 1111 1111 1111"
iconPosition="right"
showCameraButton={true}
customCameraIcon={customCameraIcon}
cameraIconHeight={32}
/>

See below for full prop documentation.

Expiration Date Formats

DyScan's integration with VGS Collect supports expiration dates either in MM/YYYY or in MM/YY format. To choose between them, change the mask prop to DynetiVGSTextInput. We also recommend updating the placeholder text to avoid confusing end-users. For example, the following component works well for MM/YY format.

<DynetiVGSTextInput
collector={collector}
fieldName="expiration_date"
dyScanClient={client}
type="expDate"
mask="##/##"
placeholder="MM/YY"
divider="/"
/>

Adapt the below example component for MM/YYYY format.

<DynetiVGSTextInput
collector={collector}
fieldName="expiration_date"
dyScanClient={client}
type="expDate"
mask="##/####"
placeholder="MM/YYYY"
divider="/"
/>

Example Component

Here's an example adapted from VGS's example but replacing the card number and expiration date with Dyneti's to allow card scanning. It then sends it to the default inbound VGS route. Note that Dyneti's components can be mixed with VGS's.

import {
DynetiVGSCardInput,
DynetiVGSTextInput,
createDyScanFormClient,
} from "@dyneti/react-native-dyscan-vgs";
import {
VGSCVCInput,
VGSCollect,
VGSError,
VGSErrorCode,
VGSTextInput,
VGSTokenizationConfiguration,
} from "@vgs/collect-react-native";
import {
Pressable,
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";

const FormExample = () => {
const collector = new VGSCollect("YOUR_VGS_VAULT_ID", "sandbox");

const handleSubmit = async () => {
try {
const { status, response } = await collector.submit("/post", "POST");
if (response.ok) {
try {
const responseBody = await response.json();
const json = JSON.stringify(responseBody, null, 2);
console.log("Success:", json);
} catch (error) {
console.warn(
"Error parsing response body. Body can be empty or your <vaultId> is wrong!",
error,
);
}
} else {
console.warn(`Server responded with error: ${status}\n${response}`);
}
} catch (error) {
if (error instanceof VGSError) {
switch (error.code) {
case VGSErrorCode.InputDataIsNotValid:
for (const fieldName in error.details) {
console.error(
`Not valid fieldName: ${fieldName}: ${error.details[
fieldName
].join(", ")}`,
);
}
break;
default:
console.error("VGSError:", error.code, error.message);
}
} else {
console.error("Network or unexpected error:", error);
}
}
};
const client = createDyScanFormClient("YOUR_API_KEY_HERE", {
bgColor: "#0C2341",
bgOpacity: 0.7,
});

return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<Text style={styles.title}>DyScan+VGS Collect Example</Text>
<VGSTextInput
collector={collector}
fieldName="card_holder"
type="cardHolderName"
placeholder="Name"
onStateChange={(state: any) => {
console.log("card_holder state: ", state);
}}
containerStyle={styles.inputContainer} // Container-specific styles
textStyle={styles.inputText} // text-specific styles
/>
<DynetiVGSTextInput
collector={collector}
fieldName="expiration_date"
dyScanClient={client}
type="expDate"
mask="##/##"
placeholder="MM/YY"
divider="/"
onStateChange={(state: any) => {
console.log("expiration_date state: ", state);
}}
containerStyle={styles.inputContainer}
textStyle={styles.inputText}
/>
<DynetiVGSCardInput
collector={collector}
fieldName="card_number"
dyScanClient={client}
placeholder="4111 1111 1111 1111"
onStateChange={(state: any) => {
console.log("card_number state: ", state);
}}
iconPosition="right"
showCardBrandIcon={true}
showCameraButton={true}
containerStyle={styles.inputContainer}
textStyle={styles.inputText}
cameraIconHeight={32}
/>
<VGSCVCInput
collector={collector}
fieldName="card_cvc"
placeholder="CVC/CVV"
onStateChange={(state: any) => {
console.log("card_cvc state: ", state);
}}
containerStyle={styles.inputContainer}
textStyle={styles.inputText}
/>
<Pressable
style={[styles.button, styles.buttonOpen]}
onPress={() => client.scanCard()}
>
<Text style={styles.textStyle}>Scan Card</Text>
</Pressable>
<TouchableOpacity
style={[styles.button, { backgroundColor: "blue" }]}
onPress={handleSubmit}
>
<Text style={{ color: "white" }}>Submit</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};

const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: "white",
},
container: {
flex: 1,
paddingHorizontal: 20,
paddingTop: 40,
},
title: {
fontSize: 20,
marginBottom: 10,
},
inputContainer: {
height: 50,
borderWidth: 2,
borderRadius: 8,
paddingHorizontal: 12,
backgroundColor: "white",
marginBottom: 20,
},
inputText: {
fontSize: 16,
color: "black",
},
button: {
backgroundColor: "blue",
padding: 15,
borderRadius: 8,
alignItems: "center",
margin: 5,
},
buttonOpen: {
backgroundColor: "#F194FF",
},
buttonClose: {
backgroundColor: "#2196F3",
},
textStyle: {
color: "white",
fontWeight: "bold",
textAlign: "center",
},
modalText: {
marginBottom: 15,
textAlign: "center",
},
});

export default FormExample;

API Reference

Also see VGS's documentation.

createFormClient(apiKey, options?)

Use createFormClient to create a DyScan client of type DyScanFormClient.

const client = createFormClient("YOUR_API_KEY", {showDynetiLogo: true});

Function parameters

ParameterDescriptionType
apiKeyAPI key provided by Dyneti.string
optionsObject containing options for configuring the scanobject

Options reference

ParameterDescriptionType
showCornersWhether to show the scan area outline.boolean
cornerThicknessHow thick to make the lines outlining the scan area, indicating to the user where to place their credit card.number
cornerInactiveColorThe color to use in drawing the outline of the scan region when nothing of interest is detected.color*
cornerCompletedColorThe color to use in drawing the outline of the scan region when we have successfully scanned a card.color*
bgColorThe color to use to obscure everything outside the scan region in order to draw the user’s attention to the scan region.color*
bgOpacityThe opacity to use when obscuring everything outside the scan region, as an integer ranging from 0.0 (transparent) to 1.0 (opaque).number
lightTorchWhenDarkWhether the phone will turn on the flashlight if multiple frames have appeared to be too dark. If set to false, torch toggle button will appear.boolean
vibrateOnCompletionWhether the device will vibrate once it has successfully extracted the credit card data from the images provided by the camera.boolean
showDynetiLogoWhether to show the Dyneti logo above the scan regionboolean
showCardOverlayWhether to show the overlay displaying a sample card number and expiration date hinting at the user to place their card in the scan region.boolean
enableSidewaysScanningIf set to true, vertical cards will be able to be scanned when positioned in the horizontal scan window. Note that enableSidewaysScanning and showRotateButton should not simultaneously be set to true.boolean
showHelperTextWhether to show the helper text on screen.boolean
helperTextStringWhat to text to display to the user to guide them in scanning their card.string
helperTextColorThe color to use to display the helper text.color*
helperTextSizeThe font size of the helper text.number
helperTextFontFamilyThe font family name of the helper text. The font needs to be placed in assets first.string
showRotateButtonWhether to show the button which allows the user to rotate the scan region by 90 degrees, facilitating the scanning of vertical cards.boolean
helperTextPositionThe preset position of the helper text. Available values: top, center, bottom. Default: bottomstring

Color Representation

Colors are inputted as hex-encoded strings representing the RGB values with a '#' at the front, the so-called 'HTML Color Codes.' For example, #ff0000 is red. The representation must be exactly 7 characters long; otherwise we will ignore it when trying to convert the strings to the native representations.

Client methods

scanCard()

This can be used to manually start the card scan. For example as part of a button outside the form.

DynetiVGSTextInput

This is the base component for integrating with DyScan. We recommend using it for collecting expiration dates by passing type={"expDate"}. We recommend using DynetiVGSCardInput for collecting credit card numbers and VGS's components for all other fields such as CVV or SSN.

Required props

PropDescriptionType
collectorA VGS Collect instance.VGSCollect
fieldNameThe name of the field. It will be used by the collector when making requests to your VGS vault.string
dyScanClientA DyScan client created by createFormClient.DyScanFormClient

Optional props

PropDescriptionType
typeThe type of the data to be collected. Both "card" and "expDate" are supported, but we recommend using DynetiVGSCardInput for credit card input."card" | "expDate"
maskThe mask pattern for the input field. We recommend "##/##" for MM/YY expiration dates and "##/####" for MM/YYYY format.string
dividerThe divider to use when replacing non-mask characters on submission. For example passing mask as "##/##" and divider as "-" will send expiration dates to VGS in MM-YY format.string
serializersA VGSSerializer array for the input field.VGSSerializer[]
validationRulesA ValidationRule array for the input field. A default one is inferred from the type prop.ValidationRule[]
onStateChangeA callback that is fired when the input field changes.(state: VGSTextInputState) => void
placeholderPlaceholder text for the input field.string
keyboardTypeThe keyboard type for the field. By default expiration date fields will receive the numeric keyboard."default" | "email-address" | "numeric" | "phone-pad"
secureTextEntryWhether the input field should have secure text entry. Defaults to false.boolean
autoCorrectWhether autocorrect should be enabled. Defaults to false.boolean
containerStyleAn object style for the container view.object
textStyleAn object style for the text input.object
tokenizationTokenization configuration for the field.false | VGSTokenizationConfiguration
testIdThe test ID for testing purposes.string

DynetiVGSCardInput

All props from DynetiVGSTextInput except for type can be supplied to DynetiVGSCardInput which also supports the following additional props.

Required props

PropDescriptionType
collectorA VGS Collect instanceVGSCollect
dyScanClientA DyScan client created by createFormClientDyScanFormClient

Addtional optional props

PropDescriptionType
iconWidthThe width of the card brand icon. Defaults to 42.number
iconHeightThe height of the card brand icon. Defaults to 24.number
iconPaddingPadding around the card brand icon. Defaults to 8.number
iconPositionThe position of the card brand and camera icon. Defaults to "right"."right" | "left" | "none"
containerHeightThe height of the container view. Defaults to 50.number
iconStyleStyle object for the card brand icon.object
showCameraButtonWhether to show a camera button inside the card input text field.boolean
customCameraIconA custom icon for the camera button.ImageSourcePropType
iconGapThe size of the gap between the card brand icon and the camera icon. Defaults to 4.number
cameraIconWidthThe width of the camera button. Defaults to match iconWidth which defaults to 42.number
cameraIconHeightThe height of the camera button. Defaults to match iconHeight which defaults to 24.number
cameraIconStyleStyle object for the card brand icon.object