Skip to content

Commit

Permalink
feat: added paypal tabs flow support for billing details (#792)
Browse files Browse the repository at this point in the history
Co-authored-by: Pritish Budhiraja <[email protected]>
  • Loading branch information
sakksham7 and PritishBudhiraja authored Nov 21, 2024
1 parent 5b05ed2 commit a7c7d19
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 42 deletions.
33 changes: 29 additions & 4 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
layout,
customerPaymentMethods,
displaySavedPaymentMethods,
sdkHandleConfirmPayment,
} = Recoil.useRecoilValueFromAtom(optionAtom)
let {themeObj, localeString} = Recoil.useRecoilValueFromAtom(configAtom)
let optionAtomValue = Recoil.useRecoilValueFromAtom(optionAtom)
let paymentMethodList = Recoil.useRecoilValueFromAtom(paymentMethodList)
let (sessions, setSessions) = React.useState(_ => Dict.make()->JSON.Encode.object)
let (paymentOptions, setPaymentOptions) = React.useState(_ => [])
let (walletOptions, setWalletOptions) = React.useState(_ => [])
let {sdkHandleConfirmPayment} = Recoil.useRecoilValueFromAtom(optionAtom)

let isApplePayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isApplePayReady)
let isGPayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isGooglePayReady)
let isApplePayReady = Recoil.useRecoilValueFromAtom(isApplePayReady)
let isGPayReady = Recoil.useRecoilValueFromAtom(isGooglePayReady)

let (paymentMethodListValue, setPaymentMethodListValue) = Recoil.useRecoilState(
PaymentUtils.paymentMethodListValue,
Expand Down Expand Up @@ -158,6 +157,11 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
googlePayThirdPartySessionObj.sessionsToken,
Gpay,
)
let {
paypalToken,
isPaypalSDKFlow,
isPaypalRedirectFlow,
} = PayPalHelpers.usePaymentMethodExperience(~paymentMethodListValue, ~sessionObj)

