Skip to content
Merged
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/App.res
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ let make = () => {
</div>
| "qrData" => <QRCodeDisplay />
| "3dsAuth" => <ThreeDSAuth />
| "redsys3ds" => <Redsys3ds />
| "3ds" => <ThreeDSMethod />
| "voucherData" => <VoucherDisplay />
| "preMountLoader" => {
Expand Down
127 changes: 127 additions & 0 deletions src/Redsys3ds.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
open Utils

@react.component
let make = () => {
let logger = HyperLogger.make(~source=Elements(Payment))
let isCompleteAuthorizeCalledRef = React.useRef(false)
let timeoutRef = React.useRef(None)
let eventsToSendToParent = ["confirmParams", "poll_status", "openurl_if_required"]
let completeAuthorize = PaymentHelpers.useRedsysCompleteAuthorize(Some(logger))

let handleCompleteAuthorizeCall = (
threeDsMethodComp,
paymentIntentId,
publishableKey,
headers,
returnUrl,
) => {
let body = [
("client_secret", paymentIntentId->JSON.Encode.string),
("threeds_method_comp_ind", threeDsMethodComp->JSON.Encode.string),
]
completeAuthorize(
~bodyArr=body,
~confirmParam={
return_url: returnUrl,
publishableKey,
},
~headers,
~iframeId="redsys3ds",
~clientSecret=Some(paymentIntentId),
)
}

eventsToSendToParent->UtilityHooks.useSendEventsToParent

React.useEffect0(() => {
messageParentWindow([("iframeMountedCallback", true->JSON.Encode.bool)])
let handle = (ev: Window.event) => {
try {
let json = ev.data->safeParse
let dict = json->getDictFromJson
if dict->Dict.get("fullScreenIframeMounted")->Option.isSome {
let metadata = dict->getJsonObjectFromDict("metadata")
let metaDataDict = metadata->JSON.Decode.object->Option.getOr(Dict.make())
let paymentIntentId = metaDataDict->getString("paymentIntentId", "")
let publishableKey = metaDataDict->getString("publishableKey", "")

logger.setClientSecret(paymentIntentId)
logger.setMerchantId(publishableKey)

let headersDict = metaDataDict->getDictFromDict("headers")

let headers = headersDict->convertDictToArrayOfKeyStringTuples

let confirmParam = metaDataDict->getDictFromObj("confirmParams")
let returnUrl = confirmParam->getString("return_url", "")
let iframeDataDict = metaDataDict->getDictFromObj("iframeData")
let methodKey = iframeDataDict->getString("method_key", "threeDSMethodData")
let threeDsMethodUrl = iframeDataDict->getString("three_ds_method_url", "")
let threeDsMethodData = iframeDataDict->getString("three_ds_method_data", "")
let threeDsIframe = CommonHooks.querySelector("#threeDsAuthFrame")

switch Window.querySelector("#threeDsDiv")->Nullable.toOption {
| Some(elem) =>
if threeDsMethodUrl !== "" {
let form = elem->makeForm(threeDsMethodUrl, "threeDsHiddenPostMethod")
let input = Types.createElement("input")
input.name = encodeURIComponent(methodKey)
input.value = encodeURIComponent(threeDsMethodData)
form.target = "threeDsAuthFrame"
form.appendChild(input)
form.submit()
}
| None => ()
}

timeoutRef.current->Option.forEach(clearTimeout)

timeoutRef.current = Some(setTimeout(() => {
isCompleteAuthorizeCalledRef.current = true
handleCompleteAuthorizeCall(
"N",
paymentIntentId,
publishableKey,
headers,
returnUrl,
)->ignore
}, 10000))

switch threeDsIframe->Nullable.toOption {
| Some(elem) =>
elem->CommonHooks.addEventListener("load", _ => {
timeoutRef.current->Option.forEach(clearTimeout)
if !isCompleteAuthorizeCalledRef.current {
handleCompleteAuthorizeCall(
"Y",
paymentIntentId,
publishableKey,
headers,
returnUrl,
)->ignore
}
})
| None => ()
}
}
} catch {
| _ =>
postFailedSubmitResponse(
~errortype="complete_authorize_failed",
~message="Something went wrong.",
)
}
}
Window.addEventListener("message", handle)
Some(
() => {
Window.removeEventListener("message", handle)
timeoutRef.current->Option.forEach(clearTimeout)
},
)
})

<div id="threeDsDiv" className="max-w-1 max-h-1 opacity-0 fixed left-[-9999px]">
<iframe id="threeDsAuthFrame" name="threeDsAuthFrame" title="3D Secure Authentication Frame" />
</div>
}
8 changes: 1 addition & 7 deletions src/ThreeDSAuth.res
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,7 @@ let make = () => {
->JSON.Decode.object
->Option.getOr(Dict.make())
->getString("three_ds_authorize_url", "")
let headers =
headersDict
->Dict.toArray
->Array.map(entries => {
let (x, val) = entries
(x, val->JSON.Decode.string->Option.getOr(""))
})
let headers = headersDict->convertDictToArrayOfKeyStringTuples

let threeDsMethodComp = metaDataDict->getString("3dsMethodComp", "U")
open Promise
Expand Down
3 changes: 3 additions & 0 deletions src/Types/PaymentConfirmTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type nextAction = {
next_action_data: option<JSON.t>,
display_text: option<string>,
border_color: option<string>,
iframe_data: option<JSON.t>,
}
type intent = {
nextAction: nextAction,
Expand Down Expand Up @@ -74,6 +75,7 @@ let defaultNextAction = {
next_action_data: None,
display_text: None,
border_color: None,
iframe_data: None,
}
let defaultIntent = {
nextAction: defaultNextAction,
Expand Down Expand Up @@ -170,6 +172,7 @@ let getNextAction = (dict, str) => {
next_action_data: Some(json->getDictFromDict("next_action_data")->JSON.Encode.object),
display_text: json->getOptionString("display_text"),
border_color: json->getOptionString("border_color"),
iframe_data: Some(json->Utils.getJsonObjectFromDict("iframe_data")),
}
})
->Option.getOr(defaultNextAction)
Expand Down
Loading