From c93e44c6161bff921dacb87e7e90206da726c6be Mon Sep 17 00:00:00 2001 From: Vrishab Srivatsa <136090360+vsrivatsa-juspay@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:44:25 +0530 Subject: [PATCH] Feature - one click confirm handler (#69) Co-authored-by: Praful Koppalkar <126236898+prafulkoppalkar@users.noreply.github.com> --- src/Hooks/CommonHooks.res | 11 +- src/LoaderController.res | 1 + src/Payments/ApplePay.res | 56 ++++---- src/Payments/GPay.res | 51 ++++---- src/Payments/PayPal.res | 35 +++-- src/Payments/PaypalSDK.res | 157 +++++++++++++---------- src/Types/PaymentType.res | 2 + src/Utilities/PaymentHelpers.res | 12 +- src/Utilities/Utils.res | 5 +- src/Window.res | 1 + src/orca-loader/Elements.res | 2 + src/orca-loader/Hyper.res | 61 ++++++--- src/orca-loader/LoaderPaymentElement.res | 16 +++ src/orca-loader/OrcaUtils.res | 36 +++++- src/orca-loader/Types.res | 19 ++- 15 files changed, 303 insertions(+), 162 deletions(-) diff --git a/src/Hooks/CommonHooks.res b/src/Hooks/CommonHooks.res index 99ce82640..b5fd1f564 100644 --- a/src/Hooks/CommonHooks.res +++ b/src/Hooks/CommonHooks.res @@ -14,6 +14,7 @@ type keys = { iframeId: string, parentURL: string, sdkHandleConfirmPayment: bool, + sdkHandleOneClickConfirmPayment: bool, } @val @scope("document") external querySelector: string => Js.Nullable.t = "querySelector" @@ -67,7 +68,7 @@ let useScript = (src: string) => { let updateKeys = (dict, keyPair, setKeys) => { let (key, value) = keyPair let valueStr = value->Js.Json.decodeString->Belt.Option.getWithDefault("") - let valueBool = value->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + let valueBool = default => value->Js.Json.decodeBoolean->Belt.Option.getWithDefault(default) if dict->Utils.getDictIsSome(key) { switch key { | "iframeId" => @@ -88,7 +89,12 @@ let updateKeys = (dict, keyPair, setKeys) => { | "sdkHandleConfirmPayment" => setKeys(.prev => { ...prev, - sdkHandleConfirmPayment: dict->Utils.getBool(key, valueBool), + sdkHandleConfirmPayment: dict->Utils.getBool(key, valueBool(false)), + }) + | "sdkHandleOneClickConfirmPayment" => + setKeys(.prev => { + ...prev, + sdkHandleOneClickConfirmPayment: dict->Utils.getBool(key, valueBool(true)), }) | _ => () } @@ -100,4 +106,5 @@ let defaultkeys = { iframeId: "", parentURL: "*", sdkHandleConfirmPayment: false, + sdkHandleOneClickConfirmPayment: true, } diff --git a/src/LoaderController.res b/src/LoaderController.res index a83b8180c..efe963d22 100644 --- a/src/LoaderController.res +++ b/src/LoaderController.res @@ -260,6 +260,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { ("publishableKey", ""->Js.Json.string), ("parentURL", "*"->Js.Json.string), ("sdkHandleConfirmPayment", false->Js.Json.boolean), + ("sdkHandleOneClickConfirmPayment", true->Js.Json.boolean), ]->Js.Array2.forEach(keyPair => { dict->CommonHooks.updateKeys(keyPair, setKeys) }) diff --git a/src/Payments/ApplePay.res b/src/Payments/ApplePay.res index babecfe5f..ed310c8ad 100644 --- a/src/Payments/ApplePay.res +++ b/src/Payments/ApplePay.res @@ -7,7 +7,9 @@ let make = ( ~walletOptions: array, ) => { let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) - let {publishableKey} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) + let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom( + RecoilAtoms.keys, + ) let isApplePayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isApplePayReady) let setIsShowOrPayUsing = Recoil.useSetRecoilState(RecoilAtoms.isShowOrPayUsing) let (showApplePay, setShowApplePay) = React.useState(() => false) @@ -234,30 +236,40 @@ let make = ( (), ) setApplePayClicked(_ => true) + open Promise + OrcaUtils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment) + ->then(result => { + let result = result->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + if result { + if isInvokeSDKFlow { + let isDelayedSessionToken = + sessionObj + ->Belt.Option.getWithDefault(Js.Json.null) + ->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 isInvokeSDKFlow { - let isDelayedSessionToken = - sessionObj - ->Belt.Option.getWithDefault(Js.Json.null) - ->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 { - setShowApplePayLoader(_ => true) - let bodyDict = PaymentBody.applePayThirdPartySdkBody(~connectors) - processPayment(bodyDict) + if isDelayedSessionToken { + setShowApplePayLoader(_ => true) + let bodyDict = PaymentBody.applePayThirdPartySdkBody(~connectors) + processPayment(bodyDict) + } else { + let message = [("applePayButtonClicked", true->Js.Json.boolean)] + Utils.handlePostMessage(message) + } + } else { + let bodyDict = PaymentBody.applePayRedirectBody(~connectors) + processPayment(bodyDict) + } } else { - let message = [("applePayButtonClicked", true->Js.Json.boolean)] - Utils.handlePostMessage(message) + setApplePayClicked(_ => false) } - } else { - let bodyDict = PaymentBody.applePayRedirectBody(~connectors) - processPayment(bodyDict) - } + resolve() + }) + ->ignore } React.useEffect3(() => { diff --git a/src/Payments/GPay.res b/src/Payments/GPay.res index a7219f339..d69fea639 100644 --- a/src/Payments/GPay.res +++ b/src/Payments/GPay.res @@ -14,7 +14,7 @@ let make = ( let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Js.Dict.empty()) let (loggerState, _setLoggerState) = Recoil.useRecoilState(loggerAtom) let {iframeId} = Recoil.useRecoilValueFromAtom(keys) - let {publishableKey} = Recoil.useRecoilValueFromAtom(keys) + let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom(keys) let {localeString} = Recoil.useRecoilValueFromAtom(configAtom) let options = Recoil.useRecoilValueFromAtom(optionAtom) let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Gpay) @@ -137,28 +137,35 @@ let make = ( ~paymentMethod="GOOGLE_PAY", (), ) - if isInvokeSDKFlow { - if isDelayedSessionToken { - let bodyDict = PaymentBody.gPayThirdPartySdkBody(~connectors) - processPayment(bodyDict) - } else { - handlePostMessage([ - ("fullscreen", true->Js.Json.boolean), - ("param", "paymentloader"->Js.Json.string), - ("iframeId", iframeId->Js.Json.string), - ]) - options.readOnly ? () : handlePostMessage([("GpayClicked", true->Js.Json.boolean)]) + open Promise + OrcaUtils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment)->then(result => { + let result = result->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + if result { + if isInvokeSDKFlow { + if isDelayedSessionToken { + let bodyDict = PaymentBody.gPayThirdPartySdkBody(~connectors) + processPayment(bodyDict) + } else { + handlePostMessage([ + ("fullscreen", true->Js.Json.boolean), + ("param", "paymentloader"->Js.Json.string), + ("iframeId", iframeId->Js.Json.string), + ]) + options.readOnly ? () : handlePostMessage([("GpayClicked", true->Js.Json.boolean)]) + } + } else { + let bodyDict = PaymentBody.gpayRedirectBody(~connectors) + processPayment(bodyDict) + } + loggerState.setLogInfo( + ~value="", + ~eventName=PAYMENT_DATA_FILLED, + ~paymentMethod="GOOGLE_PAY", + (), + ) } - } else { - let bodyDict = PaymentBody.gpayRedirectBody(~connectors) - processPayment(bodyDict) - } - loggerState.setLogInfo( - ~value="", - ~eventName=PAYMENT_DATA_FILLED, - ~paymentMethod="GOOGLE_PAY", - (), - ) + resolve() + }) } let buttonStyle = { diff --git a/src/Payments/PayPal.res b/src/Payments/PayPal.res index ffe0abbb6..041f7b00f 100644 --- a/src/Payments/PayPal.res +++ b/src/Payments/PayPal.res @@ -14,7 +14,7 @@ let payPalIcon = let make = (~list: PaymentMethodsRecord.list) => { let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) let (paypalClicked, setPaypalClicked) = React.useState(_ => false) - let {publishableKey} = Recoil.useRecoilValueFromAtom(keys) + let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom(keys) let options = Recoil.useRecoilValueFromAtom(optionAtom) let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered) let (_, _, labelType) = options.wallets.style.type_ @@ -38,17 +38,28 @@ let make = (~list: PaymentMethodsRecord.list) => { (), ) setPaypalClicked(_ => true) - let (connectors, _) = list->PaymentUtils.getConnectors(Wallets(Paypal(Redirect))) - let body = PaymentBody.paypalRedirectionBody(~connectors) - intent( - ~bodyArr=body, - ~confirmParam={ - return_url: options.wallets.walletReturnUrl, - publishableKey, - }, - ~handleUserError=true, - (), - ) + open Promise + OrcaUtils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment) + ->then(result => { + let result = result->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + if result { + let (connectors, _) = list->PaymentUtils.getConnectors(Wallets(Paypal(Redirect))) + let body = PaymentBody.paypalRedirectionBody(~connectors) + intent( + ~bodyArr=body, + ~confirmParam={ + return_url: options.wallets.walletReturnUrl, + publishableKey, + }, + ~handleUserError=true, + (), + ) + } else { + setPaypalClicked(_ => false) + } + resolve() + }) + ->ignore } React.useEffect0(() => { diff --git a/src/Payments/PaypalSDK.res b/src/Payments/PaypalSDK.res index b80a1457d..727aab4a0 100644 --- a/src/Payments/PaypalSDK.res +++ b/src/Payments/PaypalSDK.res @@ -1,6 +1,8 @@ @react.component let make = (~sessionObj: SessionsType.token, ~list: PaymentMethodsRecord.list) => { - let {iframeId, publishableKey} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) + let {iframeId, publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom( + RecoilAtoms.keys, + ) let (loggerState, _setLoggerState) = Recoil.useRecoilState(RecoilAtoms.loggerAtom) let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered) @@ -42,77 +44,90 @@ let make = (~sessionObj: SessionsType.token, ~list: PaymentMethodsRecord.list) = ~paymentMethod="PAYPAL", (), ) - braintree.client.create(.{authorization: token}, (clientErr, clientInstance) => { - if clientErr { - Js.Console.error2("Error creating client", clientErr) + open Promise + OrcaUtils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment) + ->then(result => { + let result = result->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + if result { + braintree.client.create(.{authorization: token}, (clientErr, clientInstance) => { + if clientErr { + Js.Console.error2("Error creating client", clientErr) + } + braintree.paypalCheckout.create(. + {client: clientInstance}, + (paypalCheckoutErr, paypalCheckoutInstance) => { + switch paypalCheckoutErr->Js.Nullable.toOption { + | Some(val) => Js.Console.warn(`INTEGRATION ERROR: ${val.message}`) + | None => () + } + paypalCheckoutInstance.loadPayPalSDK(. + {vault: true}, + () => { + let paypalWrapper = GooglePayType.getElementById(Utils.document, "paypal-button") + paypalWrapper.innerHTML = "" + paypal["Buttons"](. { + style: buttonStyle, + fundingSource: paypal["FUNDING"]["PAYPAL"], + createBillingAgreement: () => { + //Paypal Clicked + Utils.handlePostMessage([ + ("fullscreen", true->Js.Json.boolean), + ("param", "paymentloader"->Js.Json.string), + ("iframeId", iframeId->Js.Json.string), + ]) + options.readOnly + ? () + : paypalCheckoutInstance.createPayment(. { + ...defaultOrderDetails, + flow: "vault", + }) + }, + onApprove: (. data, _actions) => { + options.readOnly + ? () + : paypalCheckoutInstance.tokenizePayment(. + data, + (. _err, payload) => { + let (connectors, _) = + list->PaymentUtils.getConnectors(Wallets(Paypal(SDK))) + let body = PaymentBody.paypalSdkBody( + ~token=payload.nonce, + ~connectors, + ) + intent( + ~bodyArr=body, + ~confirmParam={ + return_url: options.wallets.walletReturnUrl, + publishableKey, + }, + ~handleUserError=true, + (), + ) + }, + ) + }, + onCancel: (. _data) => { + handleCloseLoader() + }, + onError: (. _err) => { + handleCloseLoader() + }, + }).render(. "#paypal-button") + areOneClickWalletsRendered(. + prev => { + ...prev, + isPaypal: true, + }, + ) + }, + ) + }, + ) + })->ignore } - braintree.paypalCheckout.create(.{client: clientInstance}, ( - paypalCheckoutErr, - paypalCheckoutInstance, - ) => { - switch paypalCheckoutErr->Js.Nullable.toOption { - | Some(val) => Js.Console.warn(`INTEGRATION ERROR: ${val.message}`) - | None => () - } - paypalCheckoutInstance.loadPayPalSDK(. - {vault: true}, - () => { - let paypalWrapper = GooglePayType.getElementById(Utils.document, "paypal-button") - paypalWrapper.innerHTML = "" - paypal["Buttons"](. { - style: buttonStyle, - fundingSource: paypal["FUNDING"]["PAYPAL"], - createBillingAgreement: () => { - //Paypal Clicked - Utils.handlePostMessage([ - ("fullscreen", true->Js.Json.boolean), - ("param", "paymentloader"->Js.Json.string), - ("iframeId", iframeId->Js.Json.string), - ]) - options.readOnly - ? () - : paypalCheckoutInstance.createPayment(. { - ...defaultOrderDetails, - flow: "vault", - }) - }, - onApprove: (. data, _actions) => { - options.readOnly - ? () - : paypalCheckoutInstance.tokenizePayment(. - data, - (. _err, payload) => { - let (connectors, _) = list->PaymentUtils.getConnectors(Wallets(Paypal(SDK))) - let body = PaymentBody.paypalSdkBody(~token=payload.nonce, ~connectors) - intent( - ~bodyArr=body, - ~confirmParam={ - return_url: options.wallets.walletReturnUrl, - publishableKey, - }, - ~handleUserError=true, - (), - ) - }, - ) - }, - onCancel: (. _data) => { - handleCloseLoader() - }, - onError: (. _err) => { - handleCloseLoader() - }, - }).render(. "#paypal-button") - areOneClickWalletsRendered(. - prev => { - ...prev, - isPaypal: true, - }, - ) - }, - ) - }) - })->ignore + resolve() + }) + ->ignore } React.useEffect0(() => { if true { diff --git a/src/Types/PaymentType.res b/src/Types/PaymentType.res index 77f21ee21..bbbd5bbe9 100644 --- a/src/Types/PaymentType.res +++ b/src/Types/PaymentType.res @@ -866,6 +866,8 @@ let itemToObjMapper = (dict, logger) => { "wallets", "showCardFormByDefault", "disableSaveCards", + "sdkHandleOneClickConfirmPayment", + "showCardFormByDefault", ], dict, "options", diff --git a/src/Utilities/PaymentHelpers.res b/src/Utilities/PaymentHelpers.res index defda0ee2..df0989b87 100644 --- a/src/Utilities/PaymentHelpers.res +++ b/src/Utilities/PaymentHelpers.res @@ -336,7 +336,11 @@ let intentCall = ( handlePostMessage(message) } else { switch paymentType { - | Card => postSubmitResponse(~jsonData=data, ~url=url.href) + | Card + | Gpay + | Applepay + | Paypal => + postSubmitResponse(~jsonData=data, ~url=url.href) | _ => openUrl(url.href) } } @@ -362,7 +366,11 @@ let intentCall = ( setIsManualRetryEnabled(. _ => intent.manualRetryAllowed) } switch paymentType { - | Card => postSubmitResponse(~jsonData=data, ~url=url.href) + | Card + | Gpay + | Applepay + | Paypal => + postSubmitResponse(~jsonData=data, ~url=url.href) | _ => openUrl(url.href) } } else { diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res index f80541cb5..167f2a12d 100644 --- a/src/Utilities/Utils.res +++ b/src/Utilities/Utils.res @@ -37,8 +37,9 @@ let handleOnClickPostMessage = (~targetOrigin="*", ev) => { ~targetOrigin, ) } -let handleOnConfirmPostMessage = (~targetOrigin="*", ()) => { - handlePostMessage([("confirmTriggered", true->Js.Json.boolean)], ~targetOrigin) +let handleOnConfirmPostMessage = (~targetOrigin="*", ~isOneClick=false, ()) => { + let message = isOneClick ? "oneClickConfirmTriggered" : "confirmTriggered" + handlePostMessage([(message, true->Js.Json.boolean)], ~targetOrigin) } let getOptionString = (dict, key) => { dict->Js.Dict.get(key)->Belt.Option.flatMap(Js.Json.decodeString) diff --git a/src/Window.res b/src/Window.res index fd57be2cc..1bf126025 100644 --- a/src/Window.res +++ b/src/Window.res @@ -37,6 +37,7 @@ type eventData = { focus: bool, blur: bool, confirmTriggered: bool, + oneClickConfirmTriggered: bool, } type loaderEvent = {key: string, data: eventData} @set external innerHTML: (Dom.element, string) => unit = "innerHTML" diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res index b663f6977..6ccb392e5 100644 --- a/src/orca-loader/Elements.res +++ b/src/orca-loader/Elements.res @@ -219,6 +219,7 @@ let make = ( mountedIframeRef, selectorString, sdkHandleConfirmPayment, + sdkHandleOneClickConfirmPayment, disableSaveCards, ) => { open Promise @@ -248,6 +249,7 @@ let make = ( ("blockConfirm", blockConfirm->Js.Json.boolean), ("switchToCustomPod", switchToCustomPod->Js.Json.boolean), ("endpoint", endpoint->Js.Json.string), + ("sdkHandleOneClickConfirmPayment", sdkHandleOneClickConfirmPayment->Js.Json.boolean), ("parentURL", "*"->Js.Json.string), ("analyticsMetadata", analyticsMetadata), ]->Js.Dict.fromArray diff --git a/src/orca-loader/Hyper.res b/src/orca-loader/Hyper.res index f72762784..3ed07f432 100644 --- a/src/orca-loader/Hyper.res +++ b/src/orca-loader/Hyper.res @@ -240,7 +240,8 @@ let make = (publishableKey, options: option, analyticsInfo: optionJs.Dict.fromArray->Js.Json.object_->Promise.resolve }) } - let confirmPayment = payload => { + + let confirmPaymentWrapper = (payload, isOneClick, result) => { let confirmParams = payload ->Js.Json.decodeObject @@ -262,24 +263,6 @@ let make = (publishableKey, options: option, analyticsInfo: optionBelt.Option.getWithDefault("") Js.Promise.make((~resolve, ~reject as _) => { - iframeRef.contents->Js.Array2.forEach(ifR => { - ifR->Window.iframePostMessage( - [ - ("doSubmit", true->Js.Json.boolean), - ("clientSecret", clientSecret.contents->Js.Json.string), - ( - "confirmParams", - [ - ("return_url", url->Js.Json.string), - ("publishableKey", publishableKey->Js.Json.string), - ] - ->Js.Dict.fromArray - ->Js.Json.object_, - ), - ]->Js.Dict.fromArray, - ) - }) - let handleMessage = (event: Types.event) => { let json = event.data->eventToJson let dict = json->getDictFromJson @@ -303,6 +286,16 @@ let make = (publishableKey, options: option, analyticsInfo: optionJs.Dict.get("url") ->Belt.Option.flatMap(Js.Json.decodeString) ->Belt.Option.getWithDefault(url) + + if isOneClick { + iframeRef.contents->Js.Array2.forEach(ifR => { + // to unset one click button loader + ifR->Window.iframePostMessage( + [("oneClickDoSubmit", false->Js.Json.boolean)]->Js.Dict.fromArray, + ) + }) + } + if ( val->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) && redirect === "always" @@ -316,10 +309,36 @@ let make = (publishableKey, options: option, analyticsInfo: option () } } - + let message = isOneClick + ? [("oneClickDoSubmit", result->Js.Json.boolean)]->Js.Dict.fromArray + : [ + ("doSubmit", true->Js.Json.boolean), + ("clientSecret", clientSecret.contents->Js.Json.string), + ( + "confirmParams", + [ + ("return_url", url->Js.Json.string), + ("publishableKey", publishableKey->Js.Json.string), + ] + ->Js.Dict.fromArray + ->Js.Json.object_, + ), + ]->Js.Dict.fromArray addSmartEventListener("message", handleMessage, "onSubmit") + iframeRef.contents->Js.Array2.forEach(ifR => { + ifR->Window.iframePostMessage(message) + }) }) } + + let confirmPayment = payload => { + confirmPaymentWrapper(payload, false, true) + } + + let confirmOneClickPayment = (payload, result: bool) => { + confirmPaymentWrapper(payload, true, result) + } + let elements = elementsOptions => { open Promise let clientSecretId = @@ -468,7 +487,9 @@ let make = (publishableKey, options: option, analyticsInfo: optionJs.Json.object_ Window.paymentRequest(methodData, details, optionsForPaymentRequest) } + let returnObject = { + confirmOneClickPayment, confirmPayment, elements, widgets: elements, diff --git a/src/orca-loader/LoaderPaymentElement.res b/src/orca-loader/LoaderPaymentElement.res index ad3fd4f0e..df198ccf0 100644 --- a/src/orca-loader/LoaderPaymentElement.res +++ b/src/orca-loader/LoaderPaymentElement.res @@ -25,6 +25,14 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = ->Belt.Option.flatMap(x => x->Js.Dict.get("sdkHandleConfirmPayment")) ->Belt.Option.flatMap(Js.Json.decodeBoolean) ->Belt.Option.getWithDefault(false) + + let sdkHandleOneClickConfirmPayment = + options + ->Js.Json.decodeObject + ->Belt.Option.flatMap(x => x->Js.Dict.get("sdkHandleOneClickConfirmPayment")) + ->Belt.Option.flatMap(Js.Json.decodeBoolean) + ->Belt.Option.getWithDefault(true) + let disableSaveCards = options ->Js.Json.decodeObject @@ -65,6 +73,13 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = ConfirmPayment, "onHelpConfirmPayment", ) + | OneClickConfirmPayment => + eventHandlerFunc( + ev => ev.data.oneClickConfirmTriggered, + eventHandler, + OneClickConfirmPayment, + "onHelpOneClickConfirmPayment", + ) | _ => () } } @@ -292,6 +307,7 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = Window.querySelector(`#orca-payment-element-iframeRef-${localSelectorString}`), localSelectorString, sdkHandleConfirmPayment, + sdkHandleOneClickConfirmPayment, disableSaveCards, ) } diff --git a/src/orca-loader/OrcaUtils.res b/src/orca-loader/OrcaUtils.res index aac452edf..9e5b68880 100644 --- a/src/orca-loader/OrcaUtils.res +++ b/src/orca-loader/OrcaUtils.res @@ -1,5 +1,6 @@ open Utils external toNullable: Js.Json.t => Js.Nullable.t = "%identity" +external eventToJson: Types.eventData => Js.Json.t = "%identity" let safeParseOpt = st => { try { Js.Json.parseExn(st)->Some @@ -132,10 +133,12 @@ let rec flatten = (obj, addIndicatorForObject) => { let flattenedSubObj = flatten(item, addIndicatorForObject) flattenedSubObj ->Js.Dict.entries - ->Js.Array2.forEach(subEntry => { - let (subKey, subValue) = subEntry - Js.Dict.set(newDict, `${key}[${index->string_of_int}].${subKey}`, subValue) - }) + ->Js.Array2.forEach( + subEntry => { + let (subKey, subValue) = subEntry + Js.Dict.set(newDict, `${key}[${index->string_of_int}].${subKey}`, subValue) + }, + ) } | _ => @@ -243,6 +246,7 @@ let eventHandlerFunc = ( | Ready | Focus | ConfirmPayment + | OneClickConfirmPayment | Blur => switch eventHandler { | Some(eH) => eH(Some(ev.data)) @@ -330,3 +334,27 @@ let getArrayOfTupleFromDict = dict => { ->Js.Dict.keys ->Belt.Array.map(key => (key, Js.Dict.get(dict, key)->Belt.Option.getWithDefault(Js.Json.null))) } + +let makeOneClickHandlerPromise = sdkHandleOneClickConfirmPayment => { + open EventListenerManager + Js.Promise.make((~resolve, ~reject as _) => { + if sdkHandleOneClickConfirmPayment { + resolve(. Js.Json.boolean(true)) + } else { + let handleMessage = (event: Types.event) => { + let json = event.data->eventToJson->getStringfromjson("")->safeParse + + let dict = json->Utils.getDictFromJson + if dict->Js.Dict.get("oneClickDoSubmit")->Belt.Option.isSome { + resolve(. + dict + ->Js.Dict.get("oneClickDoSubmit") + ->Belt.Option.getWithDefault(true->Js.Json.boolean), + ) + } + } + addSmartEventListener("message", handleMessage, "onOneClickHandlerPaymentConfirm") + Utils.handleOnConfirmPostMessage(~targetOrigin="*", ~isOneClick=true, ()) + } + }) +} diff --git a/src/orca-loader/Types.res b/src/orca-loader/Types.res index 2fe5eea3e..a37ab20b0 100644 --- a/src/orca-loader/Types.res +++ b/src/orca-loader/Types.res @@ -8,6 +8,7 @@ type eventData = { classChange: bool, newClassType: string, confirmTriggered: bool, + oneClickConfirmTriggered: bool, } type event = {key: string, data: eventData} type eventParam = Event(event) | EventData(eventData) | Empty @@ -46,6 +47,7 @@ type confirmPaymentParams = { } type hyperInstance = { + confirmOneClickPayment: (Js.Json.t, bool) => Js.Promise.t, confirmPayment: Js.Json.t => Js.Promise.t, elements: Js.Json.t => element, confirmCardPayment: Js_OO.Callback.arity4< @@ -56,6 +58,10 @@ type hyperInstance = { paymentRequest: Js.Json.t => Js.Json.t, } +let oneClickConfirmPaymentFn = (_, _) => { + Js.Promise.resolve(Js.Dict.empty()->Js.Json.object_) +} + let confirmPaymentFn = (_elements: Js.Json.t) => { Js.Promise.resolve(Js.Dict.empty()->Js.Json.object_) } @@ -103,13 +109,14 @@ let create = (_componentType, _options) => { } let defaultElement = { - getElement: getElement, - update: update, - fetchUpdates: fetchUpdates, - create: create, + getElement, + update, + fetchUpdates, + create, } let defaultHyperInstance = { + confirmOneClickPayment: oneClickConfirmPaymentFn, confirmPayment: confirmPaymentFn, confirmCardPayment: confirmCardPaymentFn, retrievePaymentIntent: retrievePaymentIntentFn, @@ -118,7 +125,8 @@ let defaultHyperInstance = { paymentRequest: _ev => Js.Json.null, } -type eventType = Escape | Change | Click | Ready | Focus | Blur | ConfirmPayment | None +type eventType = + Escape | Change | Click | Ready | Focus | Blur | ConfirmPayment | OneClickConfirmPayment | None let eventTypeMapper = event => { switch event { @@ -129,6 +137,7 @@ let eventTypeMapper = event => { | "focus" => Focus | "blur" => Blur | "confirmTriggered" => ConfirmPayment + | "oneClickConfirmTriggered" => OneClickConfirmPayment | _ => None } }