React.useEffect(() => {
switch paymentMethodList {
Expand Down Expand Up @@ -355,6 +359,27 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
| _ => React.null
}}
</SessionPaymentWrapper>
| PayPal =>
<SessionPaymentWrapper type_={Wallet}>
{switch paypalToken {
| OtherTokenOptional(optToken) =>
switch (optToken, isPaypalSDKFlow, isPaypalRedirectFlow) {
| (Some(_token), true, _) => {
loggerState.setLogInfo(
~value="PayPal Invoke SDK Flow in Tabs",
~eventName=PAYPAL_SDK_FLOW,
)
React.null
}
| (_, _, true) => <PayPalLazy paymentType walletOptions />
| _ => React.null
}
| _ =>
<RenderIf condition={isPaypalRedirectFlow}>
<PayPalLazy paymentType walletOptions />
</RenderIf>
}}
</SessionPaymentWrapper>
| _ =>
<ReusableReactSuspense loaderComponent={loader()} componentName="PaymentMethodsWrapperLazy">
<PaymentMethodsWrapperLazy paymentType paymentMethodName=selectedOption />
Expand Down
85 changes: 64 additions & 21 deletions src/Payments/PayPal.res
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ module Loader = {
let payPalIcon = <Icon size=35 width=90 name="paypal" />

@react.component
let make = () => {
let make = (~paymentType, ~walletOptions) => {
let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom)
let (paypalClicked, setPaypalClicked) = React.useState(_ => false)
let sdkHandleIsThere = Recoil.useRecoilValueFromAtom(isPaymentButtonHandlerProvidedAtom)
let {publishableKey} = Recoil.useRecoilValueFromAtom(keys)
let options = Recoil.useRecoilValueFromAtom(optionAtom)
let areOneClickWalletsRendered = Recoil.useSetRecoilState(areOneClickWalletsRendered)
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)
let isWallet = walletOptions->Array.includes("paypal")
let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Dict.make())

let (_, _, labelType) = options.wallets.style.type_
let _label = switch labelType {
Expand Down Expand Up @@ -56,12 +58,16 @@ let make = () => {
let (connectors, _) =
paymentMethodListValue->PaymentUtils.getConnectors(Wallets(Paypal(Redirect)))
let body = PaymentBody.paypalRedirectionBody(~connectors)

let modifiedPaymentBody = PaymentUtils.appendedCustomerAcceptance(
let basePaymentBody = PaymentUtils.appendedCustomerAcceptance(
~isGuestCustomer,
~paymentType=paymentMethodListValue.payment_type,
~body,
)
let modifiedPaymentBody = if isWallet {
basePaymentBody
} else {
basePaymentBody->Utils.mergeAndFlattenToTuples(requiredFieldsBody)
}

intent(
~bodyArr=modifiedPaymentBody,
Expand All @@ -88,24 +94,61 @@ let make = () => {
None
})

<button
style={
display: "inline-block",
color: textColor,
height: `${height->Int.toString}px`,
borderRadius: `${options.wallets.style.buttonRadius->Int.toString}px`,
width: "100%",
backgroundColor: buttonColor,
}
onClick={_ => options.readOnly ? () : onPaypalClick()}>
<div className="justify-center" style={display: "flex", flexDirection: "row", color: textColor}>
{if !paypalClicked {
payPalIcon
} else {
<Loader />
}}
</div>
</button>
let useSubmitCallback = (~isWallet) => {
let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid)
let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty)
let {localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
let {iframeId} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys)

React.useCallback((ev: Window.event) => {
if !isWallet {
let json = ev.data->Utils.safeParse
let confirm = json->Utils.getDictFromJson->ConfirmType.itemToObjMapper
if confirm.doSubmit && areRequiredFieldsValid && !areRequiredFieldsEmpty {
onPaypalClick(ev)
} else if areRequiredFieldsEmpty {
Utils.postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterFieldsText,
)
} else if !areRequiredFieldsValid {
Utils.postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterValidDetailsText,
)
}
}
}, (areRequiredFieldsValid, areRequiredFieldsEmpty, isWallet, iframeId))
}

let submitCallback = useSubmitCallback(~isWallet)
Utils.useSubmitPaymentData(submitCallback)

if isWallet {
<button
style={
display: "inline-block",
color: textColor,
height: `${height->Int.toString}px`,
borderRadius: `${options.wallets.style.buttonRadius->Int.toString}px`,
width: "100%",
backgroundColor: buttonColor,
}
onClick={_ => options.readOnly ? () : onPaypalClick()}>
<div
className="justify-center" style={display: "flex", flexDirection: "row", color: textColor}>
{if !paypalClicked {
payPalIcon
} else {
<Loader />
}}
</div>
</button>
} else {
<DynamicFields
paymentType paymentMethod="wallet" paymentMethodType="paypal" setRequiredFieldsBody
/>
}
}

let default = make
2 changes: 1 addition & 1 deletion src/Payments/PayPal.resi
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@react.component
let default: unit => React.element
let default: (~paymentType: CardThemeType.mode, ~walletOptions: array<string>) => React.element
31 changes: 31 additions & 0 deletions src/Payments/PayPalHelpers.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
open PaymentMethodsRecord
open SessionsType

type paypalExperienceData = {
paypalToken: optionalTokenType,
isPaypalSDKFlow: bool,
isPaypalRedirectFlow: bool,
}

