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: add functionality for removing any beforeunload listeners once payment form is submitted #860

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions src/Hooks/UtilityHooks.res
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,29 @@ let useSendEventsToParent = eventsToSendToParent => {
})
}

let useUpdateShouldUseTopRedirection = () => {
let setShouldUseTopRedirection = Recoil.useSetRecoilState(RecoilAtoms.shouldUseTopRedirectionAtom)
let updateTopRedirectionAtom = paymentOptions => {
let useUpdateRedirectionFlags = () => {
let setRedirectionFlags = Recoil.useSetRecoilState(RecoilAtoms.redirectionFlagsAtom)
let updateRedirectionFlagsAtom = paymentOptions => {
paymentOptions
->Dict.get("shouldUseTopRedirection")
->Option.flatMap(JSON.Decode.bool)
->Option.map(useTop => {
setShouldUseTopRedirection(_ => useTop)
setRedirectionFlags(cv => {
shouldUseTopRedirection: useTop,
shouldRemoveBeforeUnloadEvents: cv.shouldRemoveBeforeUnloadEvents,
})
})
->ignore
paymentOptions
->Dict.get("shouldRemoveBeforeUnloadEvents")
->Option.flatMap(JSON.Decode.bool)
->Option.map(removeEvent => {
setRedirectionFlags(cv => {
shouldUseTopRedirection: cv.shouldUseTopRedirection,
shouldRemoveBeforeUnloadEvents: removeEvent,
})
})
->ignore
}
updateTopRedirectionAtom
updateRedirectionFlagsAtom
}
6 changes: 3 additions & 3 deletions src/LoaderController.res
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime
}
}

let updateShouldUseTopRedirection = UtilityHooks.useUpdateShouldUseTopRedirection()
let updateRedirectionFlags = UtilityHooks.useUpdateRedirectionFlags()

