From f3f404733f7c13eb69589779ea2e2c8a6569259d Mon Sep 17 00:00:00 2001
From: ArushKapoorJuspay <121166031+ArushKapoorJuspay@users.noreply.github.com>
Date: Wed, 13 Mar 2024 14:23:14 +0530
Subject: [PATCH] feat: cvc nickname gpay (#224)
Co-authored-by: Pritish Budhiraja <1805317@kiit.ac.in>
---
src/Components/DynamicFields.res | 15 ++---
src/Components/NicknamePaymentInput.res | 19 +++++++
src/Components/SavedCardItem.res | 19 ++++---
src/Components/SavedMethods.res | 13 ++++-
src/LocaleString.res | 18 ++++++
src/Payment.res | 2 +
src/PaymentElement.res | 25 +++++++-
src/Payments/CardPayment.res | 43 ++++++++++----
src/Types/PaymentType.res | 7 +++
src/Utilities/DynamicFieldsUtils.res | 21 +++++++
src/Utilities/PaymentBody.res | 76 +++++++++++++++----------
src/Utilities/PaymentUtils.res | 8 ++-
12 files changed, 200 insertions(+), 66 deletions(-)
create mode 100644 src/Components/NicknamePaymentInput.res
diff --git a/src/Components/DynamicFields.res b/src/Components/DynamicFields.res
index 4310b59a..96a4af12 100644
--- a/src/Components/DynamicFields.res
+++ b/src/Components/DynamicFields.res
@@ -21,16 +21,11 @@ let make = (
let {billingAddress} = Recoil.useRecoilValueFromAtom(optionAtom)
//<...>//
- let paymentMethodTypes = React.useMemo3(() => {
- PaymentMethodsRecord.getPaymentMethodTypeFromList(
- ~list,
- ~paymentMethod,
- ~paymentMethodType=PaymentUtils.getPaymentMethodName(
- ~paymentMethodType=paymentMethod,
- ~paymentMethodName=paymentMethodType,
- ),
- )->Belt.Option.getWithDefault(PaymentMethodsRecord.defaultPaymentMethodType)
- }, (list, paymentMethod, paymentMethodType))
+ let paymentMethodTypes = DynamicFieldsUtils.usePaymentMethodTypeFromList(
+ ~list,
+ ~paymentMethod,
+ ~paymentMethodType,
+ )
let requiredFieldsWithBillingDetails = React.useMemo3(() => {
if paymentMethod === "card" {
diff --git a/src/Components/NicknamePaymentInput.res b/src/Components/NicknamePaymentInput.res
new file mode 100644
index 00000000..941a26df
--- /dev/null
+++ b/src/Components/NicknamePaymentInput.res
@@ -0,0 +1,19 @@
+@react.component
+let make = (~paymentType: CardThemeType.mode, ~value, ~setValue) => {
+ let {config, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
+
+ let onChange = ev => {
+ let val = ReactEvent.Form.target(ev)["value"]
+ setValue(_ => val)
+ }
+
+
+}
diff --git a/src/Components/SavedCardItem.res b/src/Components/SavedCardItem.res
index 4bf71a45..59f9a243 100644
--- a/src/Components/SavedCardItem.res
+++ b/src/Components/SavedCardItem.res
@@ -48,6 +48,7 @@ let make = (
}, [isActive])
let isCard = paymentItem.paymentMethod === "card"
+ let isRenderCvv = isCard && paymentItem.requiresCvv
let paymentMethodType = switch paymentItem.paymentMethodType {
| Some(paymentMethodType) => paymentMethodType->Utils.snakeToTitleCase
@@ -90,15 +91,15 @@ let make = (
border="1px solid currentColor"
/>
-
- brandIcon
-
-
+
brandIcon
+
{isCard
- ?
-
{React.string(`****`)}
-
{React.string({paymentItem.card.last4Digits})}
+ ?
+
{React.string(paymentItem.card.nickname)}
+
+
{React.string(`****`)}
+
{React.string(paymentItem.card.last4Digits)}
+
:
{React.string(paymentMethodType)}
}
@@ -125,7 +126,7 @@ let make = (
-
+
diff --git a/src/Components/SavedMethods.res b/src/Components/SavedMethods.res
index 3a5bac83..d85894e9 100644
--- a/src/Components/SavedMethods.res
+++ b/src/Components/SavedMethods.res
@@ -83,8 +83,16 @@ let make = (
->Belt.Array.get(0)
->Belt.Option.getWithDefault(PaymentType.defaultCustomerMethods)
let isCardPaymentMethod = customerMethod.paymentMethod === "card"
+ let isCardPaymentMethodValid = !customerMethod.requiresCvv || (complete && !empty)
+
let savedPaymentMethodBody = switch customerMethod.paymentMethod {
- | "card" => PaymentBody.savedCardBody(~paymentToken=token, ~customerId, ~cvcNumber)
+ | "card" =>
+ PaymentBody.savedCardBody(
+ ~paymentToken=token,
+ ~customerId,
+ ~cvcNumber,
+ ~requiresCvv=customerMethod.requiresCvv,
+ )
| _ => {
let paymentMethodType = switch customerMethod.paymentMethodType {
| Some("")
@@ -99,8 +107,9 @@ let make = (
)
}
}
+
if confirm.doSubmit {
- if areRequiredFieldsValid && (!isCardPaymentMethod || (complete && !empty)) {
+ if areRequiredFieldsValid && (!isCardPaymentMethod || isCardPaymentMethodValid) {
intent(
~bodyArr=savedPaymentMethodBody
->Js.Dict.fromArray
diff --git a/src/LocaleString.res b/src/LocaleString.res
index 1c89b232..26de81df 100644
--- a/src/LocaleString.res
+++ b/src/LocaleString.res
@@ -67,6 +67,8 @@ type localeStrings = {
useExistingPaymentMethods: string,
selectPaymentMethodLabel: string,
savedPaymentMethodsLabel: string,
+ nicknameLabel: string,
+ nicknamePlaceholder: string,
}
let defaultLocale = {
@@ -149,6 +151,8 @@ let defaultLocale = {
useExistingPaymentMethods: "Use saved payment methods",
selectPaymentMethodLabel: "Select Payment Method",
savedPaymentMethodsLabel: "Saved Payment Methods",
+ nicknameLabel: "Card Nickname",
+ nicknamePlaceholder: "Card Nickname (Optional)",
}
type locale = {localeStrings: array
}
@@ -233,6 +237,8 @@ let localeStrings = [
useExistingPaymentMethods: "Use saved payment methods",
selectPaymentMethodLabel: "Select Payment Method",
savedPaymentMethodsLabel: "Saved Payment Methods",
+ nicknameLabel: "Card Nickname",
+ nicknamePlaceholder: "Card Nickname (Optional)",
},
{
locale: "he",
@@ -314,6 +320,8 @@ let localeStrings = [
useExistingPaymentMethods: `השתמש באמצעי תשלום שמורים`,
selectPaymentMethodLabel: `בחר שיטת תשלום`,
savedPaymentMethodsLabel: `אמצעי תשלום שמורים`,
+ nicknameLabel: `כינוי לכרטיס`,
+ nicknamePlaceholder: `כינוי לכרטיס (אופציונלי)`,
},
{
locale: `fr`,
@@ -395,6 +403,8 @@ let localeStrings = [
useExistingPaymentMethods: `Utiliser les modes de paiement enregistrés`,
selectPaymentMethodLabel: `Sélectionnez le mode de paiement`,
savedPaymentMethodsLabel: `Modes de paiement enregistrés`,
+ nicknameLabel: `Pseudonyme de la carte`,
+ nicknamePlaceholder: `Surnom de la carte (facultatif)`,
},
{
locale: "en-GB",
@@ -476,6 +486,8 @@ let localeStrings = [
useExistingPaymentMethods: "Use saved payment methods",
selectPaymentMethodLabel: "Select Payment Method",
savedPaymentMethodsLabel: "Saved Payment Methods",
+ nicknameLabel: "Card Nickname",
+ nicknamePlaceholder: "Card Nickname (Optional)",
},
{
locale: "ar",
@@ -557,6 +569,8 @@ let localeStrings = [
useExistingPaymentMethods: `استخدم طرق الدفع المحفوظة`,
selectPaymentMethodLabel: `اختار طريقة الدفع`,
savedPaymentMethodsLabel: `طرق الدفع المحفوظة`,
+ nicknameLabel: `الاسم علي الكارت`,
+ nicknamePlaceholder: `اسم البطاقة (اختياري)`,
},
{
locale: "ja",
@@ -638,6 +652,8 @@ let localeStrings = [
useExistingPaymentMethods: `保存した支払い方法を使用する`,
selectPaymentMethodLabel: `支払い方法を選択してください`,
savedPaymentMethodsLabel: `保存された支払い方法`,
+ nicknameLabel: `カードのニックネーム`,
+ nicknamePlaceholder: `カードニックネーム(任意)`,
},
{
locale: "de",
@@ -719,5 +735,7 @@ let localeStrings = [
useExistingPaymentMethods: `Gespeicherte Zahlungsarten nutzen`,
selectPaymentMethodLabel: `Wählen Sie die Zahlungsmethode`,
savedPaymentMethodsLabel: `Gespeicherte Zahlungsarten`,
+ nicknameLabel: `Spitzname der Karte`,
+ nicknamePlaceholder: `Kartenname (optional)`,
},
]
diff --git a/src/Payment.res b/src/Payment.res
index 8ec944f5..230f0fc5 100644
--- a/src/Payment.res
+++ b/src/Payment.res
@@ -252,6 +252,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
~cardHolderName="",
~cvcNumber,
~cardBrand=cardNetwork,
+ (),
)
| CardNumberElement =>
let (month, year) = getExpiryDates(getCardElementValue(iframeId, "card-expiry"))
@@ -263,6 +264,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
~cardHolderName="",
~cvcNumber=localCvcNumber,
~cardBrand=cardNetwork,
+ (),
)
| _ => []
}
diff --git a/src/PaymentElement.res b/src/PaymentElement.res
index 9c38dbec..11547b33 100644
--- a/src/PaymentElement.res
+++ b/src/PaymentElement.res
@@ -108,7 +108,19 @@ let make = (
None
}, [savedMethods])
- let (walletList, paymentOptionsList, actualList) = React.useMemo4(() => {
+ let areAllGooglePayRequiredFieldsPrefilled = DynamicFieldsUtils.useAreAllRequiredFieldsPrefilled(
+ ~list,
+ ~paymentMethod="wallet",
+ ~paymentMethodType="google_pay",
+ )
+
+ let areAllApplePayRequiredFieldsPrefilled = DynamicFieldsUtils.useAreAllRequiredFieldsPrefilled(
+ ~list,
+ ~paymentMethod="wallet",
+ ~paymentMethodType="apple_pay",
+ )
+
+ let (walletList, paymentOptionsList, actualList) = React.useMemo6(() => {
switch methodslist {
| Loaded(paymentlist) =>
let paymentOrder =
@@ -119,6 +131,8 @@ let make = (
~order=paymentOrder,
~showApplePay=isApplePayReady,
~showGooglePay=isGooglePayReady,
+ ~areAllGooglePayRequiredFieldsPrefilled,
+ ~areAllApplePayRequiredFieldsPrefilled,
)
(
wallets->Utils.removeDuplicate,
@@ -131,7 +145,14 @@ let make = (
: ([], [], [])
| _ => ([], [], [])
}
- }, (methodslist, paymentMethodOrder, isApplePayReady, isGooglePayReady))
+ }, (
+ methodslist,
+ paymentMethodOrder,
+ isApplePayReady,
+ isGooglePayReady,
+ areAllGooglePayRequiredFieldsPrefilled,
+ areAllApplePayRequiredFieldsPrefilled,
+ ))
React.useEffect4(() => {
switch methodslist {
diff --git a/src/Payments/CardPayment.res b/src/Payments/CardPayment.res
index ff551570..2656b500 100644
--- a/src/Payments/CardPayment.res
+++ b/src/Payments/CardPayment.res
@@ -16,6 +16,9 @@ let make = (
let {config, themeObj, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom)
let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)
+
+ let (nickname, setNickname) = React.useState(_ => "")
+
let (
isCardValid,
setIsCardValid,
@@ -100,7 +103,15 @@ let make = (
~isCvcValidValue,
)
- let submitCallback = React.useCallback5((ev: Window.event) => {
+ let isCustomerAcceptanceRequired = React.useMemo1(() => {
+ if displaySavedPaymentMethodsCheckbox {
+ isSaveCardsChecked || list.payment_type === SETUP_MANDATE
+ } else {
+ !(isGuestCustomer || list.payment_type === NORMAL)
+ }
+ }, [isSaveCardsChecked])
+
+ let submitCallback = React.useCallback6((ev: Window.event) => {
let json = ev.data->Js.Json.parseExn
let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper
let (month, year) = CardUtils.getExpiryDates(cardExpiry)
@@ -120,18 +131,14 @@ let make = (
~cardHolderName="",
~cvcNumber,
~cardBrand=cardNetwork,
+ ~nickname,
+ (),
)
let banContactBody = PaymentBody.bancontactBody()
- let cardBody = if displaySavedPaymentMethodsCheckbox {
- if isSaveCardsChecked || list.payment_type === SETUP_MANDATE {
- defaultCardBody->Js.Array2.concat(onSessionBody)
- } else {
- defaultCardBody
- }
- } else if isGuestCustomer || list.payment_type === NORMAL {
- defaultCardBody
- } else {
+ let cardBody = if isCustomerAcceptanceRequired {
defaultCardBody->Js.Array2.concat(onSessionBody)
+ } else {
+ defaultCardBody
}
if confirm.doSubmit {
let validFormat =
@@ -171,7 +178,14 @@ let make = (
}
}
}
- }, (areRequiredFieldsValid, requiredFieldsBody, empty, complete, isSaveCardsChecked))
+ }, (
+ areRequiredFieldsValid,
+ requiredFieldsBody,
+ empty,
+ complete,
+ isCustomerAcceptanceRequired,
+ nickname,
+ ))
submitPaymentData(submitCallback)
let paymentMethod = isBancontact ? "bank_redirect" : "card"
@@ -182,6 +196,8 @@ let make = (
options.displaySavedPaymentMethodsCheckbox &&
!isBancontact
+ let nicknameFieldClassName = conditionsForShowingSaveCardCheckbox ? "pt-2" : "pt-5"
+
+
+
+
+
+
Array.length > 0 &&
diff --git a/src/Types/PaymentType.res b/src/Types/PaymentType.res
index 43fa329c..86246d00 100644
--- a/src/Types/PaymentType.res
+++ b/src/Types/PaymentType.res
@@ -114,6 +114,7 @@ type customerCard = {
expiryYear: string,
cardToken: string,
cardHolderName: option,
+ nickname: string,
}
type customerMethods = {
paymentToken: string,
@@ -123,6 +124,7 @@ type customerMethods = {
card: customerCard,
paymentMethodType: option,
defaultPaymentMethodSet: bool,
+ requiresCvv: bool,
}
type savedCardsLoadState =
LoadingSavedCards | LoadedSavedCards(array, bool) | NoResult(bool)
@@ -164,6 +166,7 @@ let defaultCardDetails = {
expiryYear: "",
cardToken: "",
cardHolderName: None,
+ nickname: "",
}
let defaultCustomerMethods = {
paymentToken: "",
@@ -173,6 +176,7 @@ let defaultCustomerMethods = {
card: defaultCardDetails,
paymentMethodType: None,
defaultPaymentMethodSet: false,
+ requiresCvv: true,
}
let defaultLayout = {
defaultCollapsed: false,
@@ -780,6 +784,7 @@ let getCardDetails = (dict, str) => {
expiryYear: getString(json, "expiry_year", ""),
cardToken: getString(json, "card_token", ""),
cardHolderName: Some(getString(json, "card_holder_name", "")),
+ nickname: getString(json, "nick_name", ""),
}
})
->Belt.Option.getWithDefault(defaultCardDetails)
@@ -820,6 +825,7 @@ let createCustomerObjArr = dict => {
card: getCardDetails(dict, "card"),
paymentMethodType: getPaymentMethodType(dict),
defaultPaymentMethodSet: getBool(dict, "default_payment_method_set", false),
+ requiresCvv: getBool(dict, "requires_cvv", true),
}
})
LoadedSavedCards(customerPaymentMethods, isGuestCustomer)
@@ -842,6 +848,7 @@ let getCustomerMethods = (dict, str) => {
card: getCardDetails(json, "card"),
paymentMethodType: getPaymentMethodType(dict),
defaultPaymentMethodSet: getBool(dict, "default_payment_method_set", false),
+ requiresCvv: getBool(dict, "requires_cvv", true),
}
})
LoadedSavedCards(customerPaymentMethods, false)
diff --git a/src/Utilities/DynamicFieldsUtils.res b/src/Utilities/DynamicFieldsUtils.res
index d6a680df..fa1e8f47 100644
--- a/src/Utilities/DynamicFieldsUtils.res
+++ b/src/Utilities/DynamicFieldsUtils.res
@@ -695,3 +695,24 @@ let useSubmitCallback = () => {
}
}, (line1, line2, state, city, postalCode))
}
+
+let usePaymentMethodTypeFromList = (~list, ~paymentMethod, ~paymentMethodType) => {
+ React.useMemo3(() => {
+ PaymentMethodsRecord.getPaymentMethodTypeFromList(
+ ~list,
+ ~paymentMethod,
+ ~paymentMethodType=PaymentUtils.getPaymentMethodName(
+ ~paymentMethodType=paymentMethod,
+ ~paymentMethodName=paymentMethodType,
+ ),
+ )->Belt.Option.getWithDefault(PaymentMethodsRecord.defaultPaymentMethodType)
+ }, (list, paymentMethod, paymentMethodType))
+}
+
+let useAreAllRequiredFieldsPrefilled = (~list, ~paymentMethod, ~paymentMethodType) => {
+ let paymentMethodTypes = usePaymentMethodTypeFromList(~list, ~paymentMethod, ~paymentMethodType)
+
+ paymentMethodTypes.required_fields->Js.Array2.reduce((acc, requiredField) => {
+ acc && requiredField.value != ""
+ }, true)
+}
diff --git a/src/Utilities/PaymentBody.res b/src/Utilities/PaymentBody.res
index 21787104..a3904f0f 100644
--- a/src/Utilities/PaymentBody.res
+++ b/src/Utilities/PaymentBody.res
@@ -1,29 +1,38 @@
@val @scope("window")
external btoa: string => string = "btoa"
-let cardPaymentBody = (~cardNumber, ~month, ~year, ~cardHolderName, ~cvcNumber, ~cardBrand) => [
- ("payment_method", "card"->Js.Json.string),
- (
- "payment_method_data",
- [
- (
- "card",
- [
- ("card_number", cardNumber->CardUtils.clearSpaces->Js.Json.string),
- ("card_exp_month", month->Js.Json.string),
- ("card_exp_year", year->Js.Json.string),
- ("card_holder_name", cardHolderName->Js.Json.string),
- ("card_cvc", cvcNumber->Js.Json.string),
- ("card_issuer", ""->Js.Json.string),
- ]
- ->Js.Array2.concat(cardBrand)
- ->Js.Dict.fromArray
- ->Js.Json.object_,
- ),
- ]
- ->Js.Dict.fromArray
- ->Js.Json.object_,
- ),
-]
+let cardPaymentBody = (
+ ~cardNumber,
+ ~month,
+ ~year,
+ ~cardHolderName,
+ ~cvcNumber,
+ ~cardBrand,
+ ~nickname="",
+ (),
+) => {
+ let cardBody = [
+ ("card_number", cardNumber->CardUtils.clearSpaces->Js.Json.string),
+ ("card_exp_month", month->Js.Json.string),
+ ("card_exp_year", year->Js.Json.string),
+ ("card_holder_name", cardHolderName->Js.Json.string),
+ ("card_cvc", cvcNumber->Js.Json.string),
+ ("card_issuer", ""->Js.Json.string),
+ ]
+
+ if nickname != "" {
+ cardBody->Js.Array2.push(("nick_name", nickname->Js.Json.string))->ignore
+ }
+
+ [
+ ("payment_method", "card"->Js.Json.string),
+ (
+ "payment_method_data",
+ [("card", cardBody->Js.Array2.concat(cardBrand)->Js.Dict.fromArray->Js.Json.object_)]
+ ->Js.Dict.fromArray
+ ->Js.Json.object_,
+ ),
+ ]
+}
let bancontactBody = () => [
("payment_method", "bank_redirect"->Js.Json.string),
@@ -55,12 +64,19 @@ let boletoBody = (~socialSecurityNumber) => [
),
]
-let savedCardBody = (~paymentToken, ~customerId, ~cvcNumber) => [
- ("payment_method", "card"->Js.Json.string),
- ("payment_token", paymentToken->Js.Json.string),
- ("customer_id", customerId->Js.Json.string),
- ("card_cvc", cvcNumber->Js.Json.string),
-]
+let savedCardBody = (~paymentToken, ~customerId, ~cvcNumber, ~requiresCvv) => {
+ let savedCardBody = [
+ ("payment_method", "card"->Js.Json.string),
+ ("payment_token", paymentToken->Js.Json.string),
+ ("customer_id", customerId->Js.Json.string),
+ ]
+
+ if requiresCvv {
+ savedCardBody->Js.Array2.push(("card_cvc", cvcNumber->Js.Json.string))->ignore
+ }
+
+ savedCardBody
+}
let customerAcceptanceBody =
[
diff --git a/src/Utilities/PaymentUtils.res b/src/Utilities/PaymentUtils.res
index a26b8efc..3ceb7a72 100644
--- a/src/Utilities/PaymentUtils.res
+++ b/src/Utilities/PaymentUtils.res
@@ -3,6 +3,8 @@ let paymentListLookupNew = (
~order,
~showApplePay,
~showGooglePay,
+ ~areAllGooglePayRequiredFieldsPrefilled,
+ ~areAllApplePayRequiredFieldsPrefilled,
) => {
let pmList = list->PaymentMethodsRecord.buildFromPaymentList
let walletsList = []
@@ -26,14 +28,16 @@ let paymentListLookupNew = (
let applePayFields = pmList->Js.Array2.find(item => item.paymentMethodName === "apple_pay")
switch googlePayFields {
| Some(val) =>
- if val.fields->Js.Array2.length > 0 && showGooglePay {
+ if (
+ val.fields->Js.Array2.length > 0 && showGooglePay && !areAllGooglePayRequiredFieldsPrefilled
+ ) {
walletToBeDisplayedInTabs->Js.Array2.push("google_pay")->ignore
}
| None => ()
}
switch applePayFields {
| Some(val) =>
- if val.fields->Js.Array2.length > 0 && showApplePay {
+ if val.fields->Js.Array2.length > 0 && showApplePay && !areAllApplePayRequiredFieldsPrefilled {
walletToBeDisplayedInTabs->Js.Array2.push("apple_pay")->ignore
}
| None => ()