Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: added Wallets to Saved Payment Methods #213

Merged
merged 3 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions public/icons/orca.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 19 additions & 19 deletions src/CardUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -316,28 +316,28 @@ let calculateLuhn = value => {
let getCardBrandIcon = (cardType, paymentType) => {
open CardThemeType
switch cardType {
| VISA => <Icon size=28 name="visa-light" />
| MASTERCARD => <Icon size=28 name="mastercard" />
| AMEX => <Icon size=28 name="amex-light" />
| MAESTRO => <Icon size=28 name="maestro" />
| DINERSCLUB => <Icon size=28 name="diners" />
| DISCOVER => <Icon size=28 name="discover" />
| BAJAJ => <Icon size=28 name="card" />
| SODEXO => <Icon size=28 name="card" />
| RUPAY => <Icon size=28 name="rupay-card" />
| JCB => <Icon size=28 name="jcb-card" />
| CARTESBANCAIRES => <Icon size=28 name="card" />
| UNIONPAY => <Icon size=28 name="card" />
| INTERAC => <Icon size=28 name="interac" />
| VISA => <Icon size=Utils.brandIconSize name="visa-light" />
| MASTERCARD => <Icon size=Utils.brandIconSize name="mastercard" />
| AMEX => <Icon size=Utils.brandIconSize name="amex-light" />
| MAESTRO => <Icon size=Utils.brandIconSize name="maestro" />
| DINERSCLUB => <Icon size=Utils.brandIconSize name="diners" />
| DISCOVER => <Icon size=Utils.brandIconSize name="discover" />
| BAJAJ => <Icon size=Utils.brandIconSize name="card" />
| SODEXO => <Icon size=Utils.brandIconSize name="card" />
| RUPAY => <Icon size=Utils.brandIconSize name="rupay-card" />
| JCB => <Icon size=Utils.brandIconSize name="jcb-card" />
| CARTESBANCAIRES => <Icon size=Utils.brandIconSize name="card" />
| UNIONPAY => <Icon size=Utils.brandIconSize name="card" />
| INTERAC => <Icon size=Utils.brandIconSize name="interac" />
| NOTFOUND =>
switch paymentType {
| Payment => <Icon size=28 name="base-card" />
| Payment => <Icon size=Utils.brandIconSize name="base-card" />
| Card
| CardNumberElement
| CardExpiryElement
| CardCVCElement
| NONE =>
<Icon size=28 name="default-card" />
<Icon size=Utils.brandIconSize name="default-card" />
}
}
}
Expand Down Expand Up @@ -640,15 +640,15 @@ let getCvcDetailsFromCvcProps = cvcProps => {

let setRightIconForCvc = (~cardEmpty, ~cardInvalid, ~color, ~cardComplete) => {
if cardEmpty {
<Icon size=28 name="cvc-empty" />
<Icon size=Utils.brandIconSize name="cvc-empty" />
} else if cardInvalid {
<div style={ReactDOMStyle.make(~color, ())}>
<Icon size=28 name="cvc-invalid" />
<Icon size=Utils.brandIconSize name="cvc-invalid" />
</div>
} else if cardComplete {
<Icon size=28 name="cvc-complete" />
<Icon size=Utils.brandIconSize name="cvc-complete" />
} else {
<Icon size=28 name="cvc-empty" />
<Icon size=Utils.brandIconSize name="cvc-empty" />
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Components/DynamicFields.res
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ let make = (
->React.array}
<RenderIf condition={isRenderDynamicFieldsInsideBilling}>
<div
className="p-2"
className="p-2 w-full text-left"
style={ReactDOMStyle.make(
~border=`1px solid ${themeObj.borderColor}`,
~borderRadius=themeObj.borderRadius,
Expand Down
113 changes: 76 additions & 37 deletions src/Components/SavedCardItem.res
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ let make = (
~cvcProps,
~paymentType,
~list,
~savedMethods,
~setRequiredFieldsBody,
) => {
let {themeObj, config} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
let (cardBrand, setCardBrand) = Recoil.useRecoilState(RecoilAtoms.cardBrand)
Expand Down Expand Up @@ -45,6 +47,13 @@ let make = (
None
}, [isActive])

let isCard = paymentItem.paymentMethod === "card"

let paymentMethodType = switch paymentItem.paymentMethodType {
| Some(paymentMethodType) => paymentMethodType->Utils.snakeToTitleCase
| None => "debit"
}

<button
className={`PickerItem ${pickerItemClass} flex flex-row items-stretch`}
type_="button"
Expand Down Expand Up @@ -81,53 +90,83 @@ let make = (
border="1px solid currentColor"
/>
</div>
<div className="PickerItemIcon mx-3 flex items-center"> brandIcon </div>
<div className="PickerItemLabel flex flex-row gap-3 items-center w-full">
<div className="tracking-widest"> {React.string(`****`)} </div>
<div> {React.string({paymentItem.card.last4Digits})} </div>
<div className={`PickerItemIcon mx-3 flex items-center `}>
brandIcon
</div>
</div>
<div
className={`flex flex-row items-center justify-end gap-3 -mt-1`}
style={ReactDOMStyle.make(~fontSize="14px", ~opacity="0.5", ())}>
<div className="flex">
{React.string(
`${paymentItem.card.expiryMonth} / ${paymentItem.card.expiryYear->CardUtils.formatExpiryToTwoDigit}`,
)}
<div className="flex items-center gap-2">
{isCard
? <div
className={`PickerItemLabel flex flex-row gap-3 items-center`}>
<div className="tracking-widest"> {React.string(`****`)} </div>
<div> {React.string({paymentItem.card.last4Digits})} </div>
</div>
: <div> {React.string(paymentMethodType)} </div>}
<RenderIf condition={paymentItem.defaultPaymentMethodSet}>
<Icon
size=18
name="checkmark"
style={ReactDOMStyle.make(~color=themeObj.colorPrimary, ())}
/>
</RenderIf>
</div>
</div>
<RenderIf condition={isCard}>
<div
className={`flex flex-row items-center justify-end gap-3 -mt-1`}
style={ReactDOMStyle.make(~fontSize="14px", ~opacity="0.5", ())}>
<div className="flex">
{React.string(
`${paymentItem.card.expiryMonth} / ${paymentItem.card.expiryYear->CardUtils.formatExpiryToTwoDigit}`,
)}
</div>
</div>
</RenderIf>
</div>
<div className="w-full ">
<RenderIf condition={isActive}>
<div className="flex flex-col items-start mx-8">
<div
className={`flex flex-row items-start justify-start gap-2`}
style={ReactDOMStyle.make(~fontSize="14px", ~opacity="0.5", ())}>
<div className="w-12 mt-6"> {React.string("CVC: ")} </div>
<div className="flex h mx-4 justify-start w-16 opacity-1 mt-4">
<PaymentInputField
isValid=isCVCValid
setIsValid=setIsCVCValid
value=cvcNumber
onChange=changeCVCNumber
onBlur=handleCVCBlur
errorString=cvcError
inputFieldClassName="flex justify-start"
paymentType
appearance=config.appearance
type_="tel"
className={`tracking-widest justify-start w-full`}
maxLength=4
inputRef=cvcRef
placeholder="123"
height="2.2rem"
/>
<RenderIf condition={isCard}>
<div
className={`flex flex-row items-start justify-start gap-2`}
style={ReactDOMStyle.make(~fontSize="14px", ~opacity="0.5", ())}>
<div className="w-12 mt-6"> {React.string("CVC: ")} </div>
<div
className={`flex h mx-4 justify-start w-16 ${isActive
? "opacity-1 mt-4"
: "opacity-0"}`}>
<PaymentInputField
isValid=isCVCValid
setIsValid=setIsCVCValid
value=cvcNumber
onChange=changeCVCNumber
onBlur=handleCVCBlur
errorString=cvcError
inputFieldClassName="flex justify-start"
paymentType
appearance=config.appearance
type_="tel"
className={`tracking-widest justify-start w-full`}
maxLength=4
inputRef=cvcRef
placeholder="123"
height="2.2rem"
/>
</div>
</div>
</div>
</RenderIf>
<DynamicFields
paymentType
list
paymentMethod=paymentItem.paymentMethod
paymentMethodType
setRequiredFieldsBody
isSavedCardFlow=true
savedCards=savedMethods
/>
<Surcharge
list
paymentMethod="card"
paymentMethodType="debit"
paymentMethod=paymentItem.paymentMethod
paymentMethodType
cardBrand={cardBrand->CardUtils.cardType}
/>
</div>
Expand Down
81 changes: 55 additions & 26 deletions src/Components/SavedMethods.res
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,30 @@ let make = (
let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Card)
let (token, _) = paymentToken
let savedCardlength = savedMethods->Js.Array2.length

let getWalletBrandIcon = (obj: PaymentType.customerMethods) => {
switch obj.paymentMethodType {
| Some("apple_pay") => <Icon size=brandIconSize name="apple_pay_saved" />
| Some("google_pay") => <Icon size=brandIconSize name="google_pay_saved" />
| Some("paypal") => <Icon size=brandIconSize name="paypal" />
| _ => <Icon size=brandIconSize name="default-card" />
}
}

let bottomElement = {
savedMethods
->Js.Array2.mapi((obj, i) => {
let brandIcon = getCardBrandIcon(
switch obj.card.scheme {
| Some(ele) => ele
| None => ""
}->cardType,
""->CardTheme.getPaymentMode,
)
let brandIcon = switch obj.paymentMethod {
| "wallet" => getWalletBrandIcon(obj)
| _ =>
getCardBrandIcon(
switch obj.card.scheme {
| Some(ele) => ele
| None => ""
}->cardType,
""->CardTheme.getPaymentMode,
)
}
let isActive = token == obj.paymentToken
<SavedCardItem
key={i->Belt.Int.toString}
Expand All @@ -43,6 +57,8 @@ let make = (
cvcProps
paymentType
list
savedMethods
setRequiredFieldsBody
/>
})
->React.array
Expand All @@ -59,11 +75,34 @@ let make = (
let json = ev.data->Js.Json.parseExn
let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper
let (token, customerId) = paymentToken
let savedCardBody = PaymentBody.savedCardBody(~paymentToken=token, ~customerId, ~cvcNumber)
let customerMethod =
savedMethods
->Js.Array2.filter(savedMethod => {
savedMethod.paymentToken === token
})
->Belt.Array.get(0)
->Belt.Option.getWithDefault(PaymentType.defaultCustomerMethods)
let isCardPaymentMethod = customerMethod.paymentMethod === "card"
let savedPaymentMethodBody = switch customerMethod.paymentMethod {
| "card" => PaymentBody.savedCardBody(~paymentToken=token, ~customerId, ~cvcNumber)
| _ => {
let paymentMethodType = switch customerMethod.paymentMethodType {
| Some("")
| None => Js.Json.null
| Some(paymentMethodType) => paymentMethodType->Js.Json.string
}
PaymentBody.savedPaymentMethodBody(
~paymentToken=token,
~customerId,
~paymentMethod=customerMethod.paymentMethod,
~paymentMethodType,
)
}
}
if confirm.doSubmit {
if areRequiredFieldsValid && complete && !empty {
if areRequiredFieldsValid && (!isCardPaymentMethod || (complete && !empty)) {
intent(
~bodyArr=savedCardBody
~bodyArr=savedPaymentMethodBody
->Js.Dict.fromArray
->Js.Json.object_
->OrcaUtils.flattenObject(true)
Expand All @@ -81,15 +120,16 @@ let make = (
if !(isCVCValid->Belt.Option.getWithDefault(false)) {
setUserError(localeString.enterValidDetailsText)
}
if !areRequiredFieldsValid {
setUserError(localeString.enterValidDetailsText)
}
}
}
}, (areRequiredFieldsValid, requiredFieldsBody, empty, complete))
submitPaymentData(submitCallback)

<>
<div
className="flex flex-col overflow-auto h-auto no-scrollbar animate-slowShow"
style={ReactDOMStyle.make(~padding="5px", ())}>
<div className="flex flex-col overflow-auto h-auto no-scrollbar animate-slowShow">
{if (
savedCardlength === 0 && (loadSavedCards === PaymentType.LoadingSavedCards || !showFields)
) {
Expand All @@ -112,19 +152,8 @@ let make = (
</PaymentElementShimmer.Shimmer>
</div>
} else {
<RenderIf condition={!showFields}>
<Block bottomElement padding="px-4 py-1" className="max-h-[309px] overflow-auto" />
</RenderIf>
<RenderIf condition={!showFields}> {bottomElement} </RenderIf>
}}
<DynamicFields
paymentType
list
paymentMethod="card"
paymentMethodType="debit"
setRequiredFieldsBody
isSavedCardFlow=true
savedCards=savedMethods
/>
<RenderIf condition={!showFields}>
<div
className="Label flex flex-row gap-3 items-end cursor-pointer"
Expand All @@ -141,7 +170,7 @@ let make = (
setShowFields(._ => true)
}}>
<Icon name="circle-plus" size=22 />
{React.string(localeString.addNewCard)}
{React.string(localeString.morePaymentMethods)}
</div>
</RenderIf>
</div>
Expand Down
Loading
Loading