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

feat: added support for collecting_billing_details_from_wallets #529

Merged
merged 1 commit into from
Jul 31, 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
35 changes: 35 additions & 0 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
~sessions,
)

let dict = sessions->getDictFromJson
let sessionObj = SessionsType.itemToObjMapper(dict, Others)
let applePaySessionObj = SessionsType.itemToObjMapper(dict, ApplePayObject)
let applePayToken = SessionsType.getPaymentSessionObj(applePaySessionObj.sessionsToken, ApplePay)
let gPayToken = SessionsType.getPaymentSessionObj(sessionObj.sessionsToken, Gpay)
let googlePayThirdPartySessionObj = SessionsType.itemToObjMapper(dict, GooglePayThirdPartyObject)
let googlePayThirdPartyToken = SessionsType.getPaymentSessionObj(
googlePayThirdPartySessionObj.sessionsToken,
Gpay,
)

React.useEffect(() => {
switch paymentMethodList {
| Loaded(paymentlist) =>
Expand Down Expand Up @@ -317,6 +328,30 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
<React.Suspense fallback={loader()}>
<BoletoLazy paymentType />
</React.Suspense>
| ApplePay =>
switch applePayToken {
| ApplePayTokenOptional(optToken) =>
<ApplePayLazy sessionObj=optToken walletOptions paymentType />
| _ => React.null
}
| GooglePay =>
<SessionPaymentWrapper type_={Wallet}>
{switch gPayToken {
| OtherTokenOptional(optToken) =>
switch googlePayThirdPartyToken {
| GooglePayThirdPartyTokenOptional(googlePayThirdPartyOptToken) =>
<GPayLazy
sessionObj=optToken
thirdPartySessionObj=googlePayThirdPartyOptToken
walletOptions
paymentType
/>
| _ =>
<GPayLazy sessionObj=optToken thirdPartySessionObj=None walletOptions paymentType />
}
| _ => React.null
}}
</SessionPaymentWrapper>
| _ =>
<React.Suspense fallback={loader()}>
<PaymentMethodsWrapperLazy paymentType paymentMethodName=selectedOption />
Expand Down
65 changes: 44 additions & 21 deletions src/Payments/ApplePay.res
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
open Utils
open Promise
@react.component
let make = (~sessionObj: option<JSON.t>) => {
let make = (~sessionObj: option<JSON.t>, ~walletOptions, ~paymentType: CardThemeType.mode) => {
let url = RescriptReactRouter.useUrl()
let componentName = CardUtils.getQueryParamsDictforKey(url.search, "componentName")
let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)
Expand All @@ -21,6 +21,17 @@ let make = (~sessionObj: option<JSON.t>) => {
let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered)
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)

let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid)
let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty)
let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Dict.make())
let isWallet = walletOptions->Array.includes("apple_pay")

UtilityHooks.useHandlePostMessages(
PritishBudhiraja marked this conversation as resolved.
Show resolved Hide resolved
~complete=areRequiredFieldsValid,
~empty=areRequiredFieldsEmpty,
~paymentType="apple_pay",
)

