From 4d2ac428d50ac5f511f613738c8ad62a76d522a0 Mon Sep 17 00:00:00 2001 From: Vrishab Srivatsa Date: Mon, 26 Feb 2024 18:51:04 +0530 Subject: [PATCH] feat: HS-1111: Moved HTTP requests to preMountLoader iframe" --- ...perswitch_web_aws_production_deployment.sh | 6 +- aws/hyperswitch_web_aws_setup.sh | 12 +- src/App.res | 11 +- src/Payments/PreMountLoader.res | 173 ++-- src/Utilities/ApiEndpoint.res | 2 +- src/orca-loader/Elements.res | 872 ++++++++++-------- webpack.common.js | 36 +- webpack.dev.js | 22 +- 8 files changed, 614 insertions(+), 520 deletions(-) diff --git a/aws/hyperswitch_web_aws_production_deployment.sh b/aws/hyperswitch_web_aws_production_deployment.sh index 0f3fdd9a6..62c9ed923 100755 --- a/aws/hyperswitch_web_aws_production_deployment.sh +++ b/aws/hyperswitch_web_aws_production_deployment.sh @@ -76,12 +76,12 @@ echo $( (aws s3api put-bucket-policy --bucket $MY_AWS_S3_BUCKET_NAME --policy "$ echo "Bucket configuration updated" -echo "Enter the backend endpoint your Hyperswitch Client will hit (hosted Hyperswitch Backend, https://sandbox.hyperswitch.io is taken by default):" +echo "Enter the backend endpoint your Hyperswitch Client will hit (hosted Hyperswitch Backend, https://beta.hyperswitch.io/api is taken by default):" read AWS_BACKEND_URL { log }) - React.useEffect1(()=>{ - setLoggerState(._=>logger) + React.useEffect1(() => { + setLoggerState(._ => logger) None - },[logger]) + }, [logger]) let renderFullscreen = { switch fullscreenMode { | "paymentloader" => - | "fullscreen" =>
+ | "fullscreen" => +
+ +
| "qrData" => | "preMountLoader" => { let clientSecret = CardUtils.getQueryParamsDictforKey(url.search, "clientSecret") diff --git a/src/Payments/PreMountLoader.res b/src/Payments/PreMountLoader.res index c93f38d4a..717f336ac 100644 --- a/src/Payments/PreMountLoader.res +++ b/src/Payments/PreMountLoader.res @@ -1,83 +1,106 @@ @react.component let make = (~sessionId, ~publishableKey, ~clientSecret, ~endpoint) => { - // open Utils - // let logger = OrcaLogger.make( - // ~sessionId, - // ~source=Loader, - // ~merchantId=publishableKey, - // ~clientSecret, - // (), - // ) + open Utils + let (paymentMethodsResponseSent, setPaymentMethodsResponseSent) = React.useState(_ => false) + let ( + customerPaymentMethodsResponseSent, + setCustomerPaymentMethodsResponseSent, + ) = React.useState(_ => false) + let (sessionTokensResponseSent, setSessionTokensResponseSent) = React.useState(_ => false) + let logger = OrcaLogger.make( + ~sessionId, + ~source=Loader, + ~merchantId=publishableKey, + ~clientSecret, + (), + ) - // // let ( - // // paymentMethodsResponse, - // // customerPaymentMethodsResponse, - // // sessionTokensResponse, - // // ) = React.useMemo0(() => { - // // ( - // // PaymentHelpers.usePaymentMethodList( - // // ~clientSecret, - // // ~publishableKey, - // // ~logger, - // // ~switchToCustomPod=false, - // // ~endpoint, - // // ), - // // PaymentHelpers.useCustomerDetails( - // // ~clientSecret, - // // ~publishableKey, - // // ~optLogger=Some(logger), - // // ~switchToCustomPod=false, - // // ~endpoint, - // // ), - // // PaymentHelpers.useSessions( - // // ~clientSecret, - // // ~publishableKey, - // // ~optLogger=Some(logger), - // // ~switchToCustomPod=false, - // // ~endpoint, - // // (), - // // ), - // // ) - // // }) + let ( + paymentMethodsResponse, + customerPaymentMethodsResponse, + sessionTokensResponse, + ) = React.useMemo0(() => { + ( + PaymentHelpers.usePaymentMethodList( + ~clientSecret, + ~publishableKey, + ~logger, + ~switchToCustomPod=false, + ~endpoint, + ), + PaymentHelpers.useCustomerDetails( + ~clientSecret, + ~publishableKey, + ~optLogger=Some(logger), + ~switchToCustomPod=false, + ~endpoint, + ), + PaymentHelpers.useSessions( + ~clientSecret, + ~publishableKey, + ~optLogger=Some(logger), + ~switchToCustomPod=false, + ~endpoint, + (), + ), + ) + }) - // // let sendPromiseData = (promise, key) => { - // // open Promise - // // promise - // // ->then(res => { - // // handlePostMessage([("response", res), ("data", key->Js.Json.string)]) - // // resolve() - // // }) - // // ->catch(_err => { - // // handlePostMessage([("response", Js.Json.null), ("data", key->Js.Json.string)]) - // // resolve() - // // }) - // // ->ignore - // // } + let sendPromiseData = (promise, key) => { + open Promise + promise + ->then(res => { + handlePostMessage([("response", res), ("data", key->Js.Json.string)]) + resolve() + }) + ->catch(_err => { + handlePostMessage([("response", Js.Json.null), ("data", key->Js.Json.string)]) + resolve() + }) + ->ignore + } - // // React.useEffect0(() => { - // // let handle = (ev: Window.event) => { - // // let json = try { - // // ev.data->Js.Json.parseExn - // // } catch { - // // | _ => Js.Json.null - // // } - // // let dict = json->Utils.getDictFromJson - // // if dict->Js.Dict.get("sendPaymentMethodsResponse")->Belt.Option.isSome { - // // paymentMethodsResponse->sendPromiseData("payment_methods") - // // } else if dict->Js.Dict.get("sendCustomerPaymentMethodsResponse")->Belt.Option.isSome { - // // customerPaymentMethodsResponse->sendPromiseData("customer_payment_methods") - // // } else if dict->Js.Dict.get("sendSessionTokensResponse")->Belt.Option.isSome { - // // sessionTokensResponse->sendPromiseData("session_tokens") - // // } - // // } - // // Window.addEventListener("message", handle) - // // handlePostMessage([("preMountLoaderInitCallback", true->Js.Json.boolean)]) - // // Some( - // // () => { - // // Window.removeEventListener("message", handle) - // // }, - // // ) - // // }) + let handle = (ev: Window.event) => { + let json = try { + ev.data->Js.Json.parseExn + } catch { + | _ => Js.Json.null + } + Js.log("preMountHandlerEventHandler") + let dict = json->Utils.getDictFromJson + if dict->Js.Dict.get("sendPaymentMethodsResponse")->Belt.Option.isSome { + paymentMethodsResponse->sendPromiseData("payment_methods") + setPaymentMethodsResponseSent(_ => true) + } else if dict->Js.Dict.get("sendCustomerPaymentMethodsResponse")->Belt.Option.isSome { + if dict->Utils.getBool("sendCustomerPaymentMethodsResponse", false) { + customerPaymentMethodsResponse->sendPromiseData("customer_payment_methods") + } + setCustomerPaymentMethodsResponseSent(_ => true) + } else if dict->Js.Dict.get("sendSessionTokensResponse")->Belt.Option.isSome { + sessionTokensResponse->sendPromiseData("session_tokens") + setSessionTokensResponseSent(_ => true) + } + } + + React.useEffect0(() => { + Window.addEventListener("message", handle) + handlePostMessage([("preMountLoaderIframeMountedCallback", true->Js.Json.boolean)]) + Some( + () => { + Window.removeEventListener("message", handle) + }, + ) + }) + + React.useEffect3(() => { + if ( + paymentMethodsResponseSent && customerPaymentMethodsResponseSent && sessionTokensResponseSent + ) { + Js.log("Removing event listener") + Window.removeEventListener("message", handle) + } + None + }, (paymentMethodsResponseSent, customerPaymentMethodsResponseSent, sessionTokensResponseSent)) React.null } diff --git a/src/Utilities/ApiEndpoint.res b/src/Utilities/ApiEndpoint.res index 313823959..5900e0b19 100644 --- a/src/Utilities/ApiEndpoint.res +++ b/src/Utilities/ApiEndpoint.res @@ -15,7 +15,7 @@ let getApiEndPoint = (~publishableKey="", ~isConfirmCall=false, ()) => { | None => let backendEndPoint = isConfirmCall ? GlobalVars.confirmEndPoint : GlobalVars.backendEndPoint if GlobalVars.isProd { - testMode ? "https://sandbox.hyperswitch.io" : backendEndPoint + testMode ? "https://beta.hyperswitch.io/api" : backendEndPoint } else { backendEndPoint } diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res index 3bdcfeccd..2be4a3774 100644 --- a/src/orca-loader/Elements.res +++ b/src/orca-loader/Elements.res @@ -60,97 +60,121 @@ let make = ( ->Belt.Option.flatMap(Js.Json.decodeBoolean) ->Belt.Option.getWithDefault(false) - let paymentMethodListPromise = PaymentHelpers.usePaymentMethodList( - ~clientSecret, - ~publishableKey, - ~endpoint, - ~switchToCustomPod, - ~logger, - ) - - let sessionsPromise = PaymentHelpers.useSessions( - ~clientSecret, - ~publishableKey, - ~endpoint, - ~switchToCustomPod, - ~optLogger=Some(logger), - (), - ) + let localSelectorString = "hyper-preMountLoader-iframe" + let mountPreMountLoaderIframe = () => { + let componentType = "preMountLoader" + let iframeDivHtml = `` + let iframeDiv = Window.createElement("div") + iframeDiv->Window.innerHTML(iframeDivHtml) + Window.body->Window.appendChild(iframeDiv) + + let elem = Window.querySelector(`#orca-payment-element-iframeRef-${localSelectorString}`) + elem + } let locale = localOptions->getJsonStringFromDict("locale", "") let loader = localOptions->getJsonStringFromDict("loader", "") let clientSecret = localOptions->getRequiredString("clientSecret", "", ~logger) let clientSecretReMatch = Js.Re.test_(`.+_secret_[A-Za-z0-9]+`->Js.Re.fromString, clientSecret) + + let preMountLoaderMountedPromise = Js.Promise.make((~resolve, ~reject as _) => { + let preMountLoaderIframeCallback = (ev: Types.event) => { + let json = ev.data->eventToJson + let dict = json->Utils.getDictFromJson + if dict->Js.Dict.get("preMountLoaderIframeMountedCallback")->Belt.Option.isSome { + Js.log("preMountLoaderIframeMountedCallback") + resolve(. true->Js.Json.boolean) + } + } + addSmartEventListener( + "message", + preMountLoaderIframeCallback, + "onPreMountLoaderIframeCallback", + ) + }) + + let preMountLoaderIframeDiv = mountPreMountLoaderIframe() + let fetchPaymentsList = mountedIframeRef => { - open Promise - paymentMethodListPromise - ->then(json => { - let isApplePayPresent = - PaymentMethodsRecord.getPaymentMethodTypeFromList( - ~list=json->Utils.getDictFromJson->PaymentMethodsRecord.itemToObjMapper, - ~paymentMethod="wallet", - ~paymentMethodType="apple_pay", - )->Belt.Option.isSome - - let isGooglePayPresent = - PaymentMethodsRecord.getPaymentMethodTypeFromList( - ~list=json->Utils.getDictFromJson->PaymentMethodsRecord.itemToObjMapper, - ~paymentMethod="wallet", - ~paymentMethodType="google_pay", - )->Belt.Option.isSome - - if isApplePayPresent || isGooglePayPresent { - if ( - Window.querySelectorAll(`script[src="https://tpgw.trustpay.eu/js/v1.js"]`)->Js.Array2.length === 0 && - Window.querySelectorAll(`script[src="https://test-tpgw.trustpay.eu/js/v1.js"]`)->Js.Array2.length === 0 - ) { - let trustPayScriptURL = - publishableKey->Js.String2.startsWith("pk_prd_") - ? "https://tpgw.trustpay.eu/js/v1.js" - : "https://test-tpgw.trustpay.eu/js/v1.js" - let trustPayScript = Window.createElement("script") - trustPayScript->Window.elementSrc(trustPayScriptURL) - trustPayScript->Window.elementOnerror(err => { - Utils.logInfo(Js.log2("ERROR DURING LOADING TRUSTPAY APPLE PAY", err)) - }) - Window.body->Window.appendChild(trustPayScript) - logger.setLogInfo(~value="TrustPay Script Loaded", ~eventName=TRUSTPAY_SCRIPT, ()) + let handlePaymentMethodsLoaded = (event: Types.event) => { + let json = event.data->eventToJson + let dict = json->getDictFromJson + let isPaymentMethodsData = dict->Utils.getString("data", "") === "payment_methods" + if isPaymentMethodsData { + let json = dict->Utils.getJsonFromDict("response", Js.Json.null) + let isApplePayPresent = + PaymentMethodsRecord.getPaymentMethodTypeFromList( + ~list=json->Utils.getDictFromJson->PaymentMethodsRecord.itemToObjMapper, + ~paymentMethod="wallet", + ~paymentMethodType="apple_pay", + )->Belt.Option.isSome + + let isGooglePayPresent = + PaymentMethodsRecord.getPaymentMethodTypeFromList( + ~list=json->Utils.getDictFromJson->PaymentMethodsRecord.itemToObjMapper, + ~paymentMethod="wallet", + ~paymentMethodType="google_pay", + )->Belt.Option.isSome + + if isApplePayPresent || isGooglePayPresent { + if ( + Window.querySelectorAll(`script[src="https://tpgw.trustpay.eu/js/v1.js"]`)->Js.Array2.length === 0 && + Window.querySelectorAll(`script[src="https://test-tpgw.trustpay.eu/js/v1.js"]`)->Js.Array2.length === 0 + ) { + let trustPayScriptURL = + publishableKey->Js.String2.startsWith("pk_prd_") + ? "https://tpgw.trustpay.eu/js/v1.js" + : "https://test-tpgw.trustpay.eu/js/v1.js" + let trustPayScript = Window.createElement("script") + trustPayScript->Window.elementSrc(trustPayScriptURL) + trustPayScript->Window.elementOnerror(err => { + Utils.logInfo(Js.log2("ERROR DURING LOADING TRUSTPAY APPLE PAY", err)) + }) + Window.body->Window.appendChild(trustPayScript) + logger.setLogInfo(~value="TrustPay Script Loaded", ~eventName=TRUSTPAY_SCRIPT, ()) + } } + let msg = [("paymentMethodList", json)]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) } - - // Js.Global.setTimeout(() => { - let msg = [("paymentMethodList", json)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - // }, 5000)->ignore - json->resolve - }) - ->ignore + } + let msg = [("sendPaymentMethodsResponse", true->Js.Json.boolean)]->Js.Dict.fromArray + addSmartEventListener("message", handlePaymentMethodsLoaded, "onPaymentMethodsLoaded") + preMountLoaderIframeDiv->Window.iframePostMessage(msg) } - let fetchCustomerDetails = mountedIframeRef => { - let customerDetailsPromise = PaymentHelpers.useCustomerDetails( - ~clientSecret, - ~publishableKey, - ~endpoint, - ~switchToCustomPod, - ~optLogger=Some(logger), - ) - open Promise - customerDetailsPromise - ->then(json => { - // Js.Global.setTimeout(() => { - let msg = [("customerPaymentMethods", json)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - // }, 5000)->ignore - json->resolve - }) - ->catch(_err => { - let dict = - [("customer_payment_methods", []->Js.Json.array)]->Js.Dict.fromArray->Js.Json.object_ - let msg = [("customerPaymentMethods", dict)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - resolve(msg->Js.Json.object_) - }) - ->ignore + let fetchCustomerPaymentMethods = (mountedIframeRef, disableSaveCards) => { + if !disableSaveCards { + let handleCustomerPaymentMethodsLoaded = (event: Types.event) => { + let json = event.data->eventToJson + let dict = json->getDictFromJson + let isCustomerPaymentMethodsData = + dict->Utils.getString("data", "") === "customer_payment_methods" + if isCustomerPaymentMethodsData { + let json = dict->Utils.getJsonFromDict("response", Js.Json.null) + let msg = [("customerPaymentMethods", json)]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + } + } + addSmartEventListener( + "message", + handleCustomerPaymentMethodsLoaded, + "onCustomerPaymentMethodsLoaded", + ) + } + let msg = + [ + ("sendCustomerPaymentMethodsResponse", !disableSaveCards->Js.Json.boolean), + ]->Js.Dict.fromArray + preMountLoaderIframeDiv->Window.iframePostMessage(msg) } !clientSecretReMatch @@ -357,361 +381,393 @@ let make = ( addSmartEventListener("message", handleApplePayMounted, "onApplePayMount") addSmartEventListener("message", handleGooglePayThirdPartyFlow, "onGooglePayThirdParty") + Window.removeEventListener("message", handleApplePayMessages.contents) - sessionsPromise - ->then(json => { - let sessionsArr = - json - ->Js.Json.decodeObject - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->SessionsType.getSessionsTokenJson("session_token") - - let applePayPresent = sessionsArr->Js.Array2.find(item => { - let x = - item - ->Js.Json.decodeObject - ->Belt.Option.flatMap( - x => { - x->Js.Dict.get("wallet_name") - }, - ) - ->Belt.Option.flatMap(Js.Json.decodeString) - ->Belt.Option.getWithDefault("") - x === "apple_pay" || x === "applepay" - }) - if !(applePayPresent->Belt.Option.isSome) { - let msg = [("applePaySessionObjNotPresent", true->Js.Json.boolean)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - } - let googlePayPresent = sessionsArr->Js.Array2.find(item => { - let x = - item - ->Js.Json.decodeObject - ->Belt.Option.flatMap( - x => { - x->Js.Dict.get("wallet_name") - }, - ) - ->Belt.Option.flatMap(Js.Json.decodeString) - ->Belt.Option.getWithDefault("") - x === "google_pay" || x === "googlepay" - }) + let fetchSessionTokens = mountedIframeRef => { + let handleSessionTokensLoaded = (event: Types.event) => { + let json = event.data->eventToJson + let dict = json->getDictFromJson + let sessionTokensData = dict->Utils.getString("data", "") === "session_tokens" + if sessionTokensData { + let json = dict->Utils.getJsonFromDict("response", Js.Json.null) - (json, applePayPresent, googlePayPresent)->resolve - }) - ->then(res => { - let (json, applePayPresent, googlePayPresent) = res - if componentType === "payment" && applePayPresent->Belt.Option.isSome { - //do operations here - let processPayment = (token: Js.Json.t) => { - //let body = PaymentBody.applePayBody(~token) - let msg = [("applePayProcessPayment", token)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - } + { + let sessionsArr = + json + ->Js.Json.decodeObject + ->Belt.Option.getWithDefault(Js.Dict.empty()) + ->SessionsType.getSessionsTokenJson("session_token") + + let applePayPresent = sessionsArr->Js.Array2.find(item => { + let x = + item + ->Js.Json.decodeObject + ->Belt.Option.flatMap(x => { + x->Js.Dict.get("wallet_name") + }) + ->Belt.Option.flatMap(Js.Json.decodeString) + ->Belt.Option.getWithDefault("") + x === "apple_pay" || x === "applepay" + }) + if !(applePayPresent->Belt.Option.isSome) { + let msg = + [("applePaySessionObjNotPresent", true->Js.Json.boolean)]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + } + let googlePayPresent = sessionsArr->Js.Array2.find(item => { + let x = + item + ->Js.Json.decodeObject + ->Belt.Option.flatMap(x => { + x->Js.Dict.get("wallet_name") + }) + ->Belt.Option.flatMap(Js.Json.decodeString) + ->Belt.Option.getWithDefault("") + x === "google_pay" || x === "googlepay" + }) - handleApplePayMessages := - ( - (event: Types.event) => { - let json = event.data->eventToJson - let dict = json->getDictFromJson - switch dict->Js.Dict.get("applePayButtonClicked") { - | Some(val) => - if val->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) { - let isDelayedSessionToken = - applePayPresent - ->Belt.Option.flatMap(Js.Json.decodeObject) - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->Js.Dict.get("delayed_session_token") - ->Belt.Option.getWithDefault(Js.Json.null) - ->Js.Json.decodeBoolean - ->Belt.Option.getWithDefault(false) - - if isDelayedSessionToken { - logger.setLogInfo( - ~value="Delayed Session Token Flow", - ~eventName=APPLE_PAY_FLOW, - ~paymentMethod="APPLE_PAY", - (), - ) - - let applePayPresent = - dict - ->Js.Dict.get("applePayPresent") - ->Belt.Option.flatMap(Js.Json.decodeObject) - ->Belt.Option.getWithDefault(Js.Dict.empty()) - - let connector = - applePayPresent - ->Js.Dict.get("connector") - ->Belt.Option.getWithDefault(Js.Json.null) - ->Js.Json.decodeString - ->Belt.Option.getWithDefault("") - - switch connector { - | "trustpay" => - logger.setLogInfo( - ~value="TrustPay Connector Flow", - ~eventName=APPLE_PAY_FLOW, - ~paymentMethod="APPLE_PAY", - (), - ) - let secrets = - applePayPresent - ->Js.Dict.get("session_token_data") - ->Belt.Option.getWithDefault(Js.Json.null) - ->Js.Json.decodeObject - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->Js.Dict.get("secrets") - ->Belt.Option.getWithDefault(Js.Json.null) - - let paymentRequest = - applePayPresent - ->Js.Dict.get("payment_request_data") - ->Belt.Option.flatMap(Js.Json.decodeObject) - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->ApplePayTypes.jsonToPaymentRequestDataType - - let payment = - secrets - ->Js.Json.decodeObject - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->Js.Dict.get("payment") - ->Belt.Option.getWithDefault(Js.Json.null) - ->Js.Json.decodeString - ->Belt.Option.getWithDefault("") - - try { - let trustpay = trustPayApi(secrets) - trustpay.finishApplePaymentV2(. payment, paymentRequest) - ->then(res => { + (json, applePayPresent, googlePayPresent)->resolve + } + ->then(res => { + let (json, applePayPresent, googlePayPresent) = res + if componentType === "payment" && applePayPresent->Belt.Option.isSome { + //do operations here + + let processPayment = (token: Js.Json.t) => { + //let body = PaymentBody.applePayBody(~token) + let msg = [("applePayProcessPayment", token)]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + } + + handleApplePayMessages := + ( + (event: Types.event) => { + let json = event.data->eventToJson + let dict = json->getDictFromJson + + switch dict->Js.Dict.get("applePayButtonClicked") { + | Some(val) => + if val->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) { + let isDelayedSessionToken = + applePayPresent + ->Belt.Option.flatMap(Js.Json.decodeObject) + ->Belt.Option.getWithDefault(Js.Dict.empty()) + ->Js.Dict.get("delayed_session_token") + ->Belt.Option.getWithDefault(Js.Json.null) + ->Js.Json.decodeBoolean + ->Belt.Option.getWithDefault(false) + + if isDelayedSessionToken { logger.setLogInfo( - ~value="TrustPay ApplePay Success Response", - ~internalMetadata=res->Js.Json.stringify, + ~value="Delayed Session Token Flow", ~eventName=APPLE_PAY_FLOW, ~paymentMethod="APPLE_PAY", (), ) - let msg = - [("applePaySyncPayment", true->Js.Json.boolean)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - logger.setLogInfo( - ~value="", - ~eventName=PAYMENT_DATA_FILLED, - ~paymentMethod="APPLE_PAY", - (), - ) - resolve() - }) - ->catch(err => { - let exceptionMessage = err->Utils.formatException->Js.Json.stringify + + let applePayPresent = + dict + ->Js.Dict.get("applePayPresent") + ->Belt.Option.flatMap(Js.Json.decodeObject) + ->Belt.Option.getWithDefault(Js.Dict.empty()) + + let connector = + applePayPresent + ->Js.Dict.get("connector") + ->Belt.Option.getWithDefault(Js.Json.null) + ->Js.Json.decodeString + ->Belt.Option.getWithDefault("") + + switch connector { + | "trustpay" => + logger.setLogInfo( + ~value="TrustPay Connector Flow", + ~eventName=APPLE_PAY_FLOW, + ~paymentMethod="APPLE_PAY", + (), + ) + let secrets = + applePayPresent + ->Js.Dict.get("session_token_data") + ->Belt.Option.getWithDefault(Js.Json.null) + ->Js.Json.decodeObject + ->Belt.Option.getWithDefault(Js.Dict.empty()) + ->Js.Dict.get("secrets") + ->Belt.Option.getWithDefault(Js.Json.null) + + let paymentRequest = + applePayPresent + ->Js.Dict.get("payment_request_data") + ->Belt.Option.flatMap(Js.Json.decodeObject) + ->Belt.Option.getWithDefault(Js.Dict.empty()) + ->ApplePayTypes.jsonToPaymentRequestDataType + + let payment = + secrets + ->Js.Json.decodeObject + ->Belt.Option.getWithDefault(Js.Dict.empty()) + ->Js.Dict.get("payment") + ->Belt.Option.getWithDefault(Js.Json.null) + ->Js.Json.decodeString + ->Belt.Option.getWithDefault("") + + try { + let trustpay = trustPayApi(secrets) + trustpay.finishApplePaymentV2(. payment, paymentRequest) + ->then(res => { + logger.setLogInfo( + ~value="TrustPay ApplePay Success Response", + ~internalMetadata=res->Js.Json.stringify, + ~eventName=APPLE_PAY_FLOW, + ~paymentMethod="APPLE_PAY", + (), + ) + let msg = + [ + ("applePaySyncPayment", true->Js.Json.boolean), + ]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + logger.setLogInfo( + ~value="", + ~eventName=PAYMENT_DATA_FILLED, + ~paymentMethod="APPLE_PAY", + (), + ) + resolve() + }) + ->catch(err => { + let exceptionMessage = + err->Utils.formatException->Js.Json.stringify + logger.setLogInfo( + ~eventName=APPLE_PAY_FLOW, + ~paymentMethod="APPLE_PAY", + ~value=exceptionMessage, + (), + ) + let msg = + [ + ("applePaySyncPayment", true->Js.Json.boolean), + ]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + resolve() + }) + ->ignore + } catch { + | exn => { + logger.setLogInfo( + ~value=exn->Utils.formatException->Js.Json.stringify, + ~eventName=APPLE_PAY_FLOW, + ~paymentMethod="APPLE_PAY", + (), + ) + let msg = + [ + ("applePaySyncPayment", true->Js.Json.boolean), + ]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + } + } + | _ => () + } + } else { + let paymentRequest = + applePayPresent + ->Belt.Option.flatMap(Js.Json.decodeObject) + ->Belt.Option.getWithDefault(Js.Dict.empty()) + ->Js.Dict.get("payment_request_data") + ->Belt.Option.getWithDefault(Js.Dict.empty()->Js.Json.object_) + ->Utils.transformKeys(Utils.CamelCase) + + let ssn = applePaySession(3, paymentRequest) + switch applePaySessionRef.contents->Js.Nullable.toOption { + | Some(session) => + try { + session.abort(.) + } catch { + | error => Js.log2("Abort fail", error) + } + | None => () + } + + ssn.begin(.) + applePaySessionRef := ssn->Js.Nullable.return + + ssn.onvalidatemerchant = _event => { + let merchantSession = + applePayPresent + ->Belt.Option.flatMap(Js.Json.decodeObject) + ->Belt.Option.getWithDefault(Js.Dict.empty()) + ->Js.Dict.get("session_token_data") + ->Belt.Option.getWithDefault(Js.Dict.empty()->Js.Json.object_) + ->Utils.transformKeys(Utils.CamelCase) + ssn.completeMerchantValidation(. merchantSession) + } + + ssn.onpaymentauthorized = event => { + ssn.completePayment(. {"status": ssn.\"STATUS_SUCCESS"}->objToJson) + applePaySessionRef := Js.Nullable.null + processPayment(event.payment.token) + } + ssn.oncancel = _ev => { + let msg = + [("showApplePayButton", true->Js.Json.boolean)]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + applePaySessionRef := Js.Nullable.null + Utils.logInfo(Js.log("Apple Pay payment cancelled")) + } + } + } else { + () + } + | None => () + } + } + ) + + addSmartEventListener( + "message", + handleApplePayMessages.contents, + "onApplePayMessages", + ) + } + if componentType === "payment" && googlePayPresent->Belt.Option.isSome { + let dict = json->getDictFromJson + let sessionObj = SessionsType.itemToObjMapper(dict, Others) + let gPayToken = SessionsType.getPaymentSessionObj(sessionObj.sessionsToken, Gpay) + + let tokenObj = switch gPayToken { + | OtherTokenOptional(optToken) => optToken + | _ => Some(SessionsType.defaultToken) + } + + let gpayobj = switch tokenObj { + | Some(val) => val + | _ => SessionsType.defaultToken + } + + let baseRequest = { + "apiVersion": 2, + "apiVersionMinor": 0, + } + let paymentDataRequest = GooglePayType.assign2( + Js.Dict.empty()->Js.Json.object_, + baseRequest->toJson, + ) + + let payRequest = GooglePayType.assign( + Js.Dict.empty()->Js.Json.object_, + baseRequest->toJson, + { + "allowedPaymentMethods": gpayobj.allowed_payment_methods->arrayJsonToCamelCase, + }->toJson, + ) + paymentDataRequest.allowedPaymentMethods = + gpayobj.allowed_payment_methods->arrayJsonToCamelCase + paymentDataRequest.transactionInfo = + gpayobj.transaction_info->transformKeys(CamelCase) + paymentDataRequest.merchantInfo = gpayobj.merchant_info->transformKeys(CamelCase) + try { + let gPayClient = GooglePayType.google( + { + "environment": publishableKey->Js.String2.startsWith("pk_prd_") + ? "PRODUCTION" + : "TEST", + }->toJson, + ) + + gPayClient.isReadyToPay(. payRequest) + ->then(res => { + let dict = res->getDictFromJson + let isReadyToPay = getBool(dict, "result", false) + let msg = [("isReadyToPay", isReadyToPay->Js.Json.boolean)]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + resolve() + }) + ->catch(err => { + logger.setLogInfo( + ~value=err->toJson->Js.Json.stringify, + ~eventName=GOOGLE_PAY_FLOW, + ~paymentMethod="GOOGLE_PAY", + ~logType=DEBUG, + (), + ) + resolve() + }) + ->ignore + + let handleGooglePayMessages = (event: Types.event) => { + let evJson = event.data->eventToJson + let gpayClicked = + evJson + ->OrcaUtils.getOptionalJsonFromJson("GpayClicked") + ->OrcaUtils.getBoolfromjson(false) + + if gpayClicked { + Js.Global.setTimeout(() => { + gPayClient.loadPaymentData(. paymentDataRequest->toJson) + ->then( + json => { logger.setLogInfo( - ~eventName=APPLE_PAY_FLOW, - ~paymentMethod="APPLE_PAY", - ~value=exceptionMessage, + ~value=json->toJson->Js.Json.stringify, + ~eventName=GOOGLE_PAY_FLOW, + ~paymentMethod="GOOGLE_PAY", + ~logType=DEBUG, (), ) - let msg = - [("applePaySyncPayment", true->Js.Json.boolean)]->Js.Dict.fromArray + let msg = [("gpayResponse", json->toJson)]->Js.Dict.fromArray mountedIframeRef->Window.iframePostMessage(msg) resolve() - }) - ->ignore - } catch { - | exn => { + }, + ) + ->catch( + err => { logger.setLogInfo( - ~value=exn->Utils.formatException->Js.Json.stringify, - ~eventName=APPLE_PAY_FLOW, - ~paymentMethod="APPLE_PAY", + ~value=err->toJson->Js.Json.stringify, + ~eventName=GOOGLE_PAY_FLOW, + ~paymentMethod="GOOGLE_PAY", + ~logType=DEBUG, (), ) - let msg = - [("applePaySyncPayment", true->Js.Json.boolean)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - } - } - | _ => () - } - } else { - let paymentRequest = - applePayPresent - ->Belt.Option.flatMap(Js.Json.decodeObject) - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->Js.Dict.get("payment_request_data") - ->Belt.Option.getWithDefault(Js.Dict.empty()->Js.Json.object_) - ->Utils.transformKeys(Utils.CamelCase) - - let ssn = applePaySession(3, paymentRequest) - switch applePaySessionRef.contents->Js.Nullable.toOption { - | Some(session) => - try { - session.abort(.) - } catch { - | error => Js.log2("Abort fail", error) - } - | None => () - } - - ssn.begin(.) - applePaySessionRef := ssn->Js.Nullable.return - - ssn.onvalidatemerchant = _event => { - let merchantSession = - applePayPresent - ->Belt.Option.flatMap(Js.Json.decodeObject) - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->Js.Dict.get("session_token_data") - ->Belt.Option.getWithDefault(Js.Dict.empty()->Js.Json.object_) - ->Utils.transformKeys(Utils.CamelCase) - ssn.completeMerchantValidation(. merchantSession) - } - ssn.onpaymentauthorized = event => { - ssn.completePayment(. {"status": ssn.\"STATUS_SUCCESS"}->objToJson) - applePaySessionRef := Js.Nullable.null - processPayment(event.payment.token) - } - ssn.oncancel = _ev => { - let msg = - [("showApplePayButton", true->Js.Json.boolean)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - applePaySessionRef := Js.Nullable.null - Utils.logInfo(Js.log("Apple Pay payment cancelled")) - } + let msg = [("gpayError", err->toJson)]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + resolve() + }, + ) + ->ignore + }, 0)->ignore } - } else { - () } - | None => () + addSmartEventListener("message", handleGooglePayMessages, "onGooglePayMessages") + } catch { + | _ => Js.log("Error loading Gpay") } } - ) - - addSmartEventListener("message", handleApplePayMessages.contents, "onApplePayMessages") - } - if componentType === "payment" && googlePayPresent->Belt.Option.isSome { - let dict = json->getDictFromJson - let sessionObj = SessionsType.itemToObjMapper(dict, Others) - let gPayToken = SessionsType.getPaymentSessionObj(sessionObj.sessionsToken, Gpay) - - let tokenObj = switch gPayToken { - | OtherTokenOptional(optToken) => optToken - | _ => Some(SessionsType.defaultToken) - } - let gpayobj = switch tokenObj { - | Some(val) => val - | _ => SessionsType.defaultToken - } - - let baseRequest = { - "apiVersion": 2, - "apiVersionMinor": 0, - } - let paymentDataRequest = GooglePayType.assign2( - Js.Dict.empty()->Js.Json.object_, - baseRequest->toJson, - ) - - let payRequest = GooglePayType.assign( - Js.Dict.empty()->Js.Json.object_, - baseRequest->toJson, - { - "allowedPaymentMethods": gpayobj.allowed_payment_methods->arrayJsonToCamelCase, - }->toJson, - ) - paymentDataRequest.allowedPaymentMethods = - gpayobj.allowed_payment_methods->arrayJsonToCamelCase - paymentDataRequest.transactionInfo = gpayobj.transaction_info->transformKeys(CamelCase) - paymentDataRequest.merchantInfo = gpayobj.merchant_info->transformKeys(CamelCase) - try { - let gPayClient = GooglePayType.google( - { - "environment": publishableKey->Js.String2.startsWith("pk_prd_") - ? "PRODUCTION" - : "TEST", - }->toJson, - ) - - gPayClient.isReadyToPay(. payRequest) - ->then(res => { - let dict = res->getDictFromJson - let isReadyToPay = getBool(dict, "result", false) - let msg = [("isReadyToPay", isReadyToPay->Js.Json.boolean)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - resolve() + json->resolve }) - ->catch(err => { - logger.setLogInfo( - ~value=err->toJson->Js.Json.stringify, - ~eventName=GOOGLE_PAY_FLOW, - ~paymentMethod="GOOGLE_PAY", - ~logType=DEBUG, - (), - ) - resolve() + ->then(json => { + let msg = [("sessions", json)]->Js.Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + json->resolve }) ->ignore - - let handleGooglePayMessages = (event: Types.event) => { - let evJson = event.data->eventToJson - let gpayClicked = - evJson - ->OrcaUtils.getOptionalJsonFromJson("GpayClicked") - ->OrcaUtils.getBoolfromjson(false) - - if gpayClicked { - Js.Global.setTimeout(() => { - gPayClient.loadPaymentData(. paymentDataRequest->toJson) - ->then( - json => { - logger.setLogInfo( - ~value=json->toJson->Js.Json.stringify, - ~eventName=GOOGLE_PAY_FLOW, - ~paymentMethod="GOOGLE_PAY", - ~logType=DEBUG, - (), - ) - let msg = [("gpayResponse", json->toJson)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - resolve() - }, - ) - ->catch( - err => { - logger.setLogInfo( - ~value=err->toJson->Js.Json.stringify, - ~eventName=GOOGLE_PAY_FLOW, - ~paymentMethod="GOOGLE_PAY", - ~logType=DEBUG, - (), - ) - - let msg = [("gpayError", err->toJson)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - resolve() - }, - ) - ->ignore - }, 0)->ignore - } - } - addSmartEventListener("message", handleGooglePayMessages, "onGooglePayMessages") - } catch { - | _ => Js.log("Error loading Gpay") } } - - json->resolve - }) - ->then(json => { - let msg = [("sessions", json)]->Js.Dict.fromArray - mountedIframeRef->Window.iframePostMessage(msg) - json->resolve + let msg = [("sendSessionTokensResponse", true->Js.Json.boolean)]->Js.Dict.fromArray + addSmartEventListener("message", handleSessionTokensLoaded, "onSessionTokensLoaded") + preMountLoaderIframeDiv->Window.iframePostMessage(msg) + } + preMountLoaderMountedPromise + ->then(_ => { + Js.log("preMountLoaderIframeMountedCallback then") + fetchPaymentsList(mountedIframeRef) + fetchCustomerPaymentMethods(mountedIframeRef, disableSaveCards) + fetchSessionTokens(mountedIframeRef) + mountedIframeRef->Window.iframePostMessage(message) + resolve() }) ->ignore - fetchPaymentsList(mountedIframeRef) - disableSaveCards ? () : fetchCustomerDetails(mountedIframeRef) - mountedIframeRef->Window.iframePostMessage(message) } let paymentElement = LoaderPaymentElement.make( diff --git a/webpack.common.js b/webpack.common.js index f98be8ba0..d9c8df82f 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -39,12 +39,12 @@ let backendEndPoint; if (envBackendUrl === undefined) { backendEndPoint = sdkEnv === "prod" - ? "https://api.hyperswitch.io" + ? "https://checkout.hyperswitch.io/api" : sdkEnv === "sandbox" - ? "https://sandbox.hyperswitch.io" + ? "https://beta.hyperswitch.io/api" : sdkEnv === "integ" ? "https://integ-api.hyperswitch.io" - : "https://sandbox.hyperswitch.io"; + : "https://beta.hyperswitch.io/api"; } else { backendEndPoint = envBackendUrl; } @@ -53,12 +53,12 @@ let confirmEndPoint; if (envBackendUrl === undefined) { confirmEndPoint = sdkEnv === "prod" - ? "https://api.hyperswitch.io" + ? "https://checkout.hyperswitch.io/api" : sdkEnv === "sandbox" - ? "https://sandbox.hyperswitch.io" + ? "https://beta.hyperswitch.io/api" : sdkEnv === "integ" ? "https://integ-api.hyperswitch.io" - : "https://sandbox.hyperswitch.io"; + : "https://beta.hyperswitch.io/api"; } else { confirmEndPoint = envBackendUrl; } @@ -146,18 +146,18 @@ module.exports = (publicPath = "auto") => { // new webpack.HTMLInjectPlugin({ // publicPath: JSON.stringify(repoVersion), // }), - sentryWebpackPlugin({ - org: "sentry", - project: "hyperswitch-react-sdk", - authToken: process.env.SENTRY_AUTH_TOKEN, - url: process.env.SENTRY_URL, - release: { - name: "0.2", - uploadLegacySourcemaps: { - paths: ["dist"], - }, - }, - }), + // sentryWebpackPlugin({ + // org: "sentry", + // project: "hyperswitch-react-sdk", + // authToken: process.env.SENTRY_AUTH_TOKEN, + // url: process.env.SENTRY_URL, + // release: { + // name: "0.2", + // uploadLegacySourcemaps: { + // paths: ["dist"], + // }, + // }, + // }), ], module: { rules: [ diff --git a/webpack.dev.js b/webpack.dev.js index 58b4a7388..37bf57ff2 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -6,12 +6,12 @@ const sdkEnv = process.env.sdkEnv; let backendEndPoint = sdkEnv === "prod" - ? "https://api.hyperswitch.io/payments" + ? "https://checkout.hyperswitch.io/api" : sdkEnv === "sandbox" - ? "https://sandbox.hyperswitch.io/payments" + ? "https://beta.hyperswitch.io/api" : sdkEnv === "integ" - ? "https://integ-api.hyperswitch.io/payments" - : "https://sandbox.hyperswitch.io/payments"; + ? "https://integ-api.hyperswitch.io" + : "https://beta.hyperswitch.io/api"; let devServer = { contentBase: path.join(__dirname, "dist"), @@ -20,11 +20,23 @@ let devServer = { historyApiFallback: true, proxy: { "/payments": { - target: backendEndPoint, + target: backendEndPoint + "/payments", changeOrigin: true, secure: true, pathRewrite: { "^/payments": "" }, }, + "/customers": { + target: backendEndPoint + "/customers", + changeOrigin: true, + secure: true, + pathRewrite: { "^/customers": "" }, + }, + "/account": { + target: backendEndPoint + "/account", + changeOrigin: true, + secure: true, + pathRewrite: { "^/account": "" }, + }, }, headers: { "Cache-Control": "max-age=31536000,must-revalidate",