Skip to content

Commit

Permalink
feat: address collection for one click widgets (#361)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArushKapoorJuspay committed May 17, 2024
1 parent 8d190fd commit 48c48a3
Show file tree
Hide file tree
Showing 25 changed files with 670 additions and 353 deletions.
16 changes: 15 additions & 1 deletion src/Components/DynamicFields.res
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,14 @@ let make = (
| BlikCode
| SpecialField(_)
| CountryAndPincode(_)
| AddressCountry(_) => React.null
| AddressCountry(_)
| ShippingName // Shipping Details are currently supported by only one click widgets
| ShippingAddressLine1
| ShippingAddressLine2
| ShippingAddressCity
| ShippingAddressPincode
| ShippingAddressState
| ShippingAddressCountry(_) => React.null
}}
</div>
})
Expand Down Expand Up @@ -757,6 +764,13 @@ let make = (
| CardExpiryAndCvc
| Currency(_)
| FullName
| ShippingName // Shipping Details are currently supported by only one click widgets
| ShippingAddressLine1
| ShippingAddressLine2
| ShippingAddressCity
| ShippingAddressPincode
| ShippingAddressState
| ShippingAddressCountry(_)
| None => React.null
}}
</div>
Expand Down
41 changes: 2 additions & 39 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
let (walletOptions, setWalletOptions) = React.useState(_ => [])
let {sdkHandleConfirmPayment} = Recoil.useRecoilValueFromAtom(optionAtom)

let (paymentMethodListValue, setPaymentMethodListValue) = Recoil.useRecoilState(
PaymentUtils.paymentMethodListValue,
)
let setPaymentMethodListValue = Recoil.useSetRecoilState(PaymentUtils.paymentMethodListValue)
let (cardsContainerWidth, setCardsContainerWidth) = React.useState(_ => 0)
let layoutClass = CardUtils.getLayoutClass(layout)
let (selectedOption, setSelectedOption) = Recoil.useRecoilState(selectedOptionAtom)
Expand Down Expand Up @@ -102,7 +100,6 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}, [savedMethods])

let (walletList, paymentOptionsList, actualList) = PaymentUtils.useGetPaymentMethodList(
~paymentMethodListValue,
~paymentOptions,
~paymentType,
)
Expand Down Expand Up @@ -226,15 +223,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}
let dict = sessions->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)
let googlePayThirdPartyToken = SessionsType.getPaymentSessionObj(
googlePayThirdPartySessionObj.sessionsToken,
Gpay,
)