React.useEffect0(() => {
messageParentWindow([("iframeMounted", true->JSON.Encode.bool)])
Expand Down Expand Up @@ -281,7 +281,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime
logger.setClientSecret(clientSecret)

// Update top redirection atom
updateShouldUseTopRedirection(paymentOptions)
updateRedirectionFlags(paymentOptions)

switch getThemePromise(paymentOptions) {
| Some(promise) =>
Expand Down Expand Up @@ -331,7 +331,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime
logger.setClientSecret(clientSecret)

// Update top redirection atom
updateShouldUseTopRedirection(paymentOptions)
updateRedirectionFlags(paymentOptions)

switch getThemePromise(paymentOptions) {
| Some(promise) =>
Expand Down
30 changes: 30 additions & 0 deletions src/Types/RecoilAtomTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,33 @@ type paymentToken = {
paymentToken: string,
customerId: string,
}

type redirectionFlags = {
shouldUseTopRedirection: bool,
shouldRemoveBeforeUnloadEvents: bool,
}

let decodeRedirectionFlags = (
json: JSON.t,
defaultRedirectionFlags: redirectionFlags,
): redirectionFlags => {
json
->JSON.Decode.object
->Option.flatMap(obj => {
let shouldUseTopRedirection =
obj
->Dict.get("shouldUseTopRedirection")
->Option.flatMap(JSON.Decode.bool)
->Option.getOr(defaultRedirectionFlags.shouldUseTopRedirection)
let shouldRemoveBeforeUnloadEvents =
obj
->Dict.get("shouldRemoveBeforeUnloadEvents")
->Option.flatMap(JSON.Decode.bool)
->Option.getOr(defaultRedirectionFlags.shouldRemoveBeforeUnloadEvents)
Some({
shouldRemoveBeforeUnloadEvents,
shouldUseTopRedirection,
})
})
->Option.getOr(defaultRedirectionFlags)
}
30 changes: 14 additions & 16 deletions src/Utilities/PaymentHelpers.res
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ let rec intentCall = (
~isPaymentSession=false,
~isCallbackUsedVal=?,
~componentName="payment",
~shouldUseTopRedirection,
~redirectionFlags,
) => {
open Promise
let isConfirm = uri->String.includes("/confirm")
Expand Down Expand Up @@ -324,7 +324,7 @@ let rec intentCall = (
)
let handleOpenUrl = url => {
if isPaymentSession {
Window.replaceRootHref(url, shouldUseTopRedirection)
Utils.replaceRootHref(url, redirectionFlags)
} else {
openUrl(url)
}
Expand Down Expand Up @@ -451,7 +451,7 @@ let rec intentCall = (
~sdkHandleOneClickConfirmPayment,
~counter=counter + 1,
~componentName,
~shouldUseTopRedirection,
~redirectionFlags,
)
->then(
res => {
Expand Down Expand Up @@ -881,7 +881,7 @@ let rec intentCall = (
~counter=counter + 1,
~isPaymentSession,
~componentName,
~shouldUseTopRedirection,
~redirectionFlags,
)
->then(
res => {
Expand Down Expand Up @@ -912,7 +912,7 @@ let usePaymentSync = (optLogger: option<HyperLogger.loggerMake>, paymentType: pa
let keys = Recoil.useRecoilValueFromAtom(keys)
let isCallbackUsedVal = Recoil.useRecoilValueFromAtom(RecoilAtoms.isCompleteCallbackUsed)
let customPodUri = Recoil.useRecoilValueFromAtom(customPodUri)
let shouldUseTopRedirection = Recoil.useRecoilValueFromAtom(shouldUseTopRedirectionAtom)
let redirectionFlags = Recoil.useRecoilValueFromAtom(redirectionFlagsAtom)
let setIsManualRetryEnabled = Recoil.useSetRecoilState(isManualRetryEnabled)
(~handleUserError=false, ~confirmParam: ConfirmType.confirmParams, ~iframeId="") => {
switch keys.clientSecret {
Expand Down Expand Up @@ -940,7 +940,7 @@ let usePaymentSync = (optLogger: option<HyperLogger.loggerMake>, paymentType: pa
~sdkHandleOneClickConfirmPayment=keys.sdkHandleOneClickConfirmPayment,
~counter=0,
~isCallbackUsedVal,
~shouldUseTopRedirection,
~redirectionFlags,
)->ignore
}
switch paymentMethodList {
Expand Down Expand Up @@ -988,7 +988,7 @@ let usePaymentIntent = (optLogger, paymentType) => {
let paymentMethodList = Recoil.useRecoilValueFromAtom(paymentMethodList)
let keys = Recoil.useRecoilValueFromAtom(keys)
let isCallbackUsedVal = Recoil.useRecoilValueFromAtom(RecoilAtoms.isCompleteCallbackUsed)
let shouldUseTopRedirection = Recoil.useRecoilValueFromAtom(shouldUseTopRedirectionAtom)
let redirectionFlags = Recoil.useRecoilValueFromAtom(redirectionFlagsAtom)

let setIsManualRetryEnabled = Recoil.useSetRecoilState(isManualRetryEnabled)
(
Expand Down Expand Up @@ -1088,7 +1088,7 @@ let usePaymentIntent = (optLogger, paymentType) => {
~counter=0,
~isCallbackUsedVal,
~componentName,
~shouldUseTopRedirection,
~redirectionFlags,
)
->then(val => {
intentCallback(val)
Expand Down Expand Up @@ -1174,7 +1174,7 @@ let useCompleteAuthorize = (optLogger: option<HyperLogger.loggerMake>, paymentTy
let setIsManualRetryEnabled = Recoil.useSetRecoilState(isManualRetryEnabled)
let url = RescriptReactRouter.useUrl()
let isCallbackUsedVal = Recoil.useRecoilValueFromAtom(RecoilAtoms.isCompleteCallbackUsed)
let shouldUseTopRedirection = Recoil.useRecoilValueFromAtom(shouldUseTopRedirectionAtom)
let redirectionFlags = Recoil.useRecoilValueFromAtom(redirectionFlagsAtom)
let paymentTypeFromUrl =
CardUtils.getQueryParamsDictforKey(url.search, "componentName")->CardThemeType.getPaymentMode
(
Expand Down Expand Up @@ -1219,7 +1219,7 @@ let useCompleteAuthorize = (optLogger: option<HyperLogger.loggerMake>, paymentTy
~sdkHandleOneClickConfirmPayment=keys.sdkHandleOneClickConfirmPayment,
~counter=0,
~isCallbackUsedVal,
~shouldUseTopRedirection,
~redirectionFlags,
)->ignore
}
switch paymentMethodList {
Expand Down Expand Up @@ -1612,7 +1612,7 @@ let paymentIntentForPaymentSession = (
~clientSecret,
~logger,
~customPodUri,
~shouldUseTopRedirection,
~redirectionFlags,
) => {
let confirmParams =
payload
Expand Down Expand Up @@ -1669,7 +1669,7 @@ let paymentIntentForPaymentSession = (
~sdkHandleOneClickConfirmPayment=false,
~counter=0,
~isPaymentSession=true,
~shouldUseTopRedirection,
~redirectionFlags,
)
}

Expand Down Expand Up @@ -2106,9 +2106,7 @@ let usePostSessionTokens = (
let customPodUri = Recoil.useRecoilValueFromAtom(customPodUri)
let paymentMethodList = Recoil.useRecoilValueFromAtom(paymentMethodList)
let keys = Recoil.useRecoilValueFromAtom(keys)
let shouldUseTopRedirection = Recoil.useRecoilValueFromAtom(
RecoilAtoms.shouldUseTopRedirectionAtom,
)
let redirectionFlags = Recoil.useRecoilValueFromAtom(RecoilAtoms.redirectionFlagsAtom)

let setIsManualRetryEnabled = Recoil.useSetRecoilState(isManualRetryEnabled)
(
Expand Down Expand Up @@ -2200,7 +2198,7 @@ let usePostSessionTokens = (
~customPodUri,
~sdkHandleOneClickConfirmPayment=keys.sdkHandleOneClickConfirmPayment,
~counter=0,
~shouldUseTopRedirection,
~redirectionFlags
)
->then(val => {
intentCallback(val)
Expand Down
7 changes: 6 additions & 1 deletion src/Utilities/RecoilAtoms.res
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ let sessionId = Recoil.atom("sessionId", "")
let isConfirmBlocked = Recoil.atom("isConfirmBlocked", false)
let customPodUri = Recoil.atom("customPodUri", "")
let selectedOptionAtom = Recoil.atom("selectedOption", "")
let shouldUseTopRedirectionAtom = Recoil.atom("shouldUseTopRedirection", false)
let paymentTokenAtom = Recoil.atom(
"paymentToken",
{
Expand Down Expand Up @@ -100,3 +99,9 @@ let areOneClickWalletsRendered = Recoil.atom(
"areOneClickWalletsBtnRendered",
defaultAreOneClickWalletsRendered,
)

let defaultRedirectionFlags: redirectionFlags = {
shouldUseTopRedirection: false,
shouldRemoveBeforeUnloadEvents: false,
}
let redirectionFlagsAtom = Recoil.atom("redirectionFlags", defaultRedirectionFlags)
30 changes: 30 additions & 0 deletions src/Utilities/Utils.res
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ let handleOnConfirmPostMessage = (~targetOrigin="*", ~isOneClick=false) => {
let message = isOneClick ? "oneClickConfirmTriggered" : "confirmTriggered"
messageParentWindow([(message, true->JSON.Encode.bool)], ~targetOrigin)
}

let handleBeforeRedirectPostMessage = (~targetOrigin="*") => {
messageTopWindow([("disableBeforeUnloadEventListener", true->JSON.Encode.bool)], ~targetOrigin)
}

let getOptionString = (dict, key) => {
dict->Dict.get(key)->Option.flatMap(JSON.Decode.string)
}
Expand Down Expand Up @@ -1457,3 +1462,28 @@ let isDigitLimitExceeded = (val, ~digit) => {
| None => false
}
}

/* Redirect Handling */
let replaceRootHref = (href: string, redirectionFlags: RecoilAtomTypes.redirectionFlags) => {
if redirectionFlags.shouldRemoveBeforeUnloadEvents {
handleBeforeRedirectPostMessage()
}
switch redirectionFlags.shouldUseTopRedirection {
| true =>
try {
setTimeout(() => {
Window.Top.Location.replace(href)
}, 100)->ignore
} catch {
| e => {
Js.Console.error3(
"Failed to redirect root document",
e,
`Using [window.location.replace] for redirection`,
)
Window.Location.replace(href)
}
}
| false => Window.Location.replace(href)
}
}
20 changes: 0 additions & 20 deletions src/Window.res
Original file line number Diff line number Diff line change
Expand Up @@ -203,23 +203,3 @@ let getRootHostName = () =>
}
| false => Location.hostname
}

/* Redirect Handling */
let replaceRootHref = (href: string, shouldUseTopRedirection: bool) => {
switch shouldUseTopRedirection {
| true =>
try {
Top.Location.replace(href)
} catch {
| e => {
Js.Console.error3(
"Failed to redirect root document",
e,
`Using [window.location.replace] for redirection`,
)
Location.replace(href)
}
}
| false => Location.replace(href)
}
}
44 changes: 29 additions & 15 deletions src/hyper-loader/Elements.res
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ let make = (
~logger: option<HyperLogger.loggerMake>,
~analyticsMetadata,
~customBackendUrl,
~shouldUseTopRedirection,
~redirectionFlags: RecoilAtomTypes.redirectionFlags,
) => {
try {
let iframeRef = []
Expand Down Expand Up @@ -310,15 +310,29 @@ let make = (
) => {
open Promise

let widgetOptions =
[
("clientSecret", clientSecret->JSON.Encode.string),
("appearance", appearance),
("locale", locale),
("loader", loader),
("fonts", fonts),
("shouldUseTopRedirection", shouldUseTopRedirection->JSON.Encode.bool),
]->getJsonFromArrayOfJson
let widgetOptions = [
("clientSecret", clientSecret->JSON.Encode.string),
("appearance", appearance),
("locale", locale),
("loader", loader),
("fonts", fonts),
(
"redirectionFlags",
{
let dict = Dict.fromArray([
(
"shouldUseTopRedirection",
JSON.Encode.bool(redirectionFlags.shouldUseTopRedirection),
),
(
"shouldRemoveBeforeUnloadEvents",
JSON.Encode.bool(redirectionFlags.shouldRemoveBeforeUnloadEvents),
),
])
JSON.Encode.object(dict)
},
),
]->getJsonFromArrayOfJson
kashif-m marked this conversation as resolved.
Show resolved Hide resolved
let message = [
(
"paymentElementCreate",
Expand Down Expand Up @@ -675,7 +689,7 @@ let make = (
let returnUrl = dict->getString("return_url", "")
let redirectUrl = `${returnUrl}?payment_intent_client_secret=${clientSecret}&status=${status}`
if redirect.contents === "always" {
Window.replaceRootHref(redirectUrl, shouldUseTopRedirection)
Utils.replaceRootHref(redirectUrl, redirectionFlags)
resolve(JSON.Encode.null)
} else {
messageCurrentWindow([
Expand Down Expand Up @@ -703,7 +717,7 @@ let make = (

let handleErrorResponse = err => {
if redirect.contents === "always" {
Window.replaceRootHref(url, shouldUseTopRedirection)
Utils.replaceRootHref(url, redirectionFlags)
}
messageCurrentWindow([
("submitSuccessful", false->JSON.Encode.bool),
Expand Down Expand Up @@ -764,9 +778,9 @@ let make = (
->then(json => json->handleRetrievePaymentResponse)
->catch(err => {
if redirect.contents === "always" {
Window.replaceRootHref(
Utils.replaceRootHref(
redirectUrl->JSON.Decode.string->Option.getOr(""),
shouldUseTopRedirection,
redirectionFlags,
)
resolve(JSON.Encode.null)
} else {
Expand Down Expand Up @@ -1288,7 +1302,7 @@ let make = (
setElementIframeRef,
iframeRef,
mountPostMessage,
~shouldUseTopRedirection,
~redirectionFlags: RecoilAtomTypes.redirectionFlags,
)
savedPaymentElement->Dict.set(componentType, paymentElement)
paymentElement
Expand Down
Loading
Loading