Skip to content

Commit

Permalink
Merge pull request #88 from skyflowapi/SK-1611
Browse files Browse the repository at this point in the history
SK-1611 card brand choice implementation
  • Loading branch information
skyflow-shravan authored Oct 4, 2024
2 parents 6cfe699 + 285641d commit ee238e6
Show file tree
Hide file tree
Showing 31 changed files with 617 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/beta-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
- 'package.json'
- 'package-lock.json'
- '*.md'

jobs:
build-sdk:
runs-on: ubuntu-latest
Expand Down
36 changes: 36 additions & 0 deletions __tests__/components/Dropdown.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import Dropdown from '../../src/core/Dropdown';

describe('Verify dropdown component', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('should test the dropdown by selecting list item', () => {
const dropdown = render(
<Dropdown
setSelectedValue={() => {}}
listData={[{ label: 'test', value: 'test' }]}
/>
);
const cardIcon = dropdown.getByTestId('dropdown-icon')
fireEvent.press(cardIcon)
const listItem = dropdown.getByTestId('list-item');
fireEvent.press(listItem);
});

it('should test the dropdown by opening and closing', () => {
const dropdown = render(
<Dropdown
setSelectedValue={() => {}}
listData={[{ label: 'test', value: 'test' }]}
/>
);
const cardIcon = dropdown.getByTestId('dropdown-icon')
fireEvent.press(cardIcon)
const modalClose = dropdown.getByTestId('modal-close');
fireEvent.press(modalClose);
const modal = dropdown.getByTestId('modal');
fireEvent.press(modal);
});
});
51 changes: 41 additions & 10 deletions __tests__/components/__snapshots__/components.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,47 @@ exports[`test Collect And Reveal Elements Components test CardNumberElement comp
*
</Text>
</Text>
<TextInput
keyboardType="numeric"
maxLength={23}
onBlur={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
placeholder="card number"
style={Object {}}
value=""
/>
<View
style={
Object {
"borderColor": "#eae8ee",
"borderRadius": 4,
"borderWidth": 2,
"flexDirection": "row",
"gap": 4,
"paddingHorizontal": 6,
}
}
>
<Image
resizeMode="contain"
source={
Object {
"testUri": "../../../assets/visa.png",
}
}
style={
Object {
"height": 50,
"width": 50,
}
}
/>
<TextInput
keyboardType="numeric"
maxLength={23}
onBlur={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
placeholder="card number"
style={
Object {
"flex": 1,
}
}
value=""
/>
</View>
<Text
style={Object {}}
/>
Expand Down
22 changes: 21 additions & 1 deletion __tests__/core/collectElement.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe('test Collect Element class', () => {
isEmpty: false,
value: '41111111X',
isValid: false,
selectedCardScheme: '',
});
expect(cardNumberElement.getErrorText()).toBe(
`Invalid ${elementInput.label}`
Expand Down Expand Up @@ -492,4 +493,23 @@ describe('test Collect Element class', () => {
collectElement.onChangeElement('20');
expect(collectElement.getInternalState().isValid).toBe(false);
});
});

it('should test onDropdownSelect', () => {
const elementInput = {
table: 'cards',
column: 'string1',
type: ElementType.CARD_NUMBER,
label: 'Card Number',
onChange: onChangeMock,
containerType: ContainerType.COLLECT,
};
const cardNumberElement = new CollectElement(
elementInput,
{ required: true },
context
);
cardNumberElement.onDropdownSelect(CardType.DISCOVER);
cardNumberElement.onChangeElement('', true);
expect(cardNumberElement.getClientState().selectedCardScheme).toEqual('DISCOVER');
})
});
Binary file added assets/amex.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/carter-banceris.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/copyIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/diners-club.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/discover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/drop-down.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/hipercard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/jcb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/maestro.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/mastercard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/path.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/unionpay.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/unknown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/visa.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import { Button, SafeAreaView, View } from 'react-native';
import { SkyflowProvider, LogLevel, IConfig, Env } from "skyflow-react-native"
import ElementView from './ElementView';
import ComposableElements from './ComposableElements';
import CardBrandChoice from './CoBrandedCard';

const App = () => {

const [displayComposable, setDisplayComposable] = React.useState(false);
const [displayCollect, setDisplayCollect] = React.useState(false);
const [displayCobrandedCard, setDisplayCobrandedCard] = React.useState(false);

const handleReset = () => {
setDisplayCollect(false);
setDisplayComposable(false);
setDisplayCobrandedCard(false);
}

const skyflowConfig: IConfig = {
Expand Down Expand Up @@ -54,17 +57,21 @@ const App = () => {
return (
<SafeAreaView style={{ flex: 1 }}>
<SkyflowProvider config={skyflowConfig}>
{!displayCollect && !displayComposable && <>
{!displayCollect && !displayComposable && !displayCobrandedCard && <>
<View style={{ margin: 10 }}>
<Button onPress={() => { setDisplayCollect(true) }} title='Collect And Reveal Elements' />
</View>
<View style={{ margin: 10 }}>
<Button onPress={() => { setDisplayComposable(true) }} title='Composable Elements' />
</View>
<View style={{ margin: 10 }}>
<Button onPress={() => { setDisplayCobrandedCard(true) }} title='Co-branded Card' />
</View>
</>}

{displayCollect && <ElementView handleReset={handleReset} />}
{displayComposable && <ComposableElements handleReset={handleReset} />}
{displayCobrandedCard && <CardBrandChoice handleReset={handleReset} />}
</SkyflowProvider>
</SafeAreaView>
);
Expand Down
201 changes: 201 additions & 0 deletions example/src/CoBrandedCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
Copyright (c) 2022 Skyflow, Inc.
*/

import React from 'react';
import { Button, StyleSheet, View } from 'react-native';
import {
CardHolderNameElement,
CardNumberElement,
ExpirationDateElement,
InputFieldElement,
useCollectContainer,
CardType
} from 'skyflow-react-native';

const CoBrandedCard = (props) => {

const collectContainer = useCollectContainer();

const handleCollect = () => {
collectContainer
.collect()
.then((response: any) => {
console.log('Collect Success: ', JSON.stringify(response));
const fieldsTokenData = response.records[0].fields;
})
.catch((err) => {
console.error('Collect Failed: ', err);
});
};

const binLookup = (bin) => {
const myHeaders = new Headers();
myHeaders.append("X-skyflow-authorization", "{BEARER_TOKEN}"); // TODO: replace bearer token
myHeaders.append("Content-Type", "application/json");
const raw = JSON.stringify({
"BIN": bin
});
const requestOptions = {
method: "POST",
headers: myHeaders,
body: raw,
redirect: "follow"
};
return fetch("https://<VAULT_URL>/v1/card_lookup", requestOptions);
};

const getCardSchemes = (cardData)=>{
let schemeList = [];
cardData.forEach((card)=>{
if(card.card_scheme === 'VISA'){
schemeList.push(CardType.VISA);
}else if(card.card_scheme === 'MASTERCARD'){
schemeList.push(CardType.MASTERCARD)
}else if(card.card_scheme === 'CARTES BANCAIRES'){
schemeList.push(CardType.CARTES_BANCAIRES)
}
})
return schemeList
}

const [scheme, setScheme] = React.useState<CardType[]>([]);

let calledUpdate = false;

const handleOnChange = (state) => {
console.log("onChange event triggered: ", state)
const currentBin = state.value.slice(0, 8);
if (currentBin.length >= 8 && !calledUpdate) {
calledUpdate = true;
// Perform Bin Lookup
binLookup(currentBin)
.then((response) => response.text())
.then((result) => {
console.log("RESULT OF BIN_LOOKUP: ", result)
const cardData = JSON.parse(result)['cards_data'];
const schemeList = getCardSchemes(cardData);
if(schemeList.length >= 2) {
setScheme(schemeList);
}
})
.catch((error) => console.error(error));
} else if(currentBin.length < 8 && calledUpdate){
calledUpdate = false
setScheme([])
}
};



return (
<View style={viewStyles.container}>
<View style={viewStyles.box}>
<CardNumberElement
container={collectContainer}
table="cards"
column="card_number"
placeholder="XXXX XXXX XXXX XXXX"
label={'Card number'}
inputStyles={cardNumElementInputStyles}
labelStyles={elementLabelStyles}
errorTextStyles={errorTextStyles}
onChange={handleOnChange}
options={{
cardMetadata: { scheme: scheme }
}}
/>
</View>
<View style={viewStyles.box}>
<ExpirationDateElement
container={collectContainer}
table='cards'
column='expiration_date'
placeholder='MM/YYYY'
label='Expiration Date'
options={{
format: 'MM/YYYY',
}}
inputStyles={elementInputStyles}
errorTextStyles={errorTextStyles}
/>
</View>
<View style={viewStyles.box}>
<CardHolderNameElement
container={collectContainer}
table='cards'
column='cardholder_name'
placeholder={'Name'}
label={'Cardholder name'}
inputStyles={elementInputStyles}
labelStyles={elementLabelStyles}
errorTextStyles={errorTextStyles}
/>
</View>
<View style={viewStyles.box}>
<InputFieldElement
container={collectContainer}
table='cards'
column='ssn'
placeholder='XXX-XX-XXXX'
label='SSN'
inputStyles={elementInputStyles}
errorTextStyles={errorTextStyles}
/>
</View>
<View style={viewStyles.box}>
<Button title="Collect" onPress={handleCollect} />
</View>
<View style={viewStyles.box}>
<Button title="Reset" onPress={props.handleReset} />
</View>
</View>
);
};

const cardNumElementInputStyles = StyleSheet.create({
base: {
color: '#1d1d1d',
},
invalid: {
color: '#f44336',
},
});


const elementInputStyles = StyleSheet.create({
base: {
borderWidth: 2,
borderRadius: 4,
borderColor: '#eae8ee',
paddingVertical: 8,
paddingHorizontal: 6,
color: '#1d1d1d',
},
invalid: {
color: '#f44336',
},
});

const elementLabelStyles = StyleSheet.create({
focus: {
fontWeight: 'bold',
},
});

const errorTextStyles = StyleSheet.create({
base: {
color: '#f44336',
},
});

const viewStyles = StyleSheet.create({
box: {
marginVertical: 5,
},
container: {
padding: 10
}
});

export default CoBrandedCard;
Loading

0 comments on commit ee238e6

Please sign in to comment.