Skip to content

Commit

Permalink
Feature/boa apple pay (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
prafulkoppalkar authored Dec 21, 2023
1 parent cfe4bef commit 06b3f3e
Show file tree
Hide file tree
Showing 12 changed files with 307 additions and 119 deletions.
100 changes: 74 additions & 26 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.
22 changes: 16 additions & 6 deletions src/LoaderController.res
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => {
let setBlockConfirm = Recoil.useSetRecoilState(isConfirmBlocked)
let setSwitchToCustomPod = Recoil.useSetRecoilState(switchToCustomPod)
let setIsGooglePayReady = Recoil.useSetRecoilState(isGooglePayReady)
let setIsApplePayReady = Recoil.useSetRecoilState(isApplePayReady)
let (divH, setDivH) = React.useState(_ => 0.0)
let {showCardFormByDefault, paymentMethodOrder} = optionsPayment

Expand Down Expand Up @@ -66,7 +67,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => {

setConfig(._ => {
config: {
appearance: appearance,
appearance,
locale: config.locale,
fonts: config.fonts,
clientSecret: config.clientSecret,
Expand All @@ -82,6 +83,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => {

React.useEffect0(() => {
handlePostMessage([("iframeMounted", true->Js.Json.boolean)])
handlePostMessage([("applePayMounted", true->Js.Json.boolean)])
logger.setLogInitiated()
Window.addEventListener("click", ev =>
handleOnClickPostMessage(~targetOrigin=keys.parentURL, ev)
Expand Down Expand Up @@ -296,9 +298,15 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => {
let customerPaymentMethods = dict->PaymentType.createCustomerObjArr
setOptionsPayment(.prev => {
...prev,
customerPaymentMethods: customerPaymentMethods,
customerPaymentMethods,
})
}
if dict->Js.Dict.get("applePayCanMakePayments")->Belt.Option.isSome {
setIsApplePayReady(._ => true)
}
if dict->Js.Dict.get("applePaySessionObjNotPresent")->Belt.Option.isSome {
setIsApplePayReady(.prev => prev && false)
}
} catch {
| _ => setIntegrateErrorError(_ => true)
}
Expand All @@ -319,10 +327,12 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => {
]->Js.Array2.forEach(val => {
let (value, setValue) = val
if value != "" {
setValue(.prev => {
...prev,
value: value,
})
setValue(.
prev => {
...prev,
value,
},
)
}
})
if optionsPayment.defaultValues.billingDetails.address.country === "" {
Expand Down
21 changes: 18 additions & 3 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ let make = (
let {showCardFormByDefault, paymentMethodOrder, layout} = Recoil.useRecoilValueFromAtom(
optionAtom,
)
let isApplePayReady = Recoil.useRecoilValueFromAtom(isApplePayReady)
let isGooglePayReady = Recoil.useRecoilValueFromAtom(isGooglePayReady)
let pmList = Recoil.useRecoilValueFromAtom(list)
let methodslist = Recoil.useRecoilValueFromAtom(list)
let paymentOrder = paymentMethodOrder->Utils.getOptionalArr->Utils.removeDuplicate
Expand All @@ -35,12 +37,17 @@ let make = (
let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom)
let isShowOrPayUsing = Recoil.useRecoilValueFromAtom(isShowOrPayUsing)

let (walletList, paymentOptionsList, actualList) = React.useMemo2(() => {
let (walletList, paymentOptionsList, actualList) = React.useMemo4(() => {
switch methodslist {
| Loaded(paymentlist) =>
let paymentOrder = paymentOrder->Js.Array2.length > 0 ? paymentOrder : defaultOrder
let plist = paymentlist->Utils.getDictFromJson->PaymentMethodsRecord.itemToObjMapper
let (wallets, otherOptions) = plist->PaymentUtils.paymentListLookupNew(~order=paymentOrder)
let (wallets, otherOptions) =
plist->PaymentUtils.paymentListLookupNew(
~order=paymentOrder,
~showApplePay=isApplePayReady,
~showGooglePay=isGooglePayReady,
)
(
wallets->Utils.removeDuplicate,
paymentOptions->Js.Array2.concat(otherOptions)->Utils.removeDuplicate,
Expand All @@ -52,7 +59,7 @@ let make = (
: ([], [], [])
| _ => ([], [], [])
}
}, (methodslist, paymentMethodOrder))
}, (methodslist, paymentMethodOrder, isApplePayReady, isGooglePayReady))

React.useEffect4(() => {
switch methodslist {
Expand Down Expand Up @@ -175,6 +182,8 @@ let make = (
}
let dict = sessions->Utils.getDictFromJson
let sessionObj = SessionsType.itemToObjMapper(dict, Others)
let applePaySessionObj = SessionsType.itemToObjMapper(dict, ApplePayObject)
let applePayToken = SessionsType.getPaymentSessionObj(applePaySessionObj.sessionsToken, ApplePay)
let klarnaTokenObj = SessionsType.getPaymentSessionObj(sessionObj.sessionsToken, Klarna)
let gPayToken = SessionsType.getPaymentSessionObj(sessionObj.sessionsToken, Gpay)
let googlePayThirdPartySessionObj = SessionsType.itemToObjMapper(dict, GooglePayThirdPartyObject)
Expand Down Expand Up @@ -269,6 +278,12 @@ let make = (
}
| _ => React.null
}
| ApplePay =>
switch applePayToken {
| ApplePayTokenOptional(optToken) =>
<ApplePayLazy sessionObj=optToken list walletOptions paymentType />
| _ => React.null
}
| _ =>
<React.Suspense fallback={loader()}>
<PaymentMethodsWrapperLazy paymentType list paymentMethodName=selectedOption />
Expand Down
142 changes: 107 additions & 35 deletions src/Payments/ApplePay.res
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
open Utils
@react.component
let make = (~sessionObj: option<Js.Json.t>, ~list: PaymentMethodsRecord.list) => {
let make = (
~sessionObj: option<Js.Json.t>,
~list: PaymentMethodsRecord.list,
~paymentType: option<CardThemeType.mode>,
~walletOptions: array<string>,
) => {
let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)
let {publishableKey} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys)
let isApplePayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isApplePayReady)
let setIsShowOrPayUsing = Recoil.useSetRecoilState(RecoilAtoms.isShowOrPayUsing)
let (showApplePay, setShowApplePay) = React.useState(() => false)
let (showApplePayLoader, setShowApplePayLoader) = React.useState(() => false)
Expand All @@ -10,6 +17,11 @@ let make = (~sessionObj: option<Js.Json.t>, ~list: PaymentMethodsRecord.list) =>
let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom)
let (applePayClicked, setApplePayClicked) = React.useState(_ => false)
let isApplePaySDKFlow = sessionObj->Belt.Option.isSome
let {localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Js.Dict.empty())
let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid)
let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty)
let isWallet = walletOptions->Js.Array2.includes("apple_pay")

let applePayPaymentMethodType = React.useMemo1(() => {
switch PaymentMethodsRecord.getPaymentMethodTypeFromList(
Expand Down Expand Up @@ -37,15 +49,34 @@ let make = (~sessionObj: option<Js.Json.t>, ~list: PaymentMethodsRecord.list) =>
: list->PaymentUtils.getConnectors(Wallets(ApplePay(Redirect)))

let processPayment = bodyArr => {
intent(
~bodyArr,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
},
~handleUserError=true,
(),
)
if isWallet {
intent(
~bodyArr,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
},
~handleUserError=true,
(),
)
} else {
let requiredFieldsBodyArr =
bodyArr
->Js.Dict.fromArray
->Js.Json.object_
->OrcaUtils.flattenObject(true)
->OrcaUtils.mergeTwoFlattenedJsonDicts(requiredFieldsBody)
->OrcaUtils.getArrayOfTupleFromDict
intent(
~bodyArr=requiredFieldsBodyArr,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
},
~handleUserError=true,
(),
)
}
}

let syncPayment = () => {
Expand Down Expand Up @@ -229,7 +260,6 @@ let make = (~sessionObj: option<Js.Json.t>, ~list: PaymentMethodsRecord.list) =>
}

React.useEffect1(() => {
Utils.handlePostMessage([("applePayMounted", true->Js.Json.boolean)])
let handleApplePayMessages = (ev: Window.event) => {
let json = try {
ev.data->Js.Json.parseExn
Expand All @@ -239,11 +269,7 @@ let make = (~sessionObj: option<Js.Json.t>, ~list: PaymentMethodsRecord.list) =>

try {
let dict = json->Utils.getDictFromJson
if dict->Js.Dict.get("applePayCanMakePayments")->Belt.Option.isSome {
if isInvokeSDKFlow || paymentExperience == PaymentMethodsRecord.RedirectToURL {
setShowApplePay(_ => true)
}
} else if dict->Js.Dict.get("applePayProcessPayment")->Belt.Option.isSome {
if dict->Js.Dict.get("applePayProcessPayment")->Belt.Option.isSome {
let token =
dict
->Js.Dict.get("applePayProcessPayment")
Expand All @@ -268,26 +294,72 @@ let make = (~sessionObj: option<Js.Json.t>, ~list: PaymentMethodsRecord.list) =>
)
}, [isInvokeSDKFlow])

<div>
<style> {React.string(css)} </style>
{if showApplePay {
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>
React.useEffect1(() => {
if (
(isInvokeSDKFlow || paymentExperience == PaymentMethodsRecord.RedirectToURL) &&
isApplePayReady
) {
setShowApplePay(_ => true)
}
None
}, [isApplePayReady])

let submitCallback = React.useCallback((ev: Window.event) => {
if !isWallet {
let json = ev.data->Js.Json.parseExn
let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper
if confirm.doSubmit && areRequiredFieldsValid && !areRequiredFieldsEmpty {
options.readOnly
? ()
: handlePostMessage([("applePayButtonClicked", true->Js.Json.boolean)])
} else if areRequiredFieldsEmpty {
postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterFieldsText,
)
} else if !areRequiredFieldsValid {
postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterValidDetailsText,
)
}
} else {
React.null
}}
</div>
}
})
submitPaymentData(submitCallback)

{
isWallet
? <div>
<style> {React.string(css)} </style>
{if showApplePay {
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>
}
} else {
React.null
}}
</div>
: <DynamicFields
paymentType={switch paymentType {
| Some(val) => val
| _ => NONE
}}
list
paymentMethod="wallet"
paymentMethodType="apple_pay"
setRequiredFieldsBody
/>
}
}

let default = make
7 changes: 6 additions & 1 deletion src/Payments/ApplePay.resi
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
@react.component
let default: (~sessionObj: option<Js.Json.t>, ~list: PaymentMethodsRecord.list) => React.element
let default: (
~sessionObj: option<Js.Json.t>,
~list: PaymentMethodsRecord.list,
~paymentType: option<CardThemeType.mode>,
~walletOptions: array<string>,
) => React.element
2 changes: 2 additions & 0 deletions src/Payments/ApplePayLazy.res
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ open LazyUtils
type props = {
sessionObj: option<Js.Json.t>,
list: PaymentMethodsRecord.list,
paymentType: CardThemeType.mode,
walletOptions: array<string>,
}

let make: props => React.element = reactLazy(.() => import_("./ApplePay.bs.js"))
25 changes: 17 additions & 8 deletions src/Payments/PaymentMethodsRecord.res
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ let paymentMethodsFields = [
displayName: "Google Pay",
miniIcon: None,
},
{
paymentMethodName: "apple_pay",
fields: [],
icon: Some(icon("apple_pay", ~size=19, ~width=25)),
displayName: "Apple Pay",
miniIcon: None,
},
{
paymentMethodName: "mb_way",
fields: [SpecialField(<PhoneNumberPaymentInput />), InfoElement],
Expand Down Expand Up @@ -540,6 +547,7 @@ let dynamicFieldsEnabledPaymentMethods = [
"credit",
"blik",
"google_pay",
"apple_pay",
]

let getPaymentMethodFields = (
Expand Down Expand Up @@ -742,7 +750,7 @@ let getSurchargeDetails = dict => {

if displayTotalSurchargeAmount !== 0.0 {
Some({
displayTotalSurchargeAmount,
displayTotalSurchargeAmount: displayTotalSurchargeAmount,
})
} else {
None
Expand Down Expand Up @@ -864,21 +872,22 @@ let buildFromPaymentList = (plist: list) => {
paymentMethodObject.payment_method_types->Js.Array2.map(individualPaymentMethod => {
let paymentMethodName = individualPaymentMethod.payment_method_type
let bankNames = individualPaymentMethod.bank_names
let paymentExperience =
individualPaymentMethod.payment_experience->Js.Array2.map(experience => {
let paymentExperience = individualPaymentMethod.payment_experience->Js.Array2.map(
experience => {
(experience.payment_experience_type, experience.eligible_connectors)
})
},
)
{
paymentMethodName: paymentMethodName,
paymentMethodName,
fields: getPaymentMethodFields(
paymentMethodName,
individualPaymentMethod.required_fields,
(),
),
paymentFlow: paymentExperience,
handleUserError: handleUserError,
methodType: methodType,
bankNames: bankNames,
handleUserError,
methodType,
bankNames,
}
})
})
Expand Down
3 changes: 2 additions & 1 deletion src/Payments/PaymentRequestButtonElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ let make = (~sessions, ~walletOptions, ~list: PaymentMethodsRecord.list) => {
</SessionPaymentWrapper>
| ApplePayWallet =>
switch applePayToken {
| ApplePayTokenOptional(optToken) => <ApplePayLazy sessionObj=optToken list />
| ApplePayTokenOptional(optToken) =>
<ApplePayLazy sessionObj=optToken list paymentType=NONE walletOptions />
| _ => React.null
}

Expand Down
Loading

0 comments on commit 06b3f3e

Please sign in to comment.