let loader = () => {
handlePostMessageEvents(
Expand Down Expand Up @@ -299,32 +288,6 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
<React.Suspense fallback={loader()}>
<BecsBankDebitLazy paymentType />
</React.Suspense>
| GooglePay =>
switch gPayToken {
| OtherTokenOptional(optToken) =>
switch googlePayThirdPartyToken {
| GooglePayThirdPartyTokenOptional(googlePayThirdPartyOptToken) =>
<React.Suspense fallback={loader()}>
<GPayLazy
paymentType
sessionObj=optToken
thirdPartySessionObj=googlePayThirdPartyOptToken
walletOptions
/>
</React.Suspense>
| _ =>
<React.Suspense fallback={loader()}>
<GPayLazy paymentType sessionObj=optToken thirdPartySessionObj=None walletOptions />
</React.Suspense>
}
| _ => React.null
}
| ApplePay =>
switch applePayToken {
| ApplePayTokenOptional(optToken) =>
<ApplePayLazy sessionObj=optToken walletOptions paymentType />
| _ => React.null
}
| Boleto =>
<React.Suspense fallback={loader()}>
<BoletoLazy paymentType />
Expand Down Expand Up @@ -381,7 +344,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
showFields}>
<div className="flex flex-col place-items-center">
<ErrorBoundary key="payment_request_buttons_all" level={ErrorBoundary.RequestButton}>
<PaymentRequestButtonElement sessions walletOptions />
<PaymentRequestButtonElement sessions walletOptions paymentType />
</ErrorBoundary>
<RenderIf
condition={paymentOptions->Array.length > 0 &&
Expand Down
4 changes: 2 additions & 2 deletions src/PaymentElementRenderer.res
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ let make = (
switch (sessions, paymentMethodList) {
| (_, Loading) =>
<RenderIf condition=showLoader>
{paymentType->Utils.isWalletElementPaymentType
{paymentType->Utils.getIsWalletElementPaymentType
? <WalletShimmer />
: <PaymentElementShimmer />}
</RenderIf>
| _ =>
paymentType->Utils.isWalletElementPaymentType
paymentType->Utils.getIsWalletElementPaymentType
? <WalletElement paymentType />
: <PaymentElement cardProps expiryProps cvcProps paymentType />
}
Expand Down
182 changes: 72 additions & 110 deletions src/Payments/ApplePay.res
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
open Utils
open Promise
@react.component
let make = (
~sessionObj: option<JSON.t>,
~paymentType: option<CardThemeType.mode>,
~walletOptions: array<string>,
) => {
let make = (~sessionObj: option<JSON.t>) => {
let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)
let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom(
RecoilAtoms.keys,
Expand All @@ -18,19 +15,9 @@ let make = (
let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom)
let (applePayClicked, setApplePayClicked) = React.useState(_ => false)
let isApplePaySDKFlow = sessionObj->Option.isSome
let {localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Dict.make())
let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid)
let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty)
let isWallet = walletOptions->Array.includes("apple_pay")
let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered)
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)

UtilityHooks.useHandlePostMessages(
~complete=areRequiredFieldsValid,
~empty=areRequiredFieldsEmpty,
~paymentType="apple_pay",
)
let (stateJson, setStatesJson) = React.useState(_ => JSON.Encode.null)

let applePayPaymentMethodType = React.useMemo(() => {
switch PaymentMethodsRecord.getPaymentMethodTypeFromList(
Expand Down Expand Up @@ -60,42 +47,25 @@ let make = (

let isGuestCustomer = UtilityHooks.useIsGuestCustomer()

PaymentUtils.useStatesJson(setStatesJson)

let processPayment = (bodyArr, ~isThirdPartyFlow=false, ()) => {
let requestBody = PaymentUtils.appendedCustomerAcceptance(
~isGuestCustomer,
~paymentType=paymentMethodListValue.payment_type,
~body=bodyArr,
)

if isWallet {
intent(
~bodyArr=requestBody,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
},
~handleUserError=true,
~isThirdPartyFlow,
(),
)
} else {
let requiredFieldsBodyArr =
requestBody
->getJsonFromArrayOfJson
->flattenObject(true)
->mergeTwoFlattenedJsonDicts(requiredFieldsBody)
->getArrayOfTupleFromDict
intent(
~bodyArr=requiredFieldsBodyArr,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
},
~handleUserError=true,
~isThirdPartyFlow,
(),
)
}
intent(
~bodyArr=requestBody,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
},
~handleUserError=true,
~isThirdPartyFlow,
(),
)
}

let syncPayment = () => {
Expand Down Expand Up @@ -252,7 +222,6 @@ let make = (
(),
)
setApplePayClicked(_ => true)
open Promise
makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment)
->then(result => {
let result = result->JSON.Decode.bool->Option.getOr(false)
Expand All @@ -274,7 +243,7 @@ let make = (
processPayment(bodyDict, ~isThirdPartyFlow=true, ())
} else {
let message = [("applePayButtonClicked", true->JSON.Encode.bool)]
Utils.handlePostMessage(message)
handlePostMessage(message)
}
} else {
let bodyDict = PaymentBody.applePayRedirectBody(~connectors)
Expand All @@ -288,6 +257,12 @@ let make = (
->ignore
}

let paymentMethodTypes = DynamicFieldsUtils.usePaymentMethodTypeFromList(
~paymentMethodListValue,
~paymentMethod="wallet",
~paymentMethodType="apple_pay",
)

React.useEffect(() => {
let handleApplePayMessages = (ev: Window.event) => {
let json = try {
Expand All @@ -297,38 +272,60 @@ let make = (
}

try {
let dict = json->Utils.getDictFromJson
let dict = json->getDictFromJson
if dict->Dict.get("applePayProcessPayment")->Option.isSome {
let token =
dict->Dict.get("applePayProcessPayment")->Option.getOr(Dict.make()->JSON.Encode.object)

let billingContact =
dict
->getDictfromDict("applePayBillingContact")
->ApplePayTypes.billingContactItemToObjMapper

let shippingContact =
dict
->getDictfromDict("applePayShippingContact")
->ApplePayTypes.shippingContactItemToObjMapper

let requiredFieldsBody = DynamicFieldsUtils.getApplePayRequiredFields(
~billingContact,
~shippingContact,
~paymentMethodTypes,
~statesList=stateJson,
)

let bodyDict = PaymentBody.applePayBody(~token, ~connectors)
processPayment(bodyDict, ())

let applePayBody =
bodyDict
->getJsonFromArrayOfJson
->flattenObject(true)
->mergeTwoFlattenedJsonDicts(requiredFieldsBody)
->getArrayOfTupleFromDict

processPayment(applePayBody, ())
} else if dict->Dict.get("showApplePayButton")->Option.isSome {
setApplePayClicked(_ => false)
if !isWallet {
postFailedSubmitResponse(~errortype="server_error", ~message="Something went wrong")
}
} else if dict->Dict.get("applePaySyncPayment")->Option.isSome {
syncPayment()
}
} catch {
| _ => Utils.logInfo(Console.log("Error in parsing Apple Pay Data"))
| _ => logInfo(Console.log("Error in parsing Apple Pay Data"))
}
}
Window.addEventListener("message", handleApplePayMessages)
Some(
() => {
Utils.handlePostMessage([("applePaySessionAbort", true->JSON.Encode.bool)])
handlePostMessage([("applePaySessionAbort", true->JSON.Encode.bool)])
Window.removeEventListener("message", handleApplePayMessages)
},
)
}, (isInvokeSDKFlow, requiredFieldsBody, isWallet, processPayment))
}, (isInvokeSDKFlow, processPayment, stateJson))

React.useEffect(() => {
if (
(isInvokeSDKFlow || paymentExperience == PaymentMethodsRecord.RedirectToURL) &&
isApplePayReady &&
isWallet
isApplePayReady
) {
setShowApplePay(_ => true)
areOneClickWalletsRendered(prev => {
Expand All @@ -338,61 +335,26 @@ let make = (
setIsShowOrPayUsing(_ => true)
}
None
}, (isApplePayReady, isInvokeSDKFlow, paymentExperience, isWallet))
}, (isApplePayReady, isInvokeSDKFlow, paymentExperience))

let submitCallback = 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
? ()
: handlePostMessage([("applePayButtonClicked", true->JSON.Encode.bool)])
} else if areRequiredFieldsEmpty {
postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterFieldsText,
)
} else if !areRequiredFieldsValid {
postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterValidDetailsText,
)
}
}
}, (areRequiredFieldsValid, areRequiredFieldsEmpty))
useSubmitPaymentData(submitCallback)

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

let default = make
6 changes: 1 addition & 5 deletions src/Payments/ApplePay.resi
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
@react.component
let default: (
~sessionObj: option<JSON.t>,
~paymentType: option<CardThemeType.mode>,
~walletOptions: array<string>,
) => React.element
let default: (~sessionObj: option<JSON.t>) => React.element
Loading

0 comments on commit 48c48a3

Please sign in to comment.