let applePayPaymentMethodType = React.useMemo(() => {
switch PaymentMethodsRecord.getPaymentMethodTypeFromList(
~paymentMethodListValue,
Expand Down Expand Up @@ -260,12 +271,15 @@ let make = (~sessionObj: option<JSON.t>) => {
~setApplePayClicked,
~syncPayment,
~isInvokeSDKFlow,
~isWallet,
~requiredFieldsBody,
)

React.useEffect(() => {
if (
(isInvokeSDKFlow || paymentExperience == PaymentMethodsRecord.RedirectToURL) &&
isApplePayReady
isApplePayReady &&
isWallet
) {
setShowApplePay(_ => true)
areOneClickWalletsRendered(prev => {
Expand All @@ -275,26 +289,35 @@ let make = (~sessionObj: option<JSON.t>) => {
setIsShowOrPayUsing(_ => true)
}
None
}, (isApplePayReady, isInvokeSDKFlow, paymentExperience))
}, (isApplePayReady, isInvokeSDKFlow, paymentExperience, isWallet))

<RenderIf condition={showApplePay}>
<div>
<style> {React.string(css)} </style>
{if showApplePayLoader {
<div className="apple-pay-loader-div">
<div className="apple-pay-loader" />
</div>
} else {
<button
disabled=applePayClicked
className="apple-pay-button-with-text apple-pay-button-black-with-text"
onClick={_ => onApplePayButtonClicked()}>
<span className="text"> {React.string("Pay with")} </span>
<span className="logo" />
</button>
}}
</div>
</RenderIf>
let submitCallback = ApplePayHelpers.useSubmitCallback(~isWallet, ~sessionObj, ~componentName)
useSubmitPaymentData(submitCallback)

if isWallet {
<RenderIf condition={showApplePay}>
<div>
<style> {React.string(css)} </style>
{if showApplePayLoader {
<div className="apple-pay-loader-div">
<div className="apple-pay-loader" />
</div>
} else {
<button
disabled=applePayClicked
className="apple-pay-button-with-text apple-pay-button-black-with-text"
onClick={_ => onApplePayButtonClicked()}>
<span className="text"> {React.string("Pay with")} </span>
<span className="logo" />
</button>
}}
</div>
</RenderIf>
} else {
<DynamicFields
paymentType paymentMethod="wallet" paymentMethodType="apple_pay" setRequiredFieldsBody
/>
}
}

let default = make
6 changes: 5 additions & 1 deletion src/Payments/ApplePay.resi
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
@react.component
let default: (~sessionObj: option<JSON.t>) => React.element
let default: (
~sessionObj: option<JSON.t>,
~walletOptions: array<string>,
~paymentType: CardThemeType.mode,
) => React.element
52 changes: 40 additions & 12 deletions src/Payments/GPay.res
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ open GooglePayType
open Promise

@react.component
let make = (~sessionObj: option<SessionsType.token>, ~thirdPartySessionObj: option<JSON.t>) => {
let make = (
~sessionObj: option<SessionsType.token>,
~thirdPartySessionObj: option<JSON.t>,
~walletOptions,
~paymentType: CardThemeType.mode,
) => {
let url = RescriptReactRouter.useUrl()
let componentName = CardUtils.getQueryParamsDictforKey(url.search, "componentName")
let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom)
Expand All @@ -28,6 +33,17 @@ let make = (~sessionObj: option<SessionsType.token>, ~thirdPartySessionObj: opti

let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered)

let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid)
let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty)
let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Dict.make())
let isWallet = walletOptions->Array.includes("google_pay")

UtilityHooks.useHandlePostMessages(
~complete=areRequiredFieldsValid,
~empty=areRequiredFieldsEmpty,
~paymentType="google_pay",
)

let googlePayPaymentMethodType = switch PaymentMethodsRecord.getPaymentMethodTypeFromList(
~paymentMethodListValue,
~paymentMethod="wallet",
Expand Down Expand Up @@ -58,7 +74,7 @@ let make = (~sessionObj: option<SessionsType.token>, ~thirdPartySessionObj: opti
->Option.getOr(false)
}, [thirdPartySessionObj])

GooglePayHelpers.useHandleGooglePayResponse(~connectors, ~intent)
GooglePayHelpers.useHandleGooglePayResponse(~connectors, ~intent, ~isWallet, ~requiredFieldsBody)

let (_, buttonType, _) = options.wallets.style.type_
let (_, heightType, _, _) = options.wallets.style.height
Expand Down Expand Up @@ -155,9 +171,10 @@ let make = (~sessionObj: option<SessionsType.token>, ~thirdPartySessionObj: opti
React.useEffect(() => {
if (
status == "ready" &&
(isGPayReady ||
isDelayedSessionToken ||
paymentExperience == PaymentMethodsRecord.RedirectToURL)
(isGPayReady ||
isDelayedSessionToken ||
paymentExperience == PaymentMethodsRecord.RedirectToURL) &&
isWallet
) {
setIsShowOrPayUsing(_ => true)
addGooglePayButton()
Expand Down Expand Up @@ -190,7 +207,9 @@ let make = (~sessionObj: option<SessionsType.token>, ~thirdPartySessionObj: opti
})

let isRenderGooglePayButton =
isGPayReady || paymentExperience == PaymentMethodsRecord.RedirectToURL || isDelayedSessionToken
(isGPayReady ||
paymentExperience == PaymentMethodsRecord.RedirectToURL ||
isDelayedSessionToken) && isWallet

React.useEffect(() => {
areOneClickWalletsRendered(prev => {
Expand All @@ -200,13 +219,22 @@ let make = (~sessionObj: option<SessionsType.token>, ~thirdPartySessionObj: opti
None
}, [isRenderGooglePayButton])

<RenderIf condition={isRenderGooglePayButton}>
<div
style={height: `${height->Int.toString}px`}
id="google-pay-button"
className={`w-full flex flex-row justify-center rounded-md`}
let submitCallback = GooglePayHelpers.useSubmitCallback(~isWallet, ~sessionObj, ~componentName)
useSubmitPaymentData(submitCallback)

if isWallet {
<RenderIf condition={isRenderGooglePayButton}>
<div
style={height: `${height->Int.toString}px`}
id="google-pay-button"
className={`w-full flex flex-row justify-center rounded-md`}
/>
</RenderIf>
} else {
<DynamicFields
paymentType paymentMethod="wallet" paymentMethodType="google_pay" setRequiredFieldsBody
/>
</RenderIf>
}
}

let default = make
2 changes: 2 additions & 0 deletions src/Payments/GPay.resi
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
let default: (
~sessionObj: option<SessionsType.token>,
~thirdPartySessionObj: option<JSON.t>,
~walletOptions: array<string>,
~paymentType: CardThemeType.mode,
) => React.element
7 changes: 7 additions & 0 deletions src/Payments/PaymentMethodsRecord.res
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ type paymentMethodList = {
mandate_payment: option<mandate>,
payment_type: payment_type,
merchant_name: string,
collect_billing_details_from_wallets: bool,
}

let defaultPaymentMethodType = {
Expand All @@ -823,6 +824,7 @@ let defaultList = {
mandate_payment: None,
payment_type: NONE,
merchant_name: "",
collect_billing_details_from_wallets: true,
}
let getMethod = str => {
switch str {
Expand Down Expand Up @@ -1030,6 +1032,11 @@ let itemToObjMapper = dict => {
mandate_payment: getMandate(dict, "mandate_payment"),
payment_type: getString(dict, "payment_type", "")->paymentTypeMapper,
merchant_name: getString(dict, "merchant_name", ""),
collect_billing_details_from_wallets: getBool(
dict,
"collect_billing_details_from_wallets",
true,
),
}
}

Expand Down
13 changes: 10 additions & 3 deletions src/Payments/PaymentRequestButtonElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,15 @@ let make = (~sessions, ~walletOptions, ~paymentType) => {
switch googlePayThirdPartyToken {
| GooglePayThirdPartyTokenOptional(googlePayThirdPartyOptToken) =>
<GPayLazy
sessionObj=optToken thirdPartySessionObj=googlePayThirdPartyOptToken
sessionObj=optToken
thirdPartySessionObj=googlePayThirdPartyOptToken
walletOptions
paymentType
/>
| _ =>
<GPayLazy
sessionObj=optToken thirdPartySessionObj=None walletOptions paymentType
/>
| _ => <GPayLazy sessionObj=optToken thirdPartySessionObj=None />
}
| _ => React.null
}}
Expand All @@ -97,7 +103,8 @@ let make = (~sessions, ~walletOptions, ~paymentType) => {
</SessionPaymentWrapper>
| ApplePayWallet =>
switch applePayToken {
| ApplePayTokenOptional(optToken) => <ApplePayLazy sessionObj=optToken />
| ApplePayTokenOptional(optToken) =>
<ApplePayLazy sessionObj=optToken walletOptions paymentType />
| _ => React.null
}
| KlarnaWallet =>
Expand Down
52 changes: 49 additions & 3 deletions src/Utilities/ApplePayHelpers.res
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ let useHandleApplePayResponse = (
~syncPayment=() => (),
~isInvokeSDKFlow=true,
~isSavedMethodsFlow=false,
~isWallet=true,
~requiredFieldsBody=Dict.make(),
) => {
let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom)
let {publishableKey} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys)
Expand Down Expand Up @@ -187,8 +189,18 @@ let useHandleApplePayResponse = (
~isSavedMethodsFlow,
)

let bodyArr = if isWallet {
applePayBody
} else {
applePayBody
->getJsonFromArrayOfJson
->flattenObject(true)
->mergeTwoFlattenedJsonDicts(requiredFieldsBody)
->getArrayOfTupleFromDict
}

processPayment(
~bodyArr=applePayBody,
~bodyArr,
~isThirdPartyFlow=false,
~isGuestCustomer,
~paymentMethodListValue,
Expand All @@ -199,7 +211,7 @@ let useHandleApplePayResponse = (
)
} else if dict->Dict.get("showApplePayButton")->Option.isSome {
setApplePayClicked(_ => false)
if isSavedMethodsFlow {
if isSavedMethodsFlow || !isWallet {
postFailedSubmitResponse(~errortype="server_error", ~message="Something went wrong")
}
} else if dict->Dict.get("applePaySyncPayment")->Option.isSome {
Expand All @@ -216,7 +228,14 @@ let useHandleApplePayResponse = (
Window.removeEventListener("message", handleApplePayMessages)
},
)
}, (isInvokeSDKFlow, processPayment, stateJson, isManualRetryEnabled))
}, (
isInvokeSDKFlow,
processPayment,
stateJson,
isManualRetryEnabled,
isWallet,
requiredFieldsBody,
))
}

let handleApplePayButtonClicked = (~sessionObj, ~componentName) => {
Expand All @@ -227,3 +246,30 @@ let handleApplePayButtonClicked = (~sessionObj, ~componentName) => {
]
handlePostMessage(message)
}

let useSubmitCallback = (~isWallet, ~sessionObj, ~componentName) => {
let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid)
let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty)
let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom)
let {localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)

React.useCallback((ev: Window.event) => {
if !isWallet {
let json = ev.data->JSON.parseExn
let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper
if confirm.doSubmit && areRequiredFieldsValid && !areRequiredFieldsEmpty {
options.readOnly ? () : handleApplePayButtonClicked(~sessionObj, ~componentName)
} else if areRequiredFieldsEmpty {
postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterFieldsText,
)
} else if !areRequiredFieldsValid {
postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterValidDetailsText,
)
}
}
}, (areRequiredFieldsValid, areRequiredFieldsEmpty, isWallet, sessionObj, componentName))
}
Loading
Loading