Skip to content
165 changes: 137 additions & 28 deletions src/Components/SavedMethods.res
Original file line number Diff line number Diff line change
@@ -1,3 +1,66 @@
module SavedCardsElement = {
@react.component
let make = (
~cardsArr: array<PaymentType.customerMethods>,
~setPaymentToken,
~paymentTokenVal,
~savedCardlength,
~cvcProps,
~setRequiredFieldsBody,
) => {
open CardUtils

cardsArr
->Array.mapWithIndex((obj, i) =>
<SavedCardItem
key={i->Int.toString}
setPaymentToken
isActive={paymentTokenVal == obj.paymentToken}
paymentItem=obj
brandIcon={obj->getPaymentMethodBrand}
index=i
savedCardlength
cvcProps
setRequiredFieldsBody
/>
)
->React.array
}
}

module ClickToPayElement = {
@react.component
let make = (
~savedMethods,
~isClickToPayAuthenticateError,
~setIsClickToPayAuthenticateError,
~setPaymentToken,
~paymentTokenVal,
~cvcProps,
~getVisaCards,
~setIsClickToPayRememberMe,
~closeComponentIfSavedMethodsAreEmpty,
) => {
let clickToPayConfig = Recoil.useRecoilValueFromAtom(RecoilAtoms.clickToPayConfig)
let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)

<RenderIf condition={clickToPayConfig.isReady == Some(true)}>
<ClickToPayAuthenticate
loggerState
savedMethods
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
setPaymentToken
paymentTokenVal
cvcProps
getVisaCards
setIsClickToPayRememberMe
closeComponentIfSavedMethodsAreEmpty
/>
</RenderIf>
}
}