let usePaymentMethodExperience = (~paymentMethodListValue, ~sessionObj: sessions) => {
let paypalPaymentMethodExperience = React.useMemo(() => {
getPaymentExperienceTypeFromPML(
~paymentMethodList=paymentMethodListValue,
~paymentMethodName="wallet",
~paymentMethodType="paypal",
)
}, [paymentMethodListValue])

let paypalToken = React.useMemo(
() => getPaymentSessionObj(sessionObj.sessionsToken, Paypal),
[sessionObj],
)
let isPaypalSDKFlow = paypalPaymentMethodExperience->Array.includes(InvokeSDK)
let isPaypalRedirectFlow = paypalPaymentMethodExperience->Array.includes(RedirectToURL)

{
paypalToken,
isPaypalSDKFlow,
isPaypalRedirectFlow,
}
}
7 changes: 7 additions & 0 deletions src/Payments/PaymentMethodsRecord.res
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,13 @@ let paymentMethodsFields = [
fields: [InfoElement],
miniIcon: None,
},
{
paymentMethodName: "paypal",
icon: Some(icon("paypal", ~size=21, ~width=25)),
displayName: "Paypal",
fields: [],
miniIcon: None,
},
{
paymentMethodName: "local_bank_transfer_transfer",
fields: [InfoElement],
Expand Down
24 changes: 9 additions & 15 deletions src/Payments/PaymentRequestButtonElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,13 @@ let make = (~sessions, ~walletOptions, ~paymentType) => {
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)

let sessionObj = React.useMemo(() => itemToObjMapper(dict, Others), [dict])
let paypalToken = React.useMemo(
() => getPaymentSessionObj(sessionObj.sessionsToken, Paypal),
[sessionObj],
)
let paypalPaymentMethodExperience = React.useMemo(() => {
PaymentMethodsRecord.getPaymentExperienceTypeFromPML(
~paymentMethodList=paymentMethodListValue,
~paymentMethodName="wallet",
~paymentMethodType="paypal",
)
}, [paymentMethodListValue])

let {
paypalToken,
isPaypalSDKFlow,
isPaypalRedirectFlow,
} = PayPalHelpers.usePaymentMethodExperience(~paymentMethodListValue, ~sessionObj)

let gPayToken = getPaymentSessionObj(sessionObj.sessionsToken, Gpay)
let applePaySessionObj = itemToObjMapper(dict, ApplePayObject)
let applePayToken = getPaymentSessionObj(applePaySessionObj.sessionsToken, ApplePay)
Expand All @@ -70,8 +66,6 @@ let make = (~sessions, ~walletOptions, ~paymentType) => {

let {clientSecret} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys)
let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom)
let isPaypalSDKFlow = paypalPaymentMethodExperience->Array.includes(InvokeSDK)
let isPaypalRedirectFlow = paypalPaymentMethodExperience->Array.includes(RedirectToURL)

<div className="flex flex-col gap-2 h-auto w-full">
{walletOptions
Expand Down Expand Up @@ -113,12 +107,12 @@ let make = (~sessions, ~walletOptions, ~paymentType) => {
| OtherTokenOptional(optToken) =>
switch (optToken, isPaypalSDKFlow, isPaypalRedirectFlow) {
| (Some(token), true, _) => <PaypalSDKLazy sessionObj=token paymentType />
| (_, _, true) => <PayPalLazy />
| (_, _, true) => <PayPalLazy paymentType walletOptions />
| _ => React.null
}
| _ =>
<RenderIf condition={isPaypalRedirectFlow}>
<PayPalLazy />
<PayPalLazy paymentType walletOptions />
</RenderIf>
}}
</SessionPaymentWrapper>
Expand Down
2 changes: 2 additions & 0 deletions src/Types/PaymentModeType.res
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type payment =
| GooglePay
| ApplePay
| Boleto
| PayPal
| NONE

let paymentMode = str => {
Expand All @@ -43,6 +44,7 @@ let paymentMode = str => {
| "google_pay" => GooglePay
| "apple_pay" => ApplePay
| "boleto" => Boleto
| "paypal" => PayPal
| _ => NONE
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Utilities/DynamicFieldsUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let dynamicFieldsEnabledPaymentMethods = [
"bacs",
"pay_bright",
"multibanco_transfer",
"paypal",
]

let getName = (item: PaymentMethodsRecord.required_fields, field: RecoilAtomTypes.field) => {
Expand Down
Loading

0 comments on commit a7c7d19

Please sign in to comment.