diff --git a/CHANGELOG.md b/CHANGELOG.md index bae625bf..3e913f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2370,4 +2370,4 @@ ### Features -* updated Readme ([427f8fd](https://github.com/juspay/hyperswitch-web/commit/427f8fd91be58fc63b0fa4ab326d562a4caaabab)) +* updated Readme ([427f8fd](https://github.com/juspay/hyperswitch-web/commit/427f8fd91be58fc63b0fa4ab326d562a4caaabab)) \ No newline at end of file diff --git a/src/LoaderController.res b/src/LoaderController.res index 31fa6e4a..838cedee 100644 --- a/src/LoaderController.res +++ b/src/LoaderController.res @@ -38,6 +38,9 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime let (isCompleteCallbackUsed, setIsCompleteCallbackUsed) = Recoil.useRecoilState( isCompleteCallbackUsed, ) + let (isPaymentButtonHandlerProvided, setIsPaymentButtonHandlerProvided) = Recoil.useRecoilState( + isPaymentButtonHandlerProvidedAtom, + ) let optionsCallback = (optionsPayment: PaymentType.options) => { [ @@ -248,10 +251,15 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime let metadata = dict->getJsonObjectFromDict("analyticsMetadata") logger.setMetadata(metadata) } + if dict->getDictIsSome("onCompleteDoThisUsed") { let isCallbackUsedVal = dict->Utils.getBool("onCompleteDoThisUsed", false) setIsCompleteCallbackUsed(_ => isCallbackUsedVal) } + if dict->getDictIsSome("isPaymentButtonHandlerProvided") { + let isSDKClick = dict->Utils.getBool("isPaymentButtonHandlerProvided", false) + setIsPaymentButtonHandlerProvided(_ => isSDKClick) + } if dict->getDictIsSome("paymentOptions") { let paymentOptions = dict->getDictFromObj("paymentOptions") diff --git a/src/Payments/ApplePay.res b/src/Payments/ApplePay.res index afbc951e..0152259e 100644 --- a/src/Payments/ApplePay.res +++ b/src/Payments/ApplePay.res @@ -5,9 +5,10 @@ let make = (~sessionObj: option, ~walletOptions, ~paymentType: CardTheme let url = RescriptReactRouter.useUrl() let componentName = CardUtils.getQueryParamsDictforKey(url.search, "componentName") let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) - let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom( - RecoilAtoms.keys, + let sdkHandleIsThere = Recoil.useRecoilValueFromAtom( + RecoilAtoms.isPaymentButtonHandlerProvidedAtom, ) + let {publishableKey} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) let isApplePayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isApplePayReady) let setIsShowOrPayUsing = Recoil.useSetRecoilState(RecoilAtoms.isShowOrPayUsing) let (showApplePay, setShowApplePay) = React.useState(() => false) @@ -212,7 +213,7 @@ let make = (~sessionObj: option, ~walletOptions, ~paymentType: CardTheme ~paymentMethod="APPLE_PAY", ) setApplePayClicked(_ => true) - makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment) + makeOneClickHandlerPromise(sdkHandleIsThere) ->then(result => { let result = result->JSON.Decode.bool->Option.getOr(false) if result { @@ -241,7 +242,11 @@ let make = (~sessionObj: option, ~walletOptions, ~paymentType: CardTheme ~isManualRetryEnabled, ) } else { - ApplePayHelpers.handleApplePayButtonClicked(~sessionObj, ~componentName, ~paymentMethodListValue) + ApplePayHelpers.handleApplePayButtonClicked( + ~sessionObj, + ~componentName, + ~paymentMethodListValue, + ) } } else { let bodyDict = PaymentBody.applePayRedirectBody(~connectors) diff --git a/src/Payments/GPay.res b/src/Payments/GPay.res index 92e008ec..d9e0c69d 100644 --- a/src/Payments/GPay.res +++ b/src/Payments/GPay.res @@ -15,7 +15,8 @@ let make = ( let componentName = CardUtils.getQueryParamsDictforKey(url.search, "componentName") let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) let {iframeId} = Recoil.useRecoilValueFromAtom(keys) - let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom(keys) + let isSDKHandleClick = Recoil.useRecoilValueFromAtom(isPaymentButtonHandlerProvidedAtom) + let {publishableKey} = Recoil.useRecoilValueFromAtom(keys) let options = Recoil.useRecoilValueFromAtom(optionAtom) let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Gpay) let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(RecoilAtoms.isManualRetryEnabled) @@ -103,7 +104,7 @@ let make = ( ~eventName=GOOGLE_PAY_FLOW, ~paymentMethod="GOOGLE_PAY", ) - makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment)->then(result => { + makeOneClickHandlerPromise(isSDKHandleClick)->then(result => { let result = result->JSON.Decode.bool->Option.getOr(false) if result { if isInvokeSDKFlow { diff --git a/src/Payments/KlarnaSDK.res b/src/Payments/KlarnaSDK.res index b5b80397..a53771a7 100644 --- a/src/Payments/KlarnaSDK.res +++ b/src/Payments/KlarnaSDK.res @@ -9,11 +9,12 @@ open KlarnaSDKTypes let make = (~sessionObj: SessionsType.token) => { let url = RescriptReactRouter.useUrl() let componentName = CardUtils.getQueryParamsDictforKey(url.search, "componentName") - let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) - let setIsShowOrPayUsing = Recoil.useSetRecoilState(RecoilAtoms.isShowOrPayUsing) - let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom(keys) + let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) + let setIsShowOrPayUsing = Recoil.useSetRecoilState(isShowOrPayUsing) + let sdkHandleIsThere = Recoil.useRecoilValueFromAtom(isPaymentButtonHandlerProvidedAtom) + let {publishableKey} = Recoil.useRecoilValueFromAtom(keys) let options = Recoil.useRecoilValueFromAtom(optionAtom) - let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(RecoilAtoms.isManualRetryEnabled) + let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(isManualRetryEnabled) let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Other) let {iframeId} = Recoil.useRecoilValueFromAtom(keys) let status = CommonHooks.useScript("https://x.klarnacdn.net/kp/lib/v1/api.js") // Klarna SDK script @@ -65,7 +66,7 @@ let make = (~sessionObj: SessionsType.token) => { theme: options.wallets.style.theme == Dark ? "default" : "outlined", shape: "default", on_click: authorize => { - makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment)->then( + makeOneClickHandlerPromise(sdkHandleIsThere)->then( result => { let result = result->JSON.Decode.bool->Option.getOr(false) if result { diff --git a/src/Payments/PayPal.res b/src/Payments/PayPal.res index acc65239..f7edbb5f 100644 --- a/src/Payments/PayPal.res +++ b/src/Payments/PayPal.res @@ -14,7 +14,8 @@ let payPalIcon = let make = () => { let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) let (paypalClicked, setPaypalClicked) = React.useState(_ => false) - let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom(keys) + let sdkHandleIsThere = Recoil.useRecoilValueFromAtom(isPaymentButtonHandlerProvidedAtom) + let {publishableKey} = Recoil.useRecoilValueFromAtom(keys) let options = Recoil.useRecoilValueFromAtom(optionAtom) let areOneClickWalletsRendered = Recoil.useSetRecoilState(areOneClickWalletsRendered) let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) @@ -48,7 +49,7 @@ let make = () => { ) setPaypalClicked(_ => true) open Promise - Utils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment) + Utils.makeOneClickHandlerPromise(sdkHandleIsThere) ->then(result => { let result = result->JSON.Decode.bool->Option.getOr(false) if result { diff --git a/src/Payments/PaypalSDK.res b/src/Payments/PaypalSDK.res index f92957c6..1abbc288 100644 --- a/src/Payments/PaypalSDK.res +++ b/src/Payments/PaypalSDK.res @@ -5,6 +5,9 @@ let make = (~sessionObj: SessionsType.token, ~paymentType: CardThemeType.mode) = let {iframeId, publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom( RecoilAtoms.keys, ) + let sdkHandleIsThere = Recoil.useRecoilValueFromAtom( + RecoilAtoms.isPaymentButtonHandlerProvidedAtom, + ) let (loggerState, _setLoggerState) = Recoil.useRecoilState(RecoilAtoms.loggerAtom) let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered) let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) @@ -92,6 +95,7 @@ let make = (~sessionObj: SessionsType.token, ~paymentType: CardThemeType.mode) = ~areOneClickWalletsRendered, ~setIsCompleted, ~isCallbackUsedVal, + ~sdkHandleIsThere, ) }) Window.body->Window.appendChild(paypalScript) diff --git a/src/Payments/PaypalSDKHelpers.res b/src/Payments/PaypalSDKHelpers.res index c9287ff5..e05a477b 100644 --- a/src/Payments/PaypalSDKHelpers.res +++ b/src/Payments/PaypalSDKHelpers.res @@ -21,6 +21,7 @@ let loadPaypalSDK = ( ) => unit, ~setIsCompleted, ~isCallbackUsedVal: bool, + ~sdkHandleIsThere: bool, ) => { loggerState.setLogInfo( ~value="Paypal SDK Button Clicked", @@ -34,7 +35,7 @@ let loadPaypalSDK = ( style: buttonStyle, fundingSource: paypal["FUNDING"]["PAYPAL"], createOrder: () => { - Utils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment)->then(result => { + Utils.makeOneClickHandlerPromise(sdkHandleIsThere)->then(result => { let result = result->JSON.Decode.bool->Option.getOr(false) if result { Utils.messageParentWindow([ diff --git a/src/Utilities/RecoilAtoms.res b/src/Utilities/RecoilAtoms.res index 6c2da891..d1207655 100644 --- a/src/Utilities/RecoilAtoms.res +++ b/src/Utilities/RecoilAtoms.res @@ -74,6 +74,7 @@ let userPixKey = Recoil.atom("userPixKey", defaultFieldValues) let userPixCPF = Recoil.atom("userPixCPF", defaultFieldValues) let userPixCNPJ = Recoil.atom("userPixCNPJ", defaultFieldValues) let isCompleteCallbackUsed = Recoil.atom("isCompleteCallbackUsed", false) +let isPaymentButtonHandlerProvidedAtom = Recoil.atom("isPaymentButtonHandlerProvidedAtom", false) type areOneClickWalletsRendered = { isGooglePay: bool, diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res index 5c35b352..5a3b518f 100644 --- a/src/Utilities/Utils.res +++ b/src/Utilities/Utils.res @@ -1212,25 +1212,25 @@ let getThemePromise = dict => { } } -let makeOneClickHandlerPromise = sdkHandleOneClickConfirmPayment => { - open EventListenerManager +let makeOneClickHandlerPromise = sdkHandleIsThere => { Promise.make((resolve, _) => { - if sdkHandleOneClickConfirmPayment { + if !sdkHandleIsThere { resolve(JSON.Encode.bool(true)) } else { - let handleMessage = (event: Types.event) => { + let handleMessage = (event: Window.event) => { let json = try { - event.data->anyTypeToJson + event.data->safeParse } catch { | _ => JSON.Encode.null } let dict = json->getDictFromJson - if dict->Dict.get("oneClickDoSubmit")->Option.isSome { - resolve(dict->Dict.get("oneClickDoSubmit")->Option.getOr(true->JSON.Encode.bool)) + + if dict->Dict.get("walletClickEvent")->Option.isSome { + resolve(dict->Dict.get("walletClickEvent")->Option.getOr(true->JSON.Encode.bool)) } } - addSmartEventListener("message", handleMessage, "onOneClickHandlerPaymentConfirm") + Window.addEventListener("message", handleMessage) handleOnConfirmPostMessage(~targetOrigin="*", ~isOneClick=true) } }) diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res index 58828faf..110353b7 100644 --- a/src/orca-loader/Elements.res +++ b/src/orca-loader/Elements.res @@ -296,35 +296,38 @@ let make = ( ("loader", loader), ("fonts", fonts), ]->getJsonFromArrayOfJson - let message = - [ - ( - "paymentElementCreate", - componentType->getIsComponentTypeForPaymentElementCreate->JSON.Encode.bool, - ), - ("otherElements", otherElements->JSON.Encode.bool), - ("options", newOptions), - ("componentType", componentType->JSON.Encode.string), - ("paymentOptions", widgetOptions), - ("iframeId", selectorString->JSON.Encode.string), - ("publishableKey", publishableKey->JSON.Encode.string), - ("endpoint", endpoint->JSON.Encode.string), - ("sdkSessionId", sdkSessionId->JSON.Encode.string), - ("blockConfirm", blockConfirm->JSON.Encode.bool), - ("customPodUri", customPodUri->JSON.Encode.string), - ("sdkHandleOneClickConfirmPayment", sdkHandleOneClickConfirmPayment->JSON.Encode.bool), - ("parentURL", "*"->JSON.Encode.string), - ("analyticsMetadata", analyticsMetadata), - ("launchTime", launchTime->JSON.Encode.float), - ("customBackendUrl", customBackendUrl->JSON.Encode.string), - ( - "onCompleteDoThisUsed", - EventListenerManager.eventListenerMap - ->Dict.get("onCompleteDoThis") - ->Option.isSome - ->JSON.Encode.bool, - ), - ]->Dict.fromArray + let message = [ + ( + "paymentElementCreate", + componentType->getIsComponentTypeForPaymentElementCreate->JSON.Encode.bool, + ), + ("otherElements", otherElements->JSON.Encode.bool), + ("options", newOptions), + ("componentType", componentType->JSON.Encode.string), + ("paymentOptions", widgetOptions), + ("iframeId", selectorString->JSON.Encode.string), + ("publishableKey", publishableKey->JSON.Encode.string), + ("endpoint", endpoint->JSON.Encode.string), + ("sdkSessionId", sdkSessionId->JSON.Encode.string), + ("blockConfirm", blockConfirm->JSON.Encode.bool), + ("customPodUri", customPodUri->JSON.Encode.string), + ("sdkHandleOneClickConfirmPayment", sdkHandleOneClickConfirmPayment->JSON.Encode.bool), + ("parentURL", "*"->JSON.Encode.string), + ("analyticsMetadata", analyticsMetadata), + ("launchTime", launchTime->JSON.Encode.float), + ("customBackendUrl", customBackendUrl->JSON.Encode.string), + ( + "isPaymentButtonHandlerProvided", + LoaderPaymentElement.isPaymentButtonHandlerProvided.contents->JSON.Encode.bool, + ), + ( + "onCompleteDoThisUsed", + EventListenerManager.eventListenerMap + ->Dict.get("onCompleteDoThis") + ->Option.isSome + ->JSON.Encode.bool, + ), + ]->Dict.fromArray let wallets = PaymentType.getWallets(newOptions->getDictFromJson, "wallets", logger) diff --git a/src/orca-loader/LoaderPaymentElement.res b/src/orca-loader/LoaderPaymentElement.res index a80176a4..a09d0e8d 100644 --- a/src/orca-loader/LoaderPaymentElement.res +++ b/src/orca-loader/LoaderPaymentElement.res @@ -6,6 +6,8 @@ open Identity @val @scope(("navigator", "clipboard")) external writeText: string => promise<'a> = "writeText" +let onCompleteDoThisUsed = ref(false) +let isPaymentButtonHandlerProvided = ref(false) let make = ( componentType, options, @@ -32,6 +34,48 @@ let make = ( true, ) + let asyncWrapper = async fn => { + try { + await fn() + } catch { + | err => Console.log2("Async function call failure", err) + } + } + + let currEventHandler = ref(Some(() => Promise.make((_, _) => {()}))) + let walletOneClickEventHandler = (event: Types.event) => { + let json = try { + event.data->anyTypeToJson + } catch { + | _ => JSON.Encode.null + } + + let dict = json->getDictFromJson + if dict->Dict.get("oneClickConfirmTriggered")->Option.isSome { + switch currEventHandler.contents { + | Some(eH) => + asyncWrapper(eH) + ->Promise.then(() => { + let msg = [("walletClickEvent", true->JSON.Encode.bool)]->Dict.fromArray + event.source->Window.sendPostMessage(msg) + Promise.resolve() + }) + ->ignore + + | None => () + } + } + } + + Window.addEventListener("message", walletOneClickEventHandler) + + let onSDKHandleClick = (eventHandler: option RescriptCore.Promise.t<'a>>) => { + currEventHandler := eventHandler + if eventHandler->Option.isSome { + isPaymentButtonHandlerProvided := true + } + } + let on = (eventType, eventHandler) => { switch eventType->eventTypeMapper { | Escape => @@ -368,6 +412,7 @@ let make = ( destroy, update, mount, + onSDKHandleClick, } } catch { | e => { diff --git a/src/orca-loader/Types.res b/src/orca-loader/Types.res index 74e8eb07..93261280 100644 --- a/src/orca-loader/Types.res +++ b/src/orca-loader/Types.res @@ -31,6 +31,7 @@ type paymentElement = { mount: string => unit, focus: unit => unit, clear: unit => unit, + onSDKHandleClick: option Promise.t> => unit, } type element = { @@ -107,6 +108,8 @@ let fetchUpdates = () => { setTimeout(() => resolve(Dict.make()->JSON.Encode.object), 1000)->ignore }) } + +let fnArgument = Some(() => Promise.make((_, _) => {()})) let defaultPaymentElement = { on: (_str, _func) => (), collapse: () => (), @@ -117,6 +120,7 @@ let defaultPaymentElement = { mount: _string => (), focus: () => (), clear: () => (), + onSDKHandleClick: fnArgument => (), } let create = (_componentType, _options) => {