@react.component
let make = (
~paymentToken: RecoilAtomTypes.paymentToken,
Expand Down Expand Up @@ -37,9 +100,12 @@ let make = (
loggerState.setLogError(~value=message, ~eventName=INVALID_FORMAT)
}
let (isSaveCardsChecked, setIsSaveCardsChecked) = React.useState(_ => false)
let {displaySavedPaymentMethodsCheckbox, readOnly} = Recoil.useRecoilValueFromAtom(
let (showMore, setShowMore) = React.useState(_ => true)
let {displaySavedPaymentMethodsCheckbox, readOnly, layout} = Recoil.useRecoilValueFromAtom(
RecoilAtoms.optionAtom,
)
let layoutClass = CardUtils.getLayoutClass(layout)
let displayMergedSavedMethods = layoutClass.savedMethodsLayout.displayMergedSavedMethods
let isGuestCustomer = useIsGuestCustomer()

let {iframeId, clientSecret} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys)
Expand All @@ -66,38 +132,72 @@ let make = (
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)
let {paymentToken: paymentTokenVal, customerId} = paymentToken

let bottomElement = {
let (cardOptionDetails, dropDownOptionsDetails) = React.useMemo(() => {
(
savedMethods->Array.slice(~start=0, ~end=layoutClass.savedMethodsLayout.maxSavedItems),
savedMethods->Array.sliceToEnd(~start=layoutClass.savedMethodsLayout.maxSavedItems),
)
}, [savedMethods])

let mergedViewBottomElement = {
<div
className="PickerItemContainer" tabIndex={0} role="region" ariaLabel="Saved payment methods">
{savedMethods
->Array.mapWithIndex((obj, i) =>
<SavedCardItem
key={i->Int.toString}
<SavedCardsElement
cardsArr=cardOptionDetails
setPaymentToken
paymentTokenVal
savedCardlength
cvcProps
setRequiredFieldsBody
/>
<RenderIf condition={!showMore}>
<SavedCardsElement
cardsArr=dropDownOptionsDetails
setPaymentToken
isActive={paymentTokenVal == obj.paymentToken}
paymentItem=obj
brandIcon={obj->getPaymentMethodBrand}
index=i
paymentTokenVal
savedCardlength
cvcProps
setRequiredFieldsBody
/>
)
->React.array}
<RenderIf condition={clickToPayConfig.isReady == Some(true)}>
<ClickToPayAuthenticate
loggerState
savedMethods
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
</RenderIf>
<ClickToPayElement
savedMethods
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
setPaymentToken
paymentTokenVal
cvcProps
getVisaCards
setIsClickToPayRememberMe
closeComponentIfSavedMethodsAreEmpty
/>
</div>
}

let bottomElement = {
<div
className="PickerItemContainer" tabIndex={0} role="region" ariaLabel="Saved payment methods">
<RenderIf condition={!displayMergedSavedMethods}>
<SavedCardsElement
cardsArr=savedMethods
setPaymentToken
paymentTokenVal
savedCardlength
cvcProps
getVisaCards
setIsClickToPayRememberMe
closeComponentIfSavedMethodsAreEmpty
setRequiredFieldsBody
/>
</RenderIf>
<ClickToPayElement
savedMethods
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
setPaymentToken
paymentTokenVal
cvcProps
getVisaCards
setIsClickToPayRememberMe
closeComponentIfSavedMethodsAreEmpty
/>
</div>
}

Expand Down Expand Up @@ -336,15 +436,23 @@ let make = (
let enableSavedPaymentShimmer = React.useMemo(() => {
savedCardlength === 0 &&
!showPaymentMethodsScreen &&
(loadSavedCards === PaymentType.LoadingSavedCards || clickToPayConfig.isReady->Option.isNone)
}, (savedCardlength, loadSavedCards, showPaymentMethodsScreen, clickToPayConfig.isReady))
(loadSavedCards === PaymentType.LoadingSavedCards || clickToPayConfig.isReady->Option.isNone) &&
!displayMergedSavedMethods
}, (
savedCardlength,
loadSavedCards,
showPaymentMethodsScreen,
clickToPayConfig.isReady,
displayMergedSavedMethods,
))
Comment on lines 436 to +447
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to simplify this condition for better readability.


<div className="flex flex-col overflow-auto h-auto no-scrollbar animate-slowShow">
{if enableSavedPaymentShimmer {
<RenderIf condition={enableSavedPaymentShimmer}>
<PaymentElementShimmer.SavedPaymentCardShimmer />
} else {
<RenderIf condition={!showPaymentMethodsScreen}> {bottomElement} </RenderIf>
}}
</RenderIf>
<RenderIf condition={!enableSavedPaymentShimmer && !showPaymentMethodsScreen}>
{displayMergedSavedMethods ? mergedViewBottomElement : bottomElement}
</RenderIf>
<RenderIf condition={conditionsForShowingSaveCardCheckbox}>
<div className="pt-4 pb-2 flex items-center justify-start">
<SaveDetailsCheckbox isChecked=isSaveCardsChecked setIsChecked=setIsSaveCardsChecked />
Expand All @@ -360,7 +468,7 @@ let make = (
}
/>
</RenderIf>
<RenderIf condition={!enableSavedPaymentShimmer}>
<RenderIf condition={!enableSavedPaymentShimmer && !displayMergedSavedMethods}>
<div
className="Label flex flex-row gap-3 items-end cursor-pointer mt-4"
style={
Expand All @@ -386,5 +494,6 @@ let make = (
{React.string(localeString.morePaymentMethods)}
</div>
</RenderIf>
<ShowMoreButton displayMergedSavedMethods dropDownOptionsDetails showMore setShowMore />
</div>
}
20 changes: 20 additions & 0 deletions src/Components/ShowMoreButton.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@react.component
let make = (~displayMergedSavedMethods, ~dropDownOptionsDetails, ~showMore, ~setShowMore) => {
let {themeObj} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)

<RenderIf condition={displayMergedSavedMethods && dropDownOptionsDetails->Array.length > 0}>
<div
className="Label flex flex-row gap-1 items-end cursor-pointer mt-3 text-[14px] font-medium float-left w-fit"
style={
color: themeObj.colorPrimary,
}
onClick={_ => {
setShowMore(_ => !showMore)
}}>
Copy link
Contributor

@PritishBudhiraja PritishBudhiraja Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
onClick={_ => {
setShowMore(_ => !showMore)
}}>
onClick={_ => setShowMore(prev => !prev) }>

{showMore ? React.string("Show more") : React.string("Show Less")}
<div className="m-1">
{showMore ? <Icon name="arrow-down" size=10 /> : <Icon name="arrow-up" size=10 />}
</div>
</div>
</RenderIf>
}
33 changes: 28 additions & 5 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}, (clickToPayConfig, isClickToPayAuthenticateError))

let layoutClass = CardUtils.getLayoutClass(layout)
let displayMergedSavedMethods = layoutClass.savedMethodsLayout.displayMergedSavedMethods

