react-native-cashfree-pg-sdk
Version:
Cashfree PG Plugin for React Native
204 lines (203 loc) • 7.78 kB
JavaScript
import { TextInput } from 'react-native';
import React, { forwardRef } from 'react';
import { CFSubsCardPayment, CFEnvironment, } from 'cashfree-pg-api-contract';
import { CFPaymentGatewayService } from '../index';
function luhnCheck(cardNumber) {
if (cardNumber.length === 0) {
return false;
}
cardNumber = cardNumber.replace(/\s/g, '');
let sum = 0;
let isAlternate = false;
for (let i = cardNumber.length - 1; i >= 0; i--) {
let digit = parseInt(cardNumber[i], 10);
if (isAlternate) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isAlternate = !isAlternate;
}
return sum % 10 === 0;
}
function getInputValidationDetails(cardBinResponse) {
if (!cardBinResponse || !cardBinResponse.scheme) {
return null;
}
const schemeType = cardBinResponse.scheme.toLowerCase();
let inputValidationDetails;
switch (schemeType) {
case 'amex':
inputValidationDetails = { max_input_length: 15, cvv_length: 4 };
break;
case 'diners':
inputValidationDetails = { max_input_length: 14, cvv_length: 3 };
break;
default: // Covers visa, mastercard, rupay, jcb, discover, and unknown schemes
inputValidationDetails = { max_input_length: 16, cvv_length: 3 };
}
return inputValidationDetails;
}
/**
* Fetching CardBin info with card bin data & CFSubscriptionSession object
* @param session : for subs sessionId & env
* @param bin : for card number
*/
async function getCardBin(session, bin) {
const route = `/pg/sdk/js/subscription/card/bin`;
const body = JSON.stringify({ card_number: bin });
let baseUrl = 'https://api.cashfree.com';
if (session.environment === CFEnvironment.SANDBOX) {
baseUrl = 'https://sandbox.cashfree.com';
}
const url = baseUrl + route;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-sub-session-id': session.subscription_session_id,
},
body,
});
if (!response.ok) {
return null;
}
return await response.json();
}
catch (error) {
return null;
}
}
const SubsCardInput = forwardRef(({ cfSubscriptionSession, cardListener, style, ...props }, ref) => {
const [inputNumber, setInputNumber] = React.useState('');
const inputNumberRef = React.useRef('');
const sessionRef = React.useRef(cfSubscriptionSession);
React.useImperativeHandle(ref, () => ({
doSubscriptionPayment,
doSubscriptionPaymentWithNewSession,
}));
const cardBinJsonRef = React.useRef(null);
const firstEightDigitsRef = React.useRef('');
const handleChange = React.useCallback(async (cardNumber) => {
let completeResponse = {};
const textWithoutSpaces = cardNumber.replaceAll(' ', '');
if (textWithoutSpaces.length === 0)
setInputNumber('');
let formattedText = '';
for (let i = 0; i < textWithoutSpaces.length; i += 4) {
let end = i + 4;
if (end > textWithoutSpaces.length) {
end = textWithoutSpaces.length;
}
formattedText += textWithoutSpaces.substring(i, end);
if (end !== textWithoutSpaces.length) {
formattedText += ' ';
}
inputNumberRef.current = formattedText;
setInputNumber((prev) => prev === formattedText ? prev : formattedText);
}
let cardBinResponse = null;
async function fetchCardBinAndSet() {
await getCardBin(cfSubscriptionSession, textWithoutSpaces)
.then((response) => {
cardBinResponse = response;
firstEightDigitsRef.current = textWithoutSpaces.substring(0, 8);
})
.catch(() => {
cardBinResponse = null;
});
if (cardBinResponse) {
cardBinJsonRef.current = cardBinResponse;
completeResponse.card_bin_info = cardBinJsonRef.current;
completeResponse.input_validation = getInputValidationDetails(cardBinJsonRef.current);
}
}
if (textWithoutSpaces.length === 8) {
await fetchCardBinAndSet();
}
else if (textWithoutSpaces.length > 8) {
if (firstEightDigitsRef.current === textWithoutSpaces.substring(0, 8)) {
completeResponse.card_bin_info = cardBinJsonRef.current;
completeResponse.input_validation = getInputValidationDetails(cardBinJsonRef.current);
}
else {
cardBinJsonRef.current = null;
await fetchCardBinAndSet();
}
}
if (textWithoutSpaces.length < 8) {
cardBinJsonRef.current = null;
firstEightDigitsRef.current = '';
}
if (cardBinJsonRef.current !== null) {
completeResponse.card_network = cardBinJsonRef.current.scheme;
}
let luhnStatus = luhnCheck(textWithoutSpaces);
if (luhnStatus) {
completeResponse.luhn_check_info = 'SUCCESS';
if (textWithoutSpaces && textWithoutSpaces.length > 4) {
completeResponse.last_four_digit = textWithoutSpaces.substring(textWithoutSpaces.length - 4);
}
}
else {
completeResponse.luhn_check_info = 'FAIL';
}
completeResponse.card_length = textWithoutSpaces.length;
return cardListener(JSON.stringify(completeResponse));
}, [cfSubscriptionSession, cardListener]);
const doSubscriptionPayment = (cardInfo) => {
try {
let cfCardNumber = inputNumberRef.current;
cardInfo.cardNumber = cfCardNumber.replaceAll(' ', '');
const cardPayment = new CFSubsCardPayment(sessionRef.current, cardInfo);
CFPaymentGatewayService.makeSubsPayment(cardPayment);
}
catch (e) {
console.log(e.message);
}
};
const doSubscriptionPaymentWithNewSession = (cardInfo, session) => {
try {
sessionRef.current = session;
doSubscriptionPayment(cardInfo);
}
catch (e) {
console.log(e.message);
}
};
const handleSubmitEditingEvent = (event) => {
const newEvent = { ...event };
delete newEvent.nativeEvent.text;
if (onSubmitEditing) {
onSubmitEditing(newEvent);
}
};
const handleEndEditingEvent = (event) => {
const newEvent = { ...event };
delete newEvent.nativeEvent.text;
if (onEndEditing) {
onEndEditing(newEvent);
}
};
const handleFocusEvent = (event) => {
const newEvent = { ...event };
delete newEvent.nativeEvent.text;
if (onFocus) {
onFocus(newEvent);
}
};
const handleBlurEvent = (event) => {
const newEvent = { ...event };
delete newEvent.nativeEvent.text;
if (onBlur) {
onBlur(newEvent);
}
};
const InputComponent = TextInput;
const { onChangeText, onChange, onSubmitEditing, onEndEditing, onFocus, onBlur, ...otherProps } = props;
return (React.createElement(InputComponent, { keyboardType: "numeric", inputMode: 'numeric', value: inputNumber, onChangeText: handleChange, onSubmitEditing: handleSubmitEditingEvent, onEndEditing: handleEndEditingEvent, onFocus: handleFocusEvent, onBlur: handleBlurEvent, style: style, ...otherProps }));
});
export default SubsCardInput;