Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions src/Types/HyperLoggerTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ type eventName =
| TEST_MODE
| PAYMENT_METHOD_ELIGIBILITY_CALL
| PAYMENT_METHOD_ELIGIBILITY_CALL_INIT
| DDC_FLOW

type maskableDetails = Email | CardDetails
type source = Loader | Elements(CardThemeType.mode) | Headless
Expand Down
36 changes: 28 additions & 8 deletions src/Types/PaymentConfirmTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,26 @@ let defaultBacsBankInstruction = {
}

type bankTransfer = {ach_credit_transfer: achCreditTransfer}
type redirectToUrl = {
returnUrl: string,
url: string,
}

type voucherDetails = {
download_url: string,
reference: string,
}

type ddcData = {
iframeUrl: string,
timeoutMs: int,
}

let defaultDdcData = {
iframeUrl: "",
timeoutMs: 30000,
}

type nextAction = {
redirectToUrl: string,
redirectMode: string,
postDdcRedirectUrl: string,
popupUrl: string,
redirectResponseUrl: string,
type_: string,
Expand All @@ -49,6 +57,7 @@ type nextAction = {
display_text: option<string>,
border_color: option<string>,
iframe_data: option<JSON.t>,
ddc_data: option<ddcData>,
}
type intent = {
nextAction: nextAction,
Expand All @@ -62,12 +71,10 @@ type intent = {
}
open Utils

let defaultRedirectTourl = {
returnUrl: "",
url: "",
}
let defaultNextAction = {
redirectToUrl: "",
redirectMode: "required",
postDdcRedirectUrl: "",
popupUrl: "",
redirectResponseUrl: "",
type_: "",
Expand All @@ -82,6 +89,7 @@ let defaultNextAction = {
display_text: None,
border_color: None,
iframe_data: None,
ddc_data: None,
}
let defaultIntent = {
nextAction: defaultNextAction,
Expand Down Expand Up @@ -139,13 +147,24 @@ let getVoucherDetails = json => {
}
}

let getDdcData = json => {
json
->getOptionalDict("ddc_data")
->Option.map(ddcDict => {
iframeUrl: ddcDict->getString("iframe_url", ""),
timeoutMs: ddcDict->getInt("timeout_ms", 30000),
})
}

let getNextAction = (dict, str) => {
dict
->Dict.get(str)
->Option.flatMap(JSON.Decode.object)
->Option.map(json => {
{
redirectToUrl: getString(json, "redirect_to_url", ""),
redirectMode: getString(json, "redirect_mode", "required"),
postDdcRedirectUrl: getString(json, "url", ""),
popupUrl: getString(json, "popup_url", ""),
redirectResponseUrl: getString(json, "redirect_response_url", ""),
type_: getString(json, "type", ""),
Expand Down Expand Up @@ -182,6 +201,7 @@ let getNextAction = (dict, str) => {
display_text: json->getOptionString("display_text"),
border_color: json->getOptionString("border_color"),
iframe_data: Some(json->Utils.getJsonObjectFromDict("iframe_data")),
ddc_data: json->getDdcData,
}
})
->Option.getOr(defaultNextAction)
Expand Down
3 changes: 2 additions & 1 deletion src/Utilities/LoggerUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ let apiEventInitMapper = (eventName: HyperLoggerTypes.eventName): option<
| AUTHENTICATED_SESSION_INITIATED
| ONE_CLICK_HANDLER_CALLBACK
| PAYMENT_ELEMENT_OPTIONS
| TEST_MODE =>
| TEST_MODE
| DDC_FLOW =>
None
}
147 changes: 147 additions & 0 deletions src/Utilities/NextActionHelpers.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
open Utils

let handleDDC = (
~ddcData: option<PaymentConfirmTypes.ddcData>,
~iframeId,
~isPaymentSession,
~resolve,
~data,
~optLogger,
~paymentMethod,
) => {
let {iframeUrl, timeoutMs} = ddcData->Option.getOr(PaymentConfirmTypes.defaultDdcData)

messageParentWindow([
("fullscreen", true->JSON.Encode.bool),
("param", "paymentloader"->JSON.Encode.string),
("iframeId", iframeId->JSON.Encode.string),
])

let errorType = "confirm_payment_failed"
let errorMessage = "Something went wrong"

let handleFailure = () => {
closePaymentLoaderIfAny()
if !isPaymentSession {
postFailedSubmitResponse(~errortype=errorType, ~message=errorMessage)
}
let failedSubmitResponse = getFailedSubmitResponse(~errorType, ~message=errorMessage)
resolve(failedSubmitResponse)
}

if iframeUrl === "" {
LoggerUtils.handleLogging(
~optLogger,
~eventName=DDC_FLOW,
~value="DDC failed: empty iframe URL",
~paymentMethod,
~logType=ERROR,
)
handleFailure()
} else {
let timeoutIdRef = ref(None)
let messageHandlerRef = ref(None)
let iframeRef = ref(None)

let cleanup = () => {
timeoutIdRef.contents->Option.forEach(clearTimeout)
messageHandlerRef.contents->Option.forEach(h => Window.removeEventListener("message", h))
iframeRef.contents->Option.forEach(Window.remove)
timeoutIdRef := None
messageHandlerRef := None
iframeRef := None
}

let handleRedirectToUrl = (redirectUrl, redirectMode) => {
closePaymentLoaderIfAny()
switch redirectMode {
| "if_required" =>
if !isPaymentSession {
messageParentWindow([("openurl_if_required", redirectUrl->JSON.Encode.string)])
} else {
resolve(data)
}
| _ => {
LoggerUtils.handleLogging(
~optLogger,
~eventName=REDIRECTING_USER,
~value="Post DDC redirection url : " ++ redirectUrl,
~paymentMethod,
~logType=INFO,
)
openUrl(redirectUrl)
}
}
}

let handleMessage = (ev: Window.event) => {
try {
let json = ev.data->Identity.anyTypeToJson
let dict = json->getDictFromJson

if dict->Dict.get("next_action")->Option.isSome {
let nextAction = PaymentConfirmTypes.getNextAction(dict, "next_action")
let nextActionType = nextAction.type_
let redirectUrl = nextAction.postDdcRedirectUrl
let redirectMode = nextAction.redirectMode
cleanup()
if nextActionType === "redirect_to_url" && redirectUrl !== "" {
LoggerUtils.handleLogging(
~optLogger,
~eventName=DDC_FLOW,
~value="DDC completed successfully",
~paymentMethod,
)
handleRedirectToUrl(redirectUrl, redirectMode)
} else {
LoggerUtils.handleLogging(
~optLogger,
~eventName=DDC_FLOW,
~value="DDC failed: invalid next action type - " ++ nextActionType,
~paymentMethod,
~logType=ERROR,
)
handleFailure()
}
}
} catch {
| exn =>
let err = exn->Identity.anyTypeToJson->JSON.stringify
LoggerUtils.handleLogging(
~optLogger,
~eventName=DDC_FLOW,
~value="DDC failed: message parse error - " ++ err,
~paymentMethod,
~logType=ERROR,
)
cleanup()
handleFailure()
}
}

messageHandlerRef := Some(handleMessage)
Window.addEventListener("message", handleMessage)

LoggerUtils.handleLogging(
~optLogger,
~eventName=DDC_FLOW,
~value="DDC initiated - iframe URL: " ++ iframeUrl,
~paymentMethod,
)

let iframe = Window.body->makeHiddenIframe(~src=iframeUrl, ~id="ddc-iframe")
iframeRef := Some(iframe)

timeoutIdRef := Some(setTimeout(() => {
LoggerUtils.handleLogging(
~optLogger,
~eventName=DDC_FLOW,
~value="DDC timed out",
~paymentMethod,
~logType=ERROR,
)
cleanup()
handleFailure()
}, timeoutMs))
}
}
12 changes: 10 additions & 2 deletions src/Utilities/PaymentHelpers.res
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ let getPaymentType = paymentMethodType =>
| _ => Other
}

let closePaymentLoaderIfAny = () => messageParentWindow([("fullscreen", false->JSON.Encode.bool)])

let retrievePaymentIntent = async (
clientSecret,
~headers=?,
Expand Down Expand Up @@ -759,6 +757,16 @@ let rec intentCall = (
("iframeId", iframeId->JSON.Encode.string),
("metadata", metaData->JSON.Encode.object),
])
} else if intent.nextAction.type_ === "invoke_ddc" {
NextActionHelpers.handleDDC(
~ddcData=intent.nextAction.ddc_data,
~iframeId,
~isPaymentSession,
~resolve,
~data,
~optLogger,
~paymentMethod,
)
} else if intent.nextAction.type_ === "display_voucher_information" {
let voucherData = intent.nextAction.voucher_details->Option.getOr({
download_url: "",
Expand Down
10 changes: 5 additions & 5 deletions src/Utilities/PaymentHelpersV2.res
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ let intentCall = (
let dict = data->getDictFromJson
let errorObj = PaymentError.itemToObjMapper(dict)
if !isPaymentSession {
PaymentHelpers.closePaymentLoaderIfAny()
closePaymentLoaderIfAny()
postFailedSubmitResponse(
~errortype=errorObj.error.type_,
~message=errorObj.error.message,
Expand All @@ -99,7 +99,7 @@ let intentCall = (
(resolve, _) => {
let _exceptionMessage = err->formatException
if !isPaymentSession {
PaymentHelpers.closePaymentLoaderIfAny()
closePaymentLoaderIfAny()
postFailedSubmitResponse(~errortype="server_error", ~message="Something went wrong")
}
if handleUserError {
Expand Down Expand Up @@ -140,7 +140,7 @@ let intentCall = (
if isCallbackUsedVal->Option.getOr(false) {
handleOnCompleteDoThisMessage()
} else {
PaymentHelpers.closePaymentLoaderIfAny()
closePaymentLoaderIfAny()
}

postSubmitResponse(~jsonData=data, ~url=url.href)
Expand All @@ -155,7 +155,7 @@ let intentCall = (
}
| _ =>
if isCallbackUsedVal->Option.getOr(false) {
PaymentHelpers.closePaymentLoaderIfAny()
closePaymentLoaderIfAny()
handleOnCompleteDoThisMessage()
} else {
handleOpenUrl(url.href)
Expand Down Expand Up @@ -240,7 +240,7 @@ let intentCall = (
let _exceptionMessage = err->formatException

if !isPaymentSession {
PaymentHelpers.closePaymentLoaderIfAny()
closePaymentLoaderIfAny()
postFailedSubmitResponse(~errortype="server_error", ~message="Something went wrong")
}
if handleUserError {
Expand Down
17 changes: 17 additions & 0 deletions src/Utilities/Utils.res
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ let getDictFromObj = (dict, key) => {
dict->Dict.get(key)->Option.flatMap(JSON.Decode.object)->Option.getOr(Dict.make())
}

let getOptionalDict = (dict, key) => {
dict->Dict.get(key)->Option.flatMap(JSON.Decode.object)
}

let getJsonObjectFromDict = (dict, key) => {
dict->Dict.get(key)->Option.getOr(JSON.Encode.object(Dict.make()))
}
Expand Down Expand Up @@ -1482,6 +1486,17 @@ let makeIframe = (element, url) => {
element->appendChild(iframe)
})
}
let makeHiddenIframe = (element, ~src, ~id) => {
let iframe = Window.createElement("iframe")
iframe->Window.setAttribute("id", id)
iframe->Window.setAttribute("src", src)
iframe->Window.setAttribute(
"style",
"position: absolute; width: 1px; height: 1px; border: none; overflow: hidden; left: -9999px; top: -9999px;",
)
element->Window.appendChild(iframe)
iframe
}
let makeForm = (element, url, id) => {
open Types
let form = createElement("form")
Expand Down Expand Up @@ -1695,6 +1710,8 @@ let handleFailureResponse = (~message, ~errorType) =>
),
]->getJsonFromArrayOfJson

let closePaymentLoaderIfAny = () => messageParentWindow([("fullscreen", false->JSON.Encode.bool)])

let getPaymentId = clientSecret =>
String.split(clientSecret, "_secret_")->Array.get(0)->Option.getOr("")

Expand Down
1 change: 1 addition & 0 deletions src/hyper-log-catcher/HyperLogger.res
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ let make = (~sessionId=?, ~source: source, ~clientSecret=?, ~merchantId=?, ~meta
APPLE_PAY_FLOW,
PLAID_SDK,
NETWORK_STATE,
DDC_FLOW,
]
arrayOfLogs
->Array.find(log => {
Expand Down
Loading