let (getVisaCards, closeComponentIfSavedMethodsAreEmpty) = ClickToPayHook.useClickToPay(
~areClickToPayUIScriptsLoaded,
Expand Down Expand Up @@ -278,6 +279,9 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}, [selectedOption])
useSubmitPaymentData(submitCallback)
React.useEffect(() => {
if displayMergedSavedMethods {
setShowPaymentMethodsScreen(_ => selectedOption != "saved_methods")
}
setSelectedOption(prev =>
selectedOption !== ""
? prev
Expand Down Expand Up @@ -402,6 +406,19 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
</RenderIf>
}}
</SessionPaymentWrapper>
| SavedMethods =>
<SavedMethods
paymentToken
setPaymentToken
savedMethods
loadSavedCards
cvcProps
sessions
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
getVisaCards
closeComponentIfSavedMethodsAreEmpty
/>
| _ =>
<ReusableReactSuspense loaderComponent={loader()} componentName="PaymentMethodsWrapperLazy">
<PaymentMethodsWrapperLazy paymentMethodName=selectedOption />
Expand Down Expand Up @@ -450,15 +467,18 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
</div>
</RenderIf>
{if clickToPayConfig.isReady->Option.isNone {
if areClickToPayUIScriptsLoaded {
if displayMergedSavedMethods {
<PaymentElementShimmer />
} else if areClickToPayUIScriptsLoaded {
<ClickToPayHelpers.SrcLoader />
} else {
<PaymentElementShimmer.SavedPaymentCardShimmer />
}
} else {
<RenderIf
condition={!showPaymentMethodsScreen &&
(displaySavedPaymentMethods || isShowPaymentMethodsDependingOnClickToPay)}>
(displaySavedPaymentMethods || isShowPaymentMethodsDependingOnClickToPay) &&
!displayMergedSavedMethods}>
<SavedMethods
paymentToken
setPaymentToken
Expand All @@ -475,7 +495,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}}
<RenderIf
condition={(paymentOptions->Array.length > 0 || walletOptions->Array.length > 0) &&
showPaymentMethodsScreen &&
(showPaymentMethodsScreen || displayMergedSavedMethods) &&
clickToPayConfig.isReady->Option.isSome}>
<div
className="flex flex-col place-items-center"
Expand Down Expand Up @@ -510,7 +530,9 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
</RenderIf>
<RenderIf
condition={((displaySavedPaymentMethods && savedMethods->Array.length > 0) ||
isShowPaymentMethodsDependingOnClickToPay) && showPaymentMethodsScreen}>
isShowPaymentMethodsDependingOnClickToPay) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we simplify this? It's too many conditions inside a prop - not readable

showPaymentMethodsScreen &&
!displayMergedSavedMethods}>
<div
className="Label flex flex-row gap-3 items-end cursor-pointer mt-4"
style={
Expand Down Expand Up @@ -542,7 +564,8 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
</RenderIf>
| _ =>
<RenderIf
condition={!displaySavedPaymentMethods &&
condition={(!displaySavedPaymentMethods ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we simplify this? It's too many conditions inside a prop - not readable

(displayMergedSavedMethods && savedMethods->Array.length == 0)) &&
paymentOptions->Array.length == 0 &&
walletOptions->Array.length == 0}>
<PaymentElementShimmer />
Expand Down
1 change: 1 addition & 0 deletions src/PaymentElementV2.res
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
| Boleto
| PayPal
| EFT
| SavedMethods
| Unknown => React.null
}}
</ErrorBoundary>
Expand Down
7 changes: 7 additions & 0 deletions src/Payments/PaymentMethodsRecord.res
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@ let getPaymentMethodsFields = (~localeString: LocaleStringTypes.localeStrings) =
displayName: localeString.payment_methods_card,
miniIcon: None,
},
{
paymentMethodName: "saved_methods",
icon: Some(icon("default-card", ~size=19)),
fields: [],
displayName: "Saved",
miniIcon: None,
},
{
paymentMethodName: "klarna",
icon: Some(icon("klarna", ~size=19)),
Expand Down
3 changes: 3 additions & 0 deletions src/Types/PaymentModeType.res
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type payment =
| Boleto
| PayPal
| EFT
| SavedMethods
| Unknown

let paymentMode = str => {
Expand Down Expand Up @@ -57,11 +58,13 @@ let paymentMode = str => {
| "boleto" => Boleto
| "paypal" => PayPal
| "eft" => EFT
| "saved_methods" => SavedMethods
| _ => Unknown
}
}

let defaultOrder = [
"saved_methods",
"card",
"apple_pay",
"google_pay",
Expand Down
Loading