Skip to content

Commit

Permalink
fix: added Wallets to Saved Payment Methods
Browse files Browse the repository at this point in the history
fix #197
  • Loading branch information
ArushKapoorJuspay committed Mar 11, 2024
1 parent d9481d5 commit d6f0f6c
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 102 deletions.
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

0 comments on commit d6f0f6c

Please sign in to comment.