Skip to content

Commit

Permalink
feat: added one click widgets (applepay, googlepay, paypal) (#271)
Browse files Browse the repository at this point in the history
Co-authored-by: Vrishab Srivatsa <[email protected]>
Co-authored-by: Praful Koppalkar <[email protected]>
  • Loading branch information
3 people authored Apr 30, 2024
1 parent 507a183 commit 509829a
Show file tree
Hide file tree
Showing 21 changed files with 374 additions and 161 deletions.
7 changes: 4 additions & 3 deletions src/App.res
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
@react.component
let make = () => {
let (logger, initTimestamp) = React.useMemo0(() => {
(OrcaLogger.make(), Date.now())
})
let url = RescriptReactRouter.useUrl()
let (integrateError, setIntegrateErrorError) = React.useState(() => false)
let setLoggerState = Recoil.useSetRecoilState(RecoilAtoms.loggerAtom)

let paymentMode = CardUtils.getQueryParamsDictforKey(url.search, "componentName")
let paymentType = paymentMode->CardThemeType.getPaymentMode
let (logger, initTimestamp) = React.useMemo0(() => {
(OrcaLogger.make(~source=Elements(paymentType), ()), Date.now())
})
let fullscreenMode = CardUtils.getQueryParamsDictforKey(url.search, "fullscreenType")

React.useEffect(() => {
Expand Down
11 changes: 0 additions & 11 deletions src/CardTheme.res
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,6 @@ let getShowLoader = (str, logger) => {
}
}

let getPaymentMode = val => {
switch val {
| "card" => Card
| "payment" => Payment
| "cardNumber" => CardNumberElement
| "cardExpiry" => CardExpiryElement
| "cardCvc" => CardCVCElement
| _ => NONE
}
}

let defaultAppearance = {
theme: Default,
variables: DefaultTheme.default,
Expand Down
4 changes: 4 additions & 0 deletions src/CardUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ let getCardBrandIcon = (cardType, paymentType) => {
| CardNumberElement
| CardExpiryElement
| CardCVCElement
| GooglePayElement
| PayPalElement
| ApplePayElement
| PaymentRequestButtonsElement
| NONE =>
<Icon size=brandIconSize name="default-card" />
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/DynamicFields.res
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let make = (
let {billingAddress} = Recoil.useRecoilValueFromAtom(optionAtom)

//<...>//
let paymentMethodTypes = DynamicFieldsUtils.usePaymentMethodTypeFromList(
let paymentMethodTypes = PaymentUtils.usePaymentMethodTypeFromList(
~list,
~paymentMethod,
~paymentMethodType,
Expand Down
2 changes: 1 addition & 1 deletion src/Components/SavedMethods.res
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ let make = (
| Some(ele) => ele
| None => ""
}->getCardType,
""->CardTheme.getPaymentMode,
""->CardThemeType.getPaymentMode,
)
}
let isActive = token == obj.paymentToken
Expand Down
11 changes: 8 additions & 3 deletions src/LoaderController.res
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,16 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime

let updateOptions = dict => {
let optionsDict = dict->getDictFromObj("options")
switch paymentMode->CardTheme.getPaymentMode {
switch paymentMode->CardThemeType.getPaymentMode {
| CardNumberElement
| CardExpiryElement
| CardCVCElement
| Card =>
setOptions(_ => ElementType.itemToObjMapper(optionsDict, logger))
| GooglePayElement
| PayPalElement
| ApplePayElement
| PaymentRequestButtonsElement
| Payment => {
let paymentOptions = PaymentType.itemToObjMapper(optionsDict, logger)
setOptionsPayment(_ => paymentOptions)
Expand Down Expand Up @@ -176,7 +180,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime
if dict->Dict.toArray->Array.length > 0 {
generateStyleSheet("", dict, "themestyle")
}
switch paymentMode->CardTheme.getPaymentMode {
switch paymentMode->CardThemeType.getPaymentMode {
| Payment => ()
| _ =>
let styleClass = [
Expand Down Expand Up @@ -493,8 +497,9 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime
}

React.useEffect(() => {
let iframeHeight = divH->Float.equal(0.0) ? divH : divH +. 1.0
handlePostMessage([
("iframeHeight", (divH +. 1.0)->JSON.Encode.float),
("iframeHeight", iframeHeight->JSON.Encode.float),
("iframeId", iframeId->JSON.Encode.string),
])
None
Expand Down
49 changes: 3 additions & 46 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
} = Recoil.useRecoilValueFromAtom(optionAtom)
let {themeObj, localeString} = Recoil.useRecoilValueFromAtom(configAtom)
let optionAtomValue = Recoil.useRecoilValueFromAtom(optionAtom)
let isApplePayReady = Recoil.useRecoilValueFromAtom(isApplePayReady)
let isGooglePayReady = Recoil.useRecoilValueFromAtom(isGooglePayReady)
let methodslist = Recoil.useRecoilValueFromAtom(list)
let paymentOrder = paymentMethodOrder->getOptionalArr->removeDuplicate
let (sessions, setSessions) = React.useState(_ => Dict.make()->JSON.Encode.object)
let (paymentOptions, setPaymentOptions) = React.useState(_ => [])
let (walletOptions, setWalletOptions) = React.useState(_ => [])
Expand Down Expand Up @@ -102,52 +99,12 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
None
}, [savedMethods])

let areAllGooglePayRequiredFieldsPrefilled = DynamicFieldsUtils.useAreAllRequiredFieldsPrefilled(
let (walletList, paymentOptionsList, actualList) = PaymentUtils.useGetPaymentMethodList(
~list,
~paymentMethod="wallet",
~paymentMethodType="google_pay",
~paymentOptions,
~paymentType,
)

let areAllApplePayRequiredFieldsPrefilled = DynamicFieldsUtils.useAreAllRequiredFieldsPrefilled(
~list,
~paymentMethod="wallet",
~paymentMethodType="apple_pay",
)

let (walletList, paymentOptionsList, actualList) = React.useMemo(() => {
switch methodslist {
| Loaded(paymentlist) =>
let paymentOrder =
paymentOrder->Array.length > 0 ? paymentOrder : PaymentModeType.defaultOrder
let plist = paymentlist->getDictFromJson->PaymentMethodsRecord.itemToObjMapper
let (wallets, otherOptions) =
plist->PaymentUtils.paymentListLookupNew(
~order=paymentOrder,
~showApplePay=isApplePayReady,
~showGooglePay=isGooglePayReady,
~areAllGooglePayRequiredFieldsPrefilled,
~areAllApplePayRequiredFieldsPrefilled,
)
(
wallets->removeDuplicate,
paymentOptions->Array.concat(otherOptions)->removeDuplicate,
otherOptions,
)
| SemiLoaded =>
showCardFormByDefault && checkPriorityList(paymentMethodOrder)
? ([], ["card"], [])
: ([], [], [])
| _ => ([], [], [])
}
}, (
methodslist,
paymentMethodOrder,
isApplePayReady,
isGooglePayReady,
areAllGooglePayRequiredFieldsPrefilled,
areAllApplePayRequiredFieldsPrefilled,
))

React.useEffect(() => {
switch methodslist {
| Loaded(paymentlist) =>
Expand Down
9 changes: 7 additions & 2 deletions src/PaymentElementRenderer.res
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ let make = (
switch (sessions, list) {
| (_, Loading) =>
<RenderIf condition=showLoader>
<PaymentElementShimmer />
{paymentType->Utils.isWalletElementPaymentType
? <WalletShimmer />
: <PaymentElementShimmer />}
</RenderIf>
| _ => <PaymentElement cardProps expiryProps cvcProps paymentType />
| _ =>
paymentType->Utils.isWalletElementPaymentType
? <WalletElement paymentType />
: <PaymentElement cardProps expiryProps cvcProps paymentType />
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/RenderPaymentMethods.res
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,16 @@ let make = (
paymentType cardProps expiryProps cvcProps zipProps handleElementFocus isFocus
/>
</React.Suspense>
| GooglePayElement
| PayPalElement
| ApplePayElement
| PaymentRequestButtonsElement
| Payment =>
<React.Suspense
fallback={<RenderIf condition={showLoader}>
<PaymentElementShimmer />
{paymentType->Utils.isWalletElementPaymentType
? <WalletShimmer />
: <PaymentElementShimmer />}
</RenderIf>}>
<PaymentElementRendererLazy paymentType cardProps expiryProps cvcProps />
</React.Suspense>
Expand Down
2 changes: 1 addition & 1 deletion src/ThreeDSAuth.res
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ let make = () => {
let (openModal, setOpenModal) = React.useState(_ => false)
let (loader, setloader) = React.useState(_ => true)

let logger = OrcaLogger.make()
let logger = OrcaLogger.make(~source=Elements(Payment), ())

React.useEffect0(() => {
handlePostMessage([("iframeMountedCallback", true->JSON.Encode.bool)])
Expand Down
2 changes: 1 addition & 1 deletion src/ThreeDSMethod.res
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
open Utils
@react.component
let make = () => {
let logger = OrcaLogger.make()
let logger = OrcaLogger.make(~source=Elements(Payment), ())

let (stateMetadata, setStateMetadata) = React.useState(_ => Dict.make()->JSON.Encode.object)

Expand Down
34 changes: 34 additions & 0 deletions src/Types/CardThemeType.res
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ type mode =
| CardNumberElement
| CardExpiryElement
| CardCVCElement
| GooglePayElement
| PayPalElement
| ApplePayElement
| PaymentRequestButtonsElement
| NONE
type label = Above | Floating | Never
type themeClass = {
Expand Down Expand Up @@ -80,3 +84,33 @@ type configClass = {
fonts: array<fonts>,
loader: showLoader,
}

let getPaymentMode = val => {
switch val {
| "card" => Card
| "payment" => Payment
| "cardNumber" => CardNumberElement
| "cardExpiry" => CardExpiryElement
| "cardCvc" => CardCVCElement
| "googlePay" => GooglePayElement
| "payPal" => PayPalElement
| "applePay" => ApplePayElement
| "paymentRequestButtons" => PaymentRequestButtonsElement
| _ => NONE
}
}

let getPaymentModeToStrMapper = val => {
switch val {
| Card => "Card"
| Payment => "Payment"
| CardNumberElement => "CardNumberElement"
| CardExpiryElement => "CardExpiryElement"
| CardCVCElement => "CardCVCElement"
| GooglePayElement => "GooglePayElement"
| PayPalElement => "PayPalElement"
| ApplePayElement => "ApplePayElement"
| PaymentRequestButtonsElement => "PaymentRequestButtonsElement"
| NONE => "None"
}
}
8 changes: 7 additions & 1 deletion src/Types/PaymentType.res
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ type wallets = {
walletReturnUrl: string,
applePay: showType,
googlePay: showType,
payPal: showType,
style: style,
}
type business = {name: string}
Expand Down Expand Up @@ -257,6 +258,7 @@ let defaultWallets = {
walletReturnUrl: "",
applePay: Auto,
googlePay: Auto,
payPal: Auto,
style: defaultStyle,
}
let defaultBillingAddress = {
Expand Down Expand Up @@ -743,7 +745,7 @@ let getWallets = (dict, str, logger) => {
->Option.flatMap(JSON.Decode.object)
->Option.map(json => {
unknownKeysWarning(
["applePay", "googlePay", "style", "walletReturnUrl"],
["applePay", "googlePay", "style", "walletReturnUrl", "payPal"],
json,
"options.wallets",
~logger,
Expand All @@ -759,6 +761,10 @@ let getWallets = (dict, str, logger) => {
"options.wallets.googlePay",
logger,
),
payPal: getWarningString(json, "payPal", "auto", ~logger)->getShowType(
"options.wallets.payPal",
logger,
),
style: getStyle(json, "style", logger),
}
})
Expand Down
21 changes: 0 additions & 21 deletions src/Utilities/DynamicFieldsUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -685,24 +685,3 @@ let useSubmitCallback = () => {
}
}, (line1, line2, state, city, postalCode))
}

let usePaymentMethodTypeFromList = (~list, ~paymentMethod, ~paymentMethodType) => {
React.useMemo(() => {
PaymentMethodsRecord.getPaymentMethodTypeFromList(
~list,
~paymentMethod,
~paymentMethodType=PaymentUtils.getPaymentMethodName(
~paymentMethodType=paymentMethod,
~paymentMethodName=paymentMethodType,
),
)->Option.getOr(PaymentMethodsRecord.defaultPaymentMethodType)
}, (list, paymentMethod, paymentMethodType))
}

let useAreAllRequiredFieldsPrefilled = (~list, ~paymentMethod, ~paymentMethodType) => {
let paymentMethodTypes = usePaymentMethodTypeFromList(~list, ~paymentMethod, ~paymentMethodType)

paymentMethodTypes.required_fields->Array.reduce(true, (acc, requiredField) => {
acc && requiredField.value != ""
})
}
26 changes: 21 additions & 5 deletions src/Utilities/PaymentHelpers.res
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,10 @@ let rec pollRetrievePaymentIntent = (
})
}

let retrieveStatus = (~headers, ~switchToCustomPod, pollID) => {
let retrieveStatus = (~headers, ~switchToCustomPod, pollID, logger) => {
open Promise
let endpoint = ApiEndpoint.getApiEndPoint()
let uri = `${endpoint}/poll/status/${pollID}`
let logger = OrcaLogger.make()
logApi(
~optLogger=Some(logger),
~url=uri,
Expand Down Expand Up @@ -245,9 +244,17 @@ let retrieveStatus = (~headers, ~switchToCustomPod, pollID) => {
})
}

let rec pollStatus = (~headers, ~switchToCustomPod, ~pollId, ~interval, ~count, ~returnUrl) => {
let rec pollStatus = (
~headers,
~switchToCustomPod,
~pollId,
~interval,
~count,
~returnUrl,
~logger,
) => {
open Promise
retrieveStatus(~headers, ~switchToCustomPod, pollId)
retrieveStatus(~headers, ~switchToCustomPod, pollId, logger)
->then(json => {
let dict = json->JSON.Decode.object->Option.getOr(Dict.make())
let status = dict->getString("status", "")
Expand All @@ -268,6 +275,7 @@ let rec pollStatus = (~headers, ~switchToCustomPod, ~pollId, ~interval, ~count,
~interval,
~count=count - 1,
~returnUrl,
~logger,
)
},
)
Expand All @@ -277,7 +285,15 @@ let rec pollStatus = (~headers, ~switchToCustomPod, ~pollId, ~interval, ~count,
})
->catch(e => {
Console.log2("Unable to retrieve payment due to following error", e)
pollStatus(~headers, ~switchToCustomPod, ~pollId, ~interval, ~count=count - 1, ~returnUrl)
pollStatus(
~headers,
~switchToCustomPod,
~pollId,
~interval,
~count=count - 1,
~returnUrl,
~logger,
)
})
}

Expand Down
Loading

0 comments on commit 509829a

Please sign in to comment.