Skip to content

Commit

Permalink
feat: unsupported card networks validation (#370)
Browse files Browse the repository at this point in the history
Co-authored-by: Pritish Budhiraja <1805317@kiit.ac.in>
  • Loading branch information
vsrivatsa-edinburgh and Pritish Budhiraja committed May 23, 2024
1 parent 0d89c63 commit 05f527a
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/CardUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type cardIssuer =
type cardProps = (
option<bool>,
(option<bool> => option<bool>) => unit,
option<bool>,
string,
JsxEvent.Form.t => unit,
JsxEvent.Focus.t => unit,
Expand Down
2 changes: 2 additions & 0 deletions src/Components/DynamicFields.res
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ let make = (
let defaultCardProps = (
None,
_ => (),
None,
"",
_ => (),
_ => (),
Expand Down Expand Up @@ -154,6 +155,7 @@ let make = (
let (
isCardValid,
setIsCardValid,
_,
cardNumber,
changeCardNumber,
handleCardBlur,
Expand Down
58 changes: 54 additions & 4 deletions src/Payment.res
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
let showFields = Recoil.useRecoilValueFromAtom(showCardFieldsAtom)
let selectedOption = Recoil.useRecoilValueFromAtom(selectedOptionAtom)
let paymentToken = Recoil.useRecoilValueFromAtom(paymentTokenAtom)
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)

let {iframeId} = keys

Expand Down Expand Up @@ -43,6 +44,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
let (isExpiryValid, setIsExpiryValid) = React.useState(_ => None)
let (isCVCValid, setIsCVCValid) = React.useState(_ => None)
let (isZipValid, setIsZipValid) = React.useState(_ => None)
let (isCardSupported, setIsCardSupported) = React.useState(_ => None)

let (cardBrand, maxCardLength) = React.useMemo(() => {
let brand = getCardBrand(cardNumber)
Expand All @@ -51,6 +53,46 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
!showFields && isNotBancontact ? (cardScheme, maxLength) : (brand, maxLength)
}, (cardNumber, cardScheme, showFields))

let supportedCardBrands = React.useMemo(() => {
let cardPaymentMethod =
paymentMethodListValue.payment_methods->Array.find(ele => ele.payment_method === "card")

switch cardPaymentMethod {
| Some(cardPaymentMethod) =>
let cardNetworks = cardPaymentMethod.payment_method_types->Array.map(ele => ele.card_networks)
let cardNetworkNames =
cardNetworks->Array.map(ele =>
ele->Array.map(
val => val.card_network->CardUtils.getCardStringFromType->String.toLowerCase,
)
)
Some(
cardNetworkNames
->Array.reduce([], (acc, ele) => acc->Array.concat(ele))
->Utils.getUniqueArray,
)
| None => None
}
}, [paymentMethodListValue])

let checkIsCardSupported = cardNumber => {
let cardBrand = cardNumber->CardUtils.getCardBrand
let clearValue = cardNumber->clearSpaces
if cardValid(clearValue, cardBrand) {
switch supportedCardBrands {
| Some(brands) => Some(brands->Array.includes(cardBrand->String.toLowerCase))
| None => Some(true)
}
} else {
None
}
}

React.useEffect(() => {
setIsCardSupported(_ => checkIsCardSupported(cardNumber))
None
}, (supportedCardBrands, cardNumber))

let cardType = React.useMemo1(() => {
cardBrand->getCardType
}, [cardBrand])
Expand Down Expand Up @@ -92,7 +134,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
let card = val->formatCardNumber(cardType)
let clearValue = card->clearSpaces
setCardValid(clearValue, setIsCardValid)
if cardValid(clearValue, cardBrand) {
if cardValid(clearValue, cardBrand) && checkIsCardSupported(clearValue)->Option.getOr(false) {
handleInputFocus(~currentRef=cardRef, ~destinationRef=expiryRef)
}
if card->String.length > 6 && cardNumber->pincodeVisibility {
Expand Down Expand Up @@ -153,7 +195,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
let handleCardBlur = ev => {
let cardNumber = ReactEvent.Focus.target(ev)["value"]
if cardNumberInRange(cardNumber)->Array.includes(true) && calculateLuhn(cardNumber) {
setIsCardValid(_ => Some(true))
setIsCardValid(_ => checkIsCardSupported(cardNumber))
} else if cardNumber->String.length == 0 {
setIsCardValid(_ => None)
} else {
Expand Down Expand Up @@ -336,9 +378,16 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
}, (cardNumber, cvcNumber, cardExpiry, isCVCValid, isExpiryValid, isCardValid))

React.useEffect(() => {
setCardError(_ => isCardValid->Option.getOr(true) ? "" : localeString.inValidCardErrorText)
let cardError = if isCardSupported->Option.getOr(true) && isCardValid->Option.getOr(true) {
""
} else if isCardSupported->Option.getOr(true) {
localeString.inValidCardErrorText
} else {
localeString.cardBrandConfiguredErrorText(cardBrand)
}
setCardError(_ => cardError)
None
}, [isCardValid])
}, [isCardValid, isCardSupported])

React.useEffect(() => {
setCvcError(_ => isCVCValid->Option.getOr(true) ? "" : localeString.inCompleteCVCErrorText)
Expand Down Expand Up @@ -366,6 +415,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
let cardProps: CardUtils.cardProps = (
isCardValid,
setIsCardValid,
isCardSupported,
cardNumber,
changeCardNumber,
handleCardBlur,
Expand Down
26 changes: 20 additions & 6 deletions src/Payments/CardPayment.res
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let make = (
let (
isCardValid,
setIsCardValid,
isCardSupported,
cardNumber,
changeCardNumber,
handleCardBlur,
Expand Down Expand Up @@ -99,7 +100,14 @@ let make = (

let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid)

let complete = isAllValid(isCardValid, isCVCValid, isExpiryValid, true, "payment")
let complete = isAllValid(
isCardValid,
isCardSupported,
isCVCValid,
isExpiryValid,
true,
"payment",
)
let empty = cardNumber == "" || cardExpiry == "" || cvcNumber == ""
React.useEffect(() => {
setComplete(_ => complete)
Expand Down Expand Up @@ -152,8 +160,13 @@ let make = (
defaultCardBody
}
if confirm.doSubmit {
let validFormat = (isBancontact || complete) && areRequiredFieldsValid
if validFormat && (showFields || isBancontact) && isCardBrandValid {
let validFormat =
(isBancontact ||
(isCVCValid->Option.getOr(false) &&
isCardValid->Option.getOr(false) &&
isCardSupported->Option.getOr(false) &&
isExpiryValid->Option.getOr(false))) && areRequiredFieldsValid
if validFormat && (showFields || isBancontact) {
intent(
~bodyArr={
(isBancontact ? banContactBody : cardBody)
Expand All @@ -179,12 +192,13 @@ let make = (
setCvcError(_ => localeString.cvcNumberEmptyText)
setUserError(localeString.enterFieldsText)
}
if isCardSupported->Option.getOr(true)->not {
setCardError(_ => localeString.cardBrandConfiguredErrorText(cardBrand))
setUserError(localeString.cardBrandConfiguredErrorText(cardBrand))
}
if !validFormat {
setUserError(localeString.enterValidDetailsText)
}
if !isCardBrandValid {
setUserError(localeString.cardBrandConfiguredErrorText(cardBrand))
}
}
}
}, (
Expand Down
1 change: 1 addition & 0 deletions src/RenderPaymentMethods.res
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ let make = (
let (
isCardValid,
setIsCardValid,
_,
cardNumber,
changeCardNumber,
handleCardBlur,
Expand Down
1 change: 1 addition & 0 deletions src/SingleLineCardPayment.res
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ let make = (
let (
isCardValid,
setIsCardValid,
_,
cardNumber,
changeCardNumber,
handleCardBlur,
Expand Down
2 changes: 2 additions & 0 deletions src/Utilities/Utils.res
Original file line number Diff line number Diff line change
Expand Up @@ -460,12 +460,14 @@ let sortBasedOnPriority = (sortArr: array<string>, priorityArr: array<string>) =

let isAllValid = (
card: option<bool>,
cardSupported: option<bool>,
cvc: option<bool>,
expiry: option<bool>,
zip: bool,
paymentMode: string,
) => {
card->getBoolValue &&
cardSupported->getBoolValue &&
cvc->getBoolValue &&
expiry->getBoolValue &&
(paymentMode == "payment" || zip)
Expand Down
6 changes: 2 additions & 4 deletions webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const BundleAnalyzerPlugin =
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");

const sdkEnv = process.env.sdkEnv ?? "local";
Expand All @@ -20,8 +19,7 @@ let repoVersion = require("./package.json").version;
let majorVersion = "v" + repoVersion.split(".")[0];

let repoName = require("./package.json").name;
let repoPublicPath =
sdkEnv === "local" ? "" : `/${repoVersion}/${majorVersion}`;
let repoPublicPath = sdkEnv === "local" ? "" : `/${repoVersion}/${majorVersion}`;

let sdkUrl;

Expand Down

0 comments on commit 05f527a

Please sign in to comment.