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: address collection for one click widgets #361

Merged
merged 6 commits into from
May 17, 2024
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
190 changes: 83 additions & 107 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,34 @@ let make = (

let isGuestCustomer = UtilityHooks.useIsGuestCustomer()

React.useEffect0(() => {
AddressPaymentInput.importStates("./../States.json")
->then(res => {
setStatesJson(_ => res.states)
resolve()
})
->ignore

None
})

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 +231,6 @@ let make = (
(),
)
setApplePayClicked(_ => true)
open Promise
makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment)
->then(result => {
let result = result->JSON.Decode.bool->Option.getOr(false)
Expand Down Expand Up @@ -288,6 +266,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 @@ -301,13 +285,41 @@ let make = (
if dict->Dict.get("applePayProcessPayment")->Option.isSome {
let token =
dict->Dict.get("applePayProcessPayment")->Option.getOr(Dict.make()->JSON.Encode.object)

let billingContact =
dict
->Dict.get("applePayBillingContact")
ArushKapoorJuspay marked this conversation as resolved.
Show resolved Hide resolved
->Option.flatMap(JSON.Decode.object)
->Option.getOr(Dict.make())
->ApplePayTypes.billingContactItemToObjMapper

let shippingContact =
dict
->Dict.get("applePayShippingContact")
ArushKapoorJuspay marked this conversation as resolved.
Show resolved Hide resolved
->Option.flatMap(JSON.Decode.object)
->Option.getOr(Dict.make())
->ApplePayTypes.shippingContactItemToObjMapper

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

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

let applePayBody =
bodyDict
->Dict.fromArray
->JSON.Encode.object
ArushKapoorJuspay marked this conversation as resolved.
Show resolved Hide resolved
->Utils.flattenObject(true)
->Utils.mergeTwoFlattenedJsonDicts(requiredFieldsBody)
->Utils.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()
}
Expand All @@ -322,13 +334,12 @@ let make = (
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 +349,26 @@ let make = (
setIsShowOrPayUsing(_ => true)
}
None
}, (isApplePayReady, isInvokeSDKFlow, paymentExperience, isWallet))

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
}, (isApplePayReady, isInvokeSDKFlow, paymentExperience))

<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