diff --git a/public/icons/orca.svg b/public/icons/orca.svg index 5a78ee6d1..5cb955e11 100644 --- a/public/icons/orca.svg +++ b/public/icons/orca.svg @@ -2538,4 +2538,9 @@ License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL + + + + + diff --git a/src/App.res b/src/App.res index f4d9644d4..0d2cbcc0a 100644 --- a/src/App.res +++ b/src/App.res @@ -44,7 +44,14 @@ let make = () => { let sessionId = CardUtils.getQueryParamsDictforKey(url.search, "sessionId") let publishableKey = CardUtils.getQueryParamsDictforKey(url.search, "publishableKey") let endpoint = CardUtils.getQueryParamsDictforKey(url.search, "endpoint") - + let ephemeralKey = CardUtils.getQueryParamsDictforKey(url.search, "ephemeralKey") + let hyperComponentName = + CardUtils.getQueryParamsDictforKey( + url.search, + "hyperComponentName", + )->Types.getHyperComponentNameFromStr + + } | "achBankTransfer" | "bacsBankTransfer" diff --git a/src/CardTheme.res b/src/CardTheme.res index e7d3efeab..771a0a826 100644 --- a/src/CardTheme.res +++ b/src/CardTheme.res @@ -59,6 +59,7 @@ let defaultConfig = { locale: "auto", fonts: [], clientSecret: "", + ephemeralKey: "", loader: Auto, } type recoilConfig = { @@ -382,7 +383,7 @@ let itemToObjMapper = ( logger, ) => { unknownKeysWarning( - ["appearance", "fonts", "locale", "clientSecret", "loader"], + ["appearance", "fonts", "locale", "clientSecret", "loader", "ephemeralKey"], dict, "elements", ~logger, @@ -392,6 +393,7 @@ let itemToObjMapper = ( locale: getWarningString(dict, "locale", "auto", ~logger), fonts: getFonts("fonts", dict, logger), clientSecret: getWarningString(dict, "clientSecret", "", ~logger), + ephemeralKey: getWarningString(dict, "ephemeralKey", "", ~logger), loader: getWarningString(dict, "loader", "auto", ~logger)->getShowLoader(logger), } } diff --git a/src/CardUtils.res b/src/CardUtils.res index e98bd8ca2..017b1cfdd 100644 --- a/src/CardUtils.res +++ b/src/CardUtils.res @@ -363,6 +363,7 @@ let getCardBrandIcon = (cardType, paymentType) => { | ApplePayElement | KlarnaElement | ExpressCheckoutElement + | PaymentMethodsManagement | NONE => } @@ -625,3 +626,26 @@ let useCardDetails = (~cvcNumber, ~isCvcValidValue, ~isCVCValid) => { (isCardDetailsEmpty, isCardDetailsValid, isCardDetailsInvalid) }, (cvcNumber, isCvcValidValue, isCVCValid)) } + +let getWalletBrandIcon = (customerMethod: PaymentType.customerMethods) => { + switch customerMethod.paymentMethodType { + | Some("apple_pay") => + | Some("google_pay") => + | Some("paypal") => + | _ => + } +} + +let getPaymentMethodBrand = (customerMethod: PaymentType.customerMethods) => { + switch customerMethod.paymentMethod { + | "wallet" => getWalletBrandIcon(customerMethod) + | _ => + getCardBrandIcon( + switch customerMethod.card.scheme { + | Some(ele) => ele + | None => "" + }->getCardType, + ""->CardThemeType.getPaymentMode, + ) + } +} diff --git a/src/Components/PaymentElementShimmer.res b/src/Components/PaymentElementShimmer.res index e2a7eabd6..d054f5d91 100644 --- a/src/Components/PaymentElementShimmer.res +++ b/src/Components/PaymentElementShimmer.res @@ -16,6 +16,20 @@ module Shimmer = { } } +module SavedPaymentShimmer = { + @react.component + let make = () => { + +
+
+
+
+
+
+ + } +} + @react.component let make = () => {
diff --git a/src/Components/SavedMethodItem.res b/src/Components/SavedMethodItem.res new file mode 100644 index 000000000..287e712b1 --- /dev/null +++ b/src/Components/SavedMethodItem.res @@ -0,0 +1,88 @@ +@react.component +let make = (~brandIcon, ~paymentItem: PaymentType.customerMethods, ~handleDelete) => { + let {themeObj, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) + let {hideExpiredPaymentMethods} = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) + let isCard = paymentItem.paymentMethod === "card" + let expiryMonth = paymentItem.card.expiryMonth + let expiryYear = paymentItem.card.expiryYear + let expiryDate = Date.fromString(`${expiryYear}-${expiryMonth}`) + let currentDate = Date.make() + let pickerItemClass = "PickerItem--selected" + let isCardExpired = isCard && expiryDate < currentDate + let paymentMethodType = paymentItem.paymentMethodType->Option.getOr("debit") + + +
+
+
+
+
+
+
brandIcon
+
+
+ {if isCard { +
+
{React.string(paymentItem.card.nickname)}
+
+
{React.string(`****`)}
+
{React.string(paymentItem.card.last4Digits)}
+
+
+ } else { +
{React.string(paymentMethodType->Utils.snakeToTitleCase)}
+ }} +
+
+
+ +
+
+ {React.string( + `${expiryMonth} / ${expiryYear->CardUtils.formatExpiryToTwoDigit}`, + )} +
+
+
+
+ paymentItem->handleDelete} + /> +
+
+
+ +
+ {`*${localeString.cardExpiredText}`->React.string} +
+
+
+
+
+
+
+
+} diff --git a/src/Components/SavedMethods.res b/src/Components/SavedMethods.res index 7474006f8..f3e357bad 100644 --- a/src/Components/SavedMethods.res +++ b/src/Components/SavedMethods.res @@ -45,15 +45,6 @@ let make = ( let {paymentToken: paymentTokenVal, customerId} = paymentToken - let getWalletBrandIcon = (obj: PaymentType.customerMethods) => { - switch obj.paymentMethodType { - | Some("apple_pay") => - | Some("google_pay") => - | Some("paypal") => - | _ => - } - } - let isCustomerAcceptanceRequired = useIsCustomerAcceptanceRequired( ~displaySavedPaymentMethodsCheckbox, ~isSaveCardsChecked, @@ -63,18 +54,7 @@ let make = ( let bottomElement = { savedMethods ->Array.mapWithIndex((obj, i) => { - let brandIcon = switch obj.paymentMethod { - | "wallet" => getWalletBrandIcon(obj) - | "bank_debit" => - | _ => - getCardBrandIcon( - switch obj.card.scheme { - | Some(ele) => ele - | None => "" - }->getCardType, - ""->CardThemeType.getPaymentMode, - ) - } + let brandIcon = obj->getPaymentMethodBrand let isActive = paymentTokenVal == obj.paymentToken Int.toString} @@ -266,14 +246,7 @@ let make = ( fontWeight: "400", marginTop: "25px", }> - -
-
-
-
-
-
- +
} else { {bottomElement} diff --git a/src/Components/SavedPaymentManagement.res b/src/Components/SavedPaymentManagement.res new file mode 100644 index 000000000..f10467622 --- /dev/null +++ b/src/Components/SavedPaymentManagement.res @@ -0,0 +1,71 @@ +@react.component +let make = (~savedMethods: array, ~setSavedMethods) => { + open CardUtils + open Utils + + let {iframeId} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) + let {config} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) + let switchToCustomPod = Recoil.useRecoilValueFromAtom(RecoilAtoms.switchToCustomPod) + let logger = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) + + let removeSavedMethod = paymentMethodId => { + savedMethods->Array.filter(savedMethod => { + savedMethod.paymentMethodId !== paymentMethodId + }) + } + + let handleDelete = (paymentItem: PaymentType.customerMethods) => { + handlePostMessage([ + ("fullscreen", true->JSON.Encode.bool), + ("param", "paymentloader"->JSON.Encode.string), + ("iframeId", iframeId->JSON.Encode.string), + ]) + open Promise + PaymentHelpers.deletePaymentMethod( + ~ephemeralKey=config.ephemeralKey, + ~paymentMethodId=paymentItem.paymentMethodId, + ~logger, + ~switchToCustomPod, + ) + ->then(res => { + let dict = res->getDictFromJson + let paymentMethodId = dict->getString("payment_method_id", "") + let isDeleted = dict->getBool("deleted", false) + + if isDeleted { + logger.setLogInfo( + ~value="Successfully Deleted Saved Payment Method", + ~eventName=DELETE_SAVED_PAYMENT_METHOD, + (), + ) + setSavedMethods(_ => paymentMethodId->removeSavedMethod) + } else { + logger.setLogError( + ~value=res->JSON.stringify, + ~eventName=DELETE_SAVED_PAYMENT_METHOD, + (), + ) + } + handlePostMessage([("fullscreen", false->JSON.Encode.bool)]) + resolve() + }) + ->catch(err => { + let exceptionMessage = err->formatException->JSON.stringify + logger.setLogError( + ~value=`Error Deleting Saved Payment Method: ${exceptionMessage}`, + ~eventName=DELETE_SAVED_PAYMENT_METHOD, + (), + ) + handlePostMessage([("fullscreen", false->JSON.Encode.bool)]) + resolve() + }) + ->ignore + } + + savedMethods + ->Array.mapWithIndex((obj, i) => { + let brandIcon = obj->getPaymentMethodBrand + Int.toString} paymentItem=obj brandIcon handleDelete /> + }) + ->React.array +} diff --git a/src/Hooks/CommonHooks.res b/src/Hooks/CommonHooks.res index e25667f9b..be864c298 100644 --- a/src/Hooks/CommonHooks.res +++ b/src/Hooks/CommonHooks.res @@ -10,6 +10,7 @@ type element = { } type keys = { clientSecret: option, + ephemeralKey: option, publishableKey: string, iframeId: string, parentURL: string, @@ -94,6 +95,7 @@ let updateKeys = (dict, keyPair, setKeys) => { } let defaultkeys = { clientSecret: None, + ephemeralKey: None, publishableKey: "", iframeId: "", parentURL: "*", diff --git a/src/LoaderController.res b/src/LoaderController.res index 6824896d1..dccdc8b54 100644 --- a/src/LoaderController.res +++ b/src/LoaderController.res @@ -127,6 +127,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime locale: config.locale, fonts: config.fonts, clientSecret: config.clientSecret, + ephemeralKey: config.ephemeralKey, loader: config.loader, }, themeObj: appearance.variables, @@ -257,9 +258,11 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime let paymentOptions = dict->getDictFromObj("paymentOptions") let clientSecret = getWarningString(paymentOptions, "clientSecret", "", ~logger) + let ephemeralKey = getWarningString(paymentOptions, "ephemeralKey", "", ~logger) setKeys(prev => { ...prev, clientSecret: Some(clientSecret), + ephemeralKey: Some(ephemeralKey), }) logger.setClientSecret(clientSecret) @@ -304,9 +307,11 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime let paymentOptions = dict->getDictFromObj("paymentOptions") let clientSecret = getWarningString(paymentOptions, "clientSecret", "", ~logger) + let ephemeralKey = getWarningString(paymentOptions, "ephemeralKey", "", ~logger) setKeys(prev => { ...prev, clientSecret: Some(clientSecret), + ephemeralKey: Some(ephemeralKey), }) logger.setClientSecret(clientSecret) @@ -446,7 +451,8 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime setPaymentMethodList(_ => updatedState) } if dict->getDictIsSome("customerPaymentMethods") { - let customerPaymentMethods = dict->PaymentType.createCustomerObjArr + let customerPaymentMethods = + dict->PaymentType.createCustomerObjArr("customerPaymentMethods") setOptionsPayment(prev => { ...prev, customerPaymentMethods, @@ -457,6 +463,51 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime Date.now() -. launchTime } + let evalMethodsList = () => + switch paymentMethodList { + | Loaded(_) => + logger.setLogInfo( + ~value="Loaded", + ~eventName=LOADER_CHANGED, + ~latency=finalLoadLatency, + (), + ) + | LoadError(x) => + logger.setLogError( + ~value="LoadError: " ++ x->JSON.stringify, + ~eventName=LOADER_CHANGED, + ~latency=finalLoadLatency, + (), + ) + | _ => () + } + + switch optionsPayment.customerPaymentMethods { + | LoadingSavedCards => () + | LoadedSavedCards(list, _) => + list->Array.length > 0 + ? logger.setLogInfo( + ~value="Loaded", + ~eventName=LOADER_CHANGED, + ~latency=finalLoadLatency, + (), + ) + : evalMethodsList() + | NoResult(_) => evalMethodsList() + } + } + if dict->getDictIsSome("savedPaymentMethods") { + let savedPaymentMethods = dict->PaymentType.createCustomerObjArr("savedPaymentMethods") + setOptionsPayment(prev => { + ...prev, + savedPaymentMethods, + }) + let finalLoadLatency = if launchTime <= 0.0 { + -1.0 + } else { + Date.now() -. launchTime + } + let evalMethodsList = () => switch paymentMethodList { | Loaded(_) => diff --git a/src/PaymentManagement.res b/src/PaymentManagement.res new file mode 100644 index 000000000..e12768323 --- /dev/null +++ b/src/PaymentManagement.res @@ -0,0 +1,47 @@ +open PaymentType +open RecoilAtoms + +@react.component +let make = () => { + let {savedPaymentMethods, displaySavedPaymentMethods} = Recoil.useRecoilValueFromAtom(optionAtom) + let (savedMethods, setSavedMethods) = React.useState(_ => []) + let (isLoading, setIsLoading) = React.useState(_ => false) + + React.useEffect(() => { + switch savedPaymentMethods { + | LoadedSavedCards(savedPaymentMethods, _) => { + let defaultPaymentMethod = + savedPaymentMethods->Array.find(savedCard => savedCard.defaultPaymentMethodSet) + + let savedCardsWithoutDefaultPaymentMethod = savedPaymentMethods->Array.filter(savedCard => { + !savedCard.defaultPaymentMethodSet + }) + + let finalSavedPaymentMethods = switch defaultPaymentMethod { + | Some(defaultPaymentMethod) => + [defaultPaymentMethod]->Array.concat(savedCardsWithoutDefaultPaymentMethod) + | None => savedCardsWithoutDefaultPaymentMethod + } + + setSavedMethods(_ => finalSavedPaymentMethods) + setIsLoading(_ => false) + } + | LoadingSavedCards => setIsLoading(_ => true) + | NoResult(_) => setIsLoading(_ => false) + } + + None + }, (savedPaymentMethods, displaySavedPaymentMethods)) + + <> + + + + + + + + +} + +let default = make diff --git a/src/PaymentManagementLazy.res b/src/PaymentManagementLazy.res new file mode 100644 index 000000000..0f893d208 --- /dev/null +++ b/src/PaymentManagementLazy.res @@ -0,0 +1,4 @@ +open LazyUtils + +type props = {} +let make: props => React.element = reactLazy(() => import_("./PaymentManagement.bs.js")) diff --git a/src/Payments/PreMountLoader.res b/src/Payments/PreMountLoader.res index c22273856..305ebb686 100644 --- a/src/Payments/PreMountLoader.res +++ b/src/Payments/PreMountLoader.res @@ -1,11 +1,21 @@ @react.component -let make = (~sessionId, ~publishableKey, ~clientSecret, ~endpoint) => { +let make = ( + ~sessionId, + ~publishableKey, + ~clientSecret, + ~endpoint, + ~ephemeralKey, + ~hyperComponentName: Types.hyperComponentName, +) => { open Utils let (paymentMethodsResponseSent, setPaymentMethodsResponseSent) = React.useState(_ => false) let ( customerPaymentMethodsResponseSent, setCustomerPaymentMethodsResponseSent, ) = React.useState(_ => false) + let (savedPaymentMethodsResponseSent, setSavedPaymentMethodsResponseSent) = React.useState(_ => + false + ) let (sessionTokensResponseSent, setSessionTokensResponseSent) = React.useState(_ => false) let logger = OrcaLogger.make( ~sessionId, @@ -15,36 +25,67 @@ let make = (~sessionId, ~publishableKey, ~clientSecret, ~endpoint) => { (), ) - let paymentMethodsResponse = React.useMemo0(() => - PaymentHelpers.fetchPaymentMethodList( - ~clientSecret, - ~publishableKey, - ~logger, - ~switchToCustomPod=false, - ~endpoint, - ) - ) + let ( + paymentMethodsResponse, + customerPaymentMethodsResponse, + sessionTokensResponse, + savedPaymentMethodsResponse, + ) = React.useMemo0(() => { + let paymentMethodsResponse = switch hyperComponentName { + | Elements => + PaymentHelpers.fetchPaymentMethodList( + ~clientSecret, + ~publishableKey, + ~logger, + ~switchToCustomPod=false, + ~endpoint, + ) + | _ => JSON.Encode.null->Promise.resolve + } - let customerPaymentMethodsResponse = React.useMemo0(() => - PaymentHelpers.fetchCustomerPaymentMethodList( - ~clientSecret, - ~publishableKey, - ~optLogger=Some(logger), - ~switchToCustomPod=false, - ~endpoint, - ) - ) + let customerPaymentMethodsResponse = switch hyperComponentName { + | Elements => + PaymentHelpers.fetchCustomerPaymentMethodList( + ~clientSecret, + ~publishableKey, + ~optLogger=Some(logger), + ~switchToCustomPod=false, + ~endpoint, + ) + | _ => JSON.Encode.null->Promise.resolve + } - let sessionTokensResponse = React.useMemo0(() => - PaymentHelpers.fetchSessions( - ~clientSecret, - ~publishableKey, - ~optLogger=Some(logger), - ~switchToCustomPod=false, - ~endpoint, - (), + let sessionTokensResponse = switch hyperComponentName { + | Elements => + PaymentHelpers.fetchSessions( + ~clientSecret, + ~publishableKey, + ~optLogger=Some(logger), + ~switchToCustomPod=false, + ~endpoint, + (), + ) + | _ => JSON.Encode.null->Promise.resolve + } + + let savedPaymentMethodsResponse = switch hyperComponentName { + | PaymentMethodsManagementElements => + PaymentHelpers.fetchSavedPaymentMethodList( + ~ephemeralKey, + ~optLogger=Some(logger), + ~switchToCustomPod=false, + ~endpoint, + ) + | _ => JSON.Encode.null->Promise.resolve + } + + ( + paymentMethodsResponse, + customerPaymentMethodsResponse, + sessionTokensResponse, + savedPaymentMethodsResponse, ) - ) + }) let sendPromiseData = (promise, key) => { open Promise @@ -55,6 +96,7 @@ let make = (~sessionId, ~publishableKey, ~clientSecret, ~endpoint) => { | "payment_methods" => setPaymentMethodsResponseSent(_ => true) | "session_tokens" => setSessionTokensResponseSent(_ => true) | "customer_payment_methods" => setCustomerPaymentMethodsResponseSent(_ => true) + | "saved_payment_methods" => setSavedPaymentMethodsResponseSent(_ => true) | _ => () } resolve() @@ -79,6 +121,8 @@ let make = (~sessionId, ~publishableKey, ~clientSecret, ~endpoint) => { customerPaymentMethodsResponse->sendPromiseData("customer_payment_methods") } else if dict->Dict.get("sendSessionTokensResponse")->Option.isSome { sessionTokensResponse->sendPromiseData("session_tokens") + } else if dict->Dict.get("sendSavedPaymentMethodsResponse")->Belt.Option.isSome { + savedPaymentMethodsResponse->sendPromiseData("saved_payment_methods") } } @@ -92,15 +136,23 @@ let make = (~sessionId, ~publishableKey, ~clientSecret, ~endpoint) => { ) }) - React.useEffect3(() => { + React.useEffect4(() => { if ( - paymentMethodsResponseSent && customerPaymentMethodsResponseSent && sessionTokensResponseSent + paymentMethodsResponseSent && + customerPaymentMethodsResponseSent && + sessionTokensResponseSent && + savedPaymentMethodsResponseSent ) { handlePostMessage([("preMountLoaderIframeUnMount", true->JSON.Encode.bool)]) Window.removeEventListener("message", handle) } None - }, (paymentMethodsResponseSent, customerPaymentMethodsResponseSent, sessionTokensResponseSent)) + }, ( + paymentMethodsResponseSent, + customerPaymentMethodsResponseSent, + sessionTokensResponseSent, + savedPaymentMethodsResponseSent, + )) React.null } diff --git a/src/RenderPaymentMethods.res b/src/RenderPaymentMethods.res index 1207cbe1d..d02c2bbfe 100644 --- a/src/RenderPaymentMethods.res +++ b/src/RenderPaymentMethods.res @@ -142,6 +142,13 @@ let make = ( id="card-cvc" isFocus /> + | PaymentMethodsManagement => + + + }> + + | PaymentMethodCollectElement | NONE => React.null }} diff --git a/src/Types/CardThemeType.res b/src/Types/CardThemeType.res index 0ed4a76fd..ed526c19f 100644 --- a/src/Types/CardThemeType.res +++ b/src/Types/CardThemeType.res @@ -17,6 +17,7 @@ type mode = | ApplePayElement | KlarnaElement | ExpressCheckoutElement + | PaymentMethodsManagement | NONE type label = Above | Floating | Never type themeClass = { @@ -86,6 +87,7 @@ type fonts = { type configClass = { appearance: appearance, locale: string, + ephemeralKey: string, clientSecret: string, fonts: array, loader: showLoader, @@ -104,6 +106,7 @@ let getPaymentMode = val => { | "paymentMethodCollect" => PaymentMethodCollectElement | "klarna" => KlarnaElement | "expressCheckout" => ExpressCheckoutElement + | "paymentMethodsManagement" => PaymentMethodsManagement | _ => NONE } } @@ -121,6 +124,7 @@ let getPaymentModeToStrMapper = val => { | PaymentMethodCollectElement => "PaymentMethodCollectElement" | KlarnaElement => "KlarnaElement" | ExpressCheckoutElement => "ExpressCheckoutElement" + | PaymentMethodsManagement => "PaymentMethodsManagement" | NONE => "None" } } diff --git a/src/Types/PaymentType.res b/src/Types/PaymentType.res index 0f682e839..899f06583 100644 --- a/src/Types/PaymentType.res +++ b/src/Types/PaymentType.res @@ -152,6 +152,7 @@ type options = { layout: layoutType, business: business, customerPaymentMethods: savedCardsLoadState, + savedPaymentMethods: savedCardsLoadState, paymentMethodOrder: option>, displaySavedPaymentMethodsCheckbox: bool, displaySavedPaymentMethods: bool, @@ -289,6 +290,7 @@ let defaultOptions = { defaultValues: defaultDefaultValues, business: defaultBusiness, customerPaymentMethods: LoadingSavedCards, + savedPaymentMethods: LoadingSavedCards, layout: ObjectLayout(defaultLayout), paymentMethodOrder: None, fields: defaultFields, @@ -889,10 +891,10 @@ let itemToCustomerObjMapper = customerDict => { (customerPaymentMethods, isGuestCustomer) } -let createCustomerObjArr = dict => { +let createCustomerObjArr = (dict, key) => { let customerDict = dict - ->Dict.get("customerPaymentMethods") + ->Dict.get(key) ->Option.flatMap(JSON.Decode.object) ->Option.getOr(Dict.make()) let (customerPaymentMethods, isGuestCustomer) = customerDict->itemToCustomerObjMapper @@ -1013,6 +1015,7 @@ let itemToObjMapper = (dict, logger) => { business: getBusiness(dict, "business", logger), layout: getLayout(dict, "layout", logger), customerPaymentMethods: getCustomerMethods(dict, "customerPaymentMethods"), + savedPaymentMethods: getCustomerMethods(dict, "customerPaymentMethods"), paymentMethodOrder: getOptionalStrArray(dict, "paymentMethodOrder"), fields: getFields(dict, "fields", logger), branding: getWarningString(dict, "branding", "auto", ~logger)->getShowType( diff --git a/src/Utilities/PaymentHelpers.res b/src/Utilities/PaymentHelpers.res index 5352d7a1b..9a3427104 100644 --- a/src/Utilities/PaymentHelpers.res +++ b/src/Utilities/PaymentHelpers.res @@ -1901,7 +1901,9 @@ let callAuthExchange = ( setOptionValue( prev => { ...prev, - customerPaymentMethods: createCustomerObjArr(customerListResponse), + customerPaymentMethods: customerListResponse->createCustomerObjArr( + "customerPaymentMethods", + ), }, ) JSON.Encode.null->resolve @@ -1930,3 +1932,150 @@ let callAuthExchange = ( JSON.Encode.null->resolve }) } + +let fetchSavedPaymentMethodList = ( + ~ephemeralKey, + ~endpoint, + ~optLogger, + ~switchToCustomPod, + ~isPaymentSession=false, +) => { + open Promise + let headers = [("Content-Type", "application/json"), ("api-key", ephemeralKey)] + let uri = `${endpoint}/customers/payment_methods` + logApi( + ~optLogger, + ~url=uri, + ~apiLogType=Request, + ~eventName=SAVED_PAYMENT_METHODS_CALL_INIT, + ~logType=INFO, + ~logCategory=API, + ~isPaymentSession, + (), + ) + fetchApi( + uri, + ~method=#GET, + ~headers=headers->ApiEndpoint.addCustomPodHeader(~switchToCustomPod, ()), + (), + ) + ->then(res => { + let statusCode = res->Fetch.Response.status->Int.toString + if statusCode->String.charAt(0) !== "2" { + res + ->Fetch.Response.json + ->then(data => { + logApi( + ~optLogger, + ~url=uri, + ~data, + ~statusCode, + ~apiLogType=Err, + ~eventName=CUSTOMER_PAYMENT_METHODS_CALL, + ~logType=ERROR, + ~logCategory=API, + ~isPaymentSession, + (), + ) + JSON.Encode.null->resolve + }) + } else { + logApi( + ~optLogger, + ~url=uri, + ~statusCode, + ~apiLogType=Response, + ~eventName=CUSTOMER_PAYMENT_METHODS_CALL, + ~logType=INFO, + ~logCategory=API, + ~isPaymentSession, + (), + ) + res->Fetch.Response.json + } + }) + ->catch(err => { + let exceptionMessage = err->formatException + logApi( + ~optLogger, + ~url=uri, + ~apiLogType=NoResponse, + ~eventName=CUSTOMER_PAYMENT_METHODS_CALL, + ~logType=ERROR, + ~logCategory=API, + ~data=exceptionMessage, + ~isPaymentSession, + (), + ) + JSON.Encode.null->resolve + }) +} + +let deletePaymentMethod = (~ephemeralKey, ~paymentMethodId, ~logger, ~switchToCustomPod) => { + open Promise + let endpoint = ApiEndpoint.getApiEndPoint() + let headers = [("Content-Type", "application/json"), ("api-key", ephemeralKey)] + let uri = `${endpoint}/payment_methods/${paymentMethodId}` + logApi( + ~optLogger=Some(logger), + ~url=uri, + ~apiLogType=Request, + ~eventName=DELETE_PAYMENT_METHODS_CALL_INIT, + ~logType=INFO, + ~logCategory=API, + (), + ) + fetchApi( + uri, + ~method=#DELETE, + ~headers=headers->ApiEndpoint.addCustomPodHeader(~switchToCustomPod, ()), + (), + ) + ->then(resp => { + let statusCode = resp->Fetch.Response.status->Int.toString + if statusCode->String.charAt(0) !== "2" { + resp + ->Fetch.Response.json + ->then(data => { + logApi( + ~optLogger=Some(logger), + ~url=uri, + ~data, + ~statusCode, + ~apiLogType=Err, + ~eventName=SAVED_PAYMENT_METHODS_CALL, + ~logType=ERROR, + ~logCategory=API, + (), + ) + JSON.Encode.null->resolve + }) + } else { + logApi( + ~optLogger=Some(logger), + ~url=uri, + ~statusCode, + ~apiLogType=Response, + ~eventName=SAVED_PAYMENT_METHODS_CALL, + ~logType=INFO, + ~logCategory=API, + (), + ) + Fetch.Response.json(resp) + } + }) + ->catch(err => { + let exceptionMessage = err->formatException + logApi( + ~optLogger=Some(logger), + ~url=uri, + ~apiLogType=NoResponse, + ~eventName=SAVED_PAYMENT_METHODS_CALL, + ~logType=ERROR, + ~logCategory=API, + ~data=exceptionMessage, + (), + ) + JSON.Encode.null->resolve + }) +} diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res index c3e79a6c2..663020fa2 100644 --- a/src/Utilities/Utils.res +++ b/src/Utilities/Utils.res @@ -1241,7 +1241,9 @@ let expressCheckoutComponents = ["googlePay", "payPal", "applePay", "klarna", "e let spmComponents = ["paymentMethodCollect"]->Array.concat(expressCheckoutComponents) let componentsForPaymentElementCreate = - ["payment", "paymentMethodCollect"]->Array.concat(expressCheckoutComponents) + ["payment", "paymentMethodCollect", "paymentMethodsManagement"]->Array.concat( + expressCheckoutComponents, + ) let getIsExpressCheckoutComponent = componentType => { expressCheckoutComponents->Array.includes(componentType) diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res index 261075ea0..c69129e25 100644 --- a/src/orca-loader/Elements.res +++ b/src/orca-loader/Elements.res @@ -27,6 +27,7 @@ let make = ( let logger = logger->Option.getOr(OrcaLogger.defaultLoggerConfig) let savedPaymentElement = Dict.make() let localOptions = options->JSON.Decode.object->Option.getOr(Dict.make()) + let endpoint = ApiEndpoint.getApiEndPoint(~publishableKey, ()) let redirect = ref("if_required") @@ -278,6 +279,7 @@ let make = ( | "applePay" | "klarna" | "expressCheckout" + | "paymentMethodsManagement" | "payment" => () | str => manageErrorWarning(UNKNOWN_KEY, ~dynamicStr=`${str} type in create`, ~logger, ()) } diff --git a/src/orca-loader/Hyper.res b/src/orca-loader/Hyper.res index 8fff0138a..58e66940e 100644 --- a/src/orca-loader/Hyper.res +++ b/src/orca-loader/Hyper.res @@ -206,6 +206,7 @@ let make = (publishableKey, options: option, analyticsInfo: option { iframeRef.contents->Array.push(ref)->ignore } @@ -428,6 +429,56 @@ let make = (publishableKey, options: option, analyticsInfo: optiongetString("customBackendUrl", ""), ) } + + let paymentMethodsManagementElements = paymentMethodsManagementElementsOptions => { + open Promise + let paymentMethodsManagementElementsOptionsDict = + paymentMethodsManagementElementsOptions->JSON.Decode.object + paymentMethodsManagementElementsOptionsDict + ->Option.forEach(x => x->Dict.set("launchTime", Date.now()->JSON.Encode.float)) + ->ignore + + let ephemeralKeyId = + paymentMethodsManagementElementsOptionsDict + ->Option.flatMap(x => x->Dict.get("ephemeralKey")) + ->Option.flatMap(JSON.Decode.string) + ->Option.getOr("") + + let paymentMethodsManagementElementsOptions = + paymentMethodsManagementElementsOptionsDict->Option.mapOr( + paymentMethodsManagementElementsOptions, + JSON.Encode.object, + ) + ephemeralKey := ephemeralKeyId + Promise.make((resolve, _) => { + logger.setEphemeralKey(ephemeralKeyId) + resolve(JSON.Encode.null) + }) + ->then(_ => { + logger.setLogInfo( + ~value=Window.hrefWithoutSearch, + ~eventName=PAYMENT_MANAGEMENT_ELEMENTS_CALLED, + (), + ) + resolve() + }) + ->ignore + + PaymentMethodsManagementElements.make( + paymentMethodsManagementElementsOptions, + setIframeRef, + ~sdkSessionId=sessionID, + ~publishableKey, + ~ephemeralKey={ephemeralKeyId}, + ~logger=Some(logger), + ~analyticsMetadata, + ~customBackendUrl=options + ->Option.getOr(JSON.Encode.null) + ->getDictFromJson + ->getString("customBackendUrl", ""), + ) + } + let confirmCardPaymentFn = ( clientSecretId: string, data: option, @@ -563,6 +614,7 @@ let make = (publishableKey, options: option, analyticsInfo: option, analyticsInfo: option Promise.t<'a> = "writeText" -let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) => { +let make = ( + componentType, + options, + setIframeRef, + iframeRef, + mountPostMessage, + ~isPaymentManagementElement=false, +) => { try { let mountId = ref("") let setPaymentIframeRef = ref => { setIframeRef(ref) } + let (elementIframeWrapperDivId, elementIframeId) = if isPaymentManagementElement { + ("management-element", "payment-methods-management-element") + } else { + ("element", "payment-element") + } + let sdkHandleOneClickConfirmPayment = options->getDecodedBoolFromJson( callbackFuncForExtractingValFromDict("sdkHandleOneClickConfirmPayment"), @@ -155,7 +168,7 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = iframeHeight->Option.getOr(JSON.Encode.null)->Utils.getFloatFromJson(200.0) if iframeId === localSelectorString { let elem = Window.querySelector( - `#orca-payment-element-iframeRef-${localSelectorString}`, + `#orca-${elementIframeId}-iframeRef-${localSelectorString}`, ) switch elem->Nullable.toOption { | Some(ele) => @@ -205,7 +218,9 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = | None => () } if id == localSelectorString { - let elem = Window.querySelector(`#orca-element-${localSelectorString}`) + let elem = Window.querySelector( + `#orca-${elementIframeWrapperDivId}-${localSelectorString}`, + ) switch elem->Nullable.toOption { | Some(ele) => ele->Window.className(currentClass.contents) | None => () @@ -236,7 +251,7 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = | Some(ele) => ele->Window.innerHTML("") let mainElement = Window.querySelector( - `#orca-payment-element-iframeRef-${localSelectorString}`, + `#orca-${elementIframeId}-iframeRef-${localSelectorString}`, ) let iframeURL = fullscreenParam.contents != "" @@ -291,7 +306,7 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = if iframeMounted->Option.isSome { mountPostMessage( - Window.querySelector(`#orca-payment-element-iframeRef-${localSelectorString}`), + Window.querySelector(`#orca-${elementIframeId}-iframeRef-${localSelectorString}`), localSelectorString, sdkHandleOneClickConfirmPayment, ) @@ -305,11 +320,11 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = componentType->Utils.isOtherElements ? "height: 2rem;" : "height: 0;" switch oElement->Nullable.toOption { | Some(elem) => { - let iframeDiv = `
+ let iframeDiv = `
+
` + 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 preMountLoaderIframeDiv = mountPreMountLoaderIframe() + + let unMountPreMountLoaderIframe = () => { + switch preMountLoaderIframeDiv->Nullable.toOption { + | Some(iframe) => iframe->remove + | None => () + } + } + + let preMountLoaderMountedPromise = Promise.make((resolve, _reject) => { + let preMountLoaderIframeCallback = (ev: Types.event) => { + let json = ev.data->Identity.anyTypeToJson + let dict = json->getDictFromJson + if dict->Dict.get("preMountLoaderIframeMountedCallback")->Option.isSome { + resolve(true->JSON.Encode.bool) + } else if dict->Dict.get("preMountLoaderIframeUnMount")->Option.isSome { + unMountPreMountLoaderIframe() + } + } + addSmartEventListener( + "message", + preMountLoaderIframeCallback, + "onPreMountLoaderIframeCallback", + ) + }) + + let fetchSavedPaymentMethods = (mountedIframeRef, disableSaveCards, componentType) => { + if !disableSaveCards { + let handleSavedPaymentMethodsLoaded = (event: Types.event) => { + let json = event.data->Identity.anyTypeToJson + let dict = json->getDictFromJson + let isSavedPaymentMethodsData = dict->getString("data", "") === "saved_payment_methods" + if isSavedPaymentMethodsData { + let json = dict->getJsonFromDict("response", JSON.Encode.null) + let msg = [("savedPaymentMethods", json)]->Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + } + } + addSmartEventListener( + "message", + handleSavedPaymentMethodsLoaded, + `onSavedPaymentMethodsLoaded-${componentType}`, + ) + } + let msg = + [("sendSavedPaymentMethodsResponse", !disableSaveCards->JSON.Encode.bool)]->Dict.fromArray + preMountLoaderIframeDiv->Window.iframePostMessage(msg) + } + + let setElementIframeRef = ref => { + iframeRef->Array.push(ref)->ignore + setIframeRef(ref) + } + let getElement = componentName => { + savedPaymentElement->Dict.get(componentName) + } + let update = newOptions => { + let newOptionsDict = newOptions->getDictFromJson + switch newOptionsDict->Dict.get("locale") { + | Some(val) => localOptions->Dict.set("locale", val) + | None => () + } + switch newOptionsDict->Dict.get("appearance") { + | Some(val) => localOptions->Dict.set("appearance", val) + | None => () + } + + iframeRef->Array.forEach(iframe => { + let message = + [ + ("ElementsUpdate", true->JSON.Encode.bool), + ("options", newOptionsDict->JSON.Encode.object), + ]->Dict.fromArray + iframe->Window.iframePostMessage(message) + }) + } + let fetchUpdates = () => { + Promise.make((resolve, _) => { + setTimeout(() => resolve(Dict.make()->JSON.Encode.object), 1000)->ignore + }) + } + + let create = (componentType, newOptions) => { + componentType == "" + ? manageErrorWarning(REQUIRED_PARAMETER, ~dynamicStr="type", ~logger, ()) + : () + let otherElements = componentType->isOtherElements + switch componentType { + | "paymentMethodsManagement" => () + | str => manageErrorWarning(UNKNOWN_KEY, ~dynamicStr=`${str} type in create`, ~logger, ()) + } + + let mountPostMessage = ( + mountedIframeRef, + selectorString, + sdkHandleOneClickConfirmPayment, + ) => { + open Promise + + let widgetOptions = + [ + ("ephemeralKey", ephemeralKey->JSON.Encode.string), + ("appearance", appearance), + ("locale", locale), + ("loader", loader), + ("fonts", fonts), + ]->getJsonFromArrayOfJson + let message = + [ + ( + "paymentElementCreate", + componentType->getIsComponentTypeForPaymentElementCreate->JSON.Encode.bool, + ), + ("otherElements", otherElements->JSON.Encode.bool), + ("options", newOptions), + ("componentType", componentType->JSON.Encode.string), + ("paymentOptions", widgetOptions), + ("iframeId", selectorString->JSON.Encode.string), + ("publishableKey", publishableKey->JSON.Encode.string), + ("endpoint", endpoint->JSON.Encode.string), + ("sdkSessionId", sdkSessionId->JSON.Encode.string), + ("blockConfirm", blockConfirm->JSON.Encode.bool), + ("switchToCustomPod", switchToCustomPod->JSON.Encode.bool), + ("sdkHandleOneClickConfirmPayment", sdkHandleOneClickConfirmPayment->JSON.Encode.bool), + ("parentURL", "*"->JSON.Encode.string), + ("analyticsMetadata", analyticsMetadata), + ("launchTime", launchTime->JSON.Encode.float), + ("customBackendUrl", customBackendUrl->JSON.Encode.string), + ]->Dict.fromArray + + preMountLoaderMountedPromise + ->then(_ => { + let disableSavedPaymentMethods = + newOptions + ->getDictFromJson + ->getBool("displaySavedPaymentMethods", true) + if ( + disableSavedPaymentMethods && + !(expressCheckoutComponents->Array.includes(componentType)) + ) { + fetchSavedPaymentMethods(mountedIframeRef, false, componentType) + } + resolve() + }) + ->ignore + mountedIframeRef->Window.iframePostMessage(message) + } + + let paymentElement = LoaderPaymentElement.make( + componentType, + newOptions, + setElementIframeRef, + iframeRef, + mountPostMessage, + ~isPaymentManagementElement=true, + ) + savedPaymentElement->Dict.set(componentType, paymentElement) + paymentElement + } + { + getElement, + update, + fetchUpdates, + create, + } + } catch { + | e => { + Sentry.captureException(e) + defaultElement + } + } +} diff --git a/src/orca-loader/PaymentSession.res b/src/orca-loader/PaymentSession.res index 4dfd93f67..a87e7a31b 100644 --- a/src/orca-loader/PaymentSession.res +++ b/src/orca-loader/PaymentSession.res @@ -1,6 +1,12 @@ open Types -let make = (options, ~clientSecret, ~publishableKey, ~logger: option) => { +let make = ( + options, + ~clientSecret, + ~publishableKey, + ~logger: option, + ~ephemeralKey, +) => { let logger = logger->Option.getOr(OrcaLogger.defaultLoggerConfig) let switchToCustomPod = GlobalVars.isInteg && @@ -20,6 +26,13 @@ let make = (options, ~clientSecret, ~publishableKey, ~logger: option + PaymentSessionMethods.getPaymentManagementMethods( + ~ephemeralKey, + ~logger, + ~switchToCustomPod, + ~endpoint, + ), } defaultInitPaymentSession diff --git a/src/orca-loader/PaymentSessionMethods.res b/src/orca-loader/PaymentSessionMethods.res index a0e920454..e067c65e5 100644 --- a/src/orca-loader/PaymentSessionMethods.res +++ b/src/orca-loader/PaymentSessionMethods.res @@ -1,3 +1,7 @@ +open Promise +open Types +open Utils + let getCustomerSavedPaymentMethods = ( ~clientSecret, ~publishableKey, @@ -5,9 +9,6 @@ let getCustomerSavedPaymentMethods = ( ~logger, ~switchToCustomPod, ) => { - open Promise - open Types - open Utils open ApplePayTypes open GooglePayType let applePaySessionRef = ref(Nullable.null) @@ -463,3 +464,44 @@ let getCustomerSavedPaymentMethods = ( handleFailureResponse(~message=exceptionMessage, ~errorType="server_error")->resolve }) } + +let getPaymentManagementMethods = (~ephemeralKey, ~logger, ~switchToCustomPod, ~endpoint) => { + let getSavedPaymentManagementMethodsList = _ => { + PaymentHelpers.fetchSavedPaymentMethodList( + ~ephemeralKey, + ~optLogger=Some(logger), + ~switchToCustomPod=false, + ~endpoint, + ) + ->then(response => { + response->resolve + }) + ->catch(err => { + let exceptionMessage = err->formatException->JSON.stringify + handleFailureResponse(~message=exceptionMessage, ~errorType="server_error")->resolve + }) + } + + let deleteSavedPaymentMethod = paymentMethodId => { + PaymentHelpers.deletePaymentMethod( + ~ephemeralKey, + ~paymentMethodId={paymentMethodId->JSON.Decode.string->Option.getOr("")}, + ~logger, + ~switchToCustomPod, + ) + ->then(response => { + response->resolve + }) + ->catch(err => { + let exceptionMessage = err->formatException->JSON.stringify + handleFailureResponse(~message=exceptionMessage, ~errorType="server_error")->resolve + }) + } + + { + getSavedPaymentManagementMethodsList, + deleteSavedPaymentMethod, + } + ->Identity.anyTypeToJson + ->resolve +} diff --git a/src/orca-loader/Types.res b/src/orca-loader/Types.res index 8d396c99a..6c5577d31 100644 --- a/src/orca-loader/Types.res +++ b/src/orca-loader/Types.res @@ -46,7 +46,15 @@ type getCustomerSavedPaymentMethods = { confirmWithLastUsedPaymentMethod: JSON.t => Promise.t, } -type initPaymentSession = {getCustomerSavedPaymentMethods: unit => Promise.t} +type getPaymentManagementMethods = { + getSavedPaymentManagementMethodsList: unit => Promise.t, + deleteSavedPaymentMethod: JSON.t => Promise.t, +} + +type initPaymentSession = { + getCustomerSavedPaymentMethods: unit => Promise.t, + getPaymentManagementMethods: unit => Promise.t, +} type confirmParams = {return_url: string} @@ -64,6 +72,7 @@ type hyperInstance = { widgets: JSON.t => element, paymentRequest: JSON.t => JSON.t, initPaymentSession: JSON.t => initPaymentSession, + paymentMethodsManagementElements: JSON.t => element, } let oneClickConfirmPaymentFn = (_, _) => { @@ -124,6 +133,14 @@ let getCustomerDefaultSavedPaymentMethodData = () => { JSON.Encode.null } +let getSavedPaymentManagementMethodsList = () => { + JSON.Encode.null +} + +let deleteSavedPaymentMethod = () => { + JSON.Encode.null +} + let getCustomerLastUsedPaymentMethodData = () => { JSON.Encode.null } @@ -145,8 +162,13 @@ let defaultGetCustomerSavedPaymentMethods = () => { Promise.resolve(JSON.Encode.null) } +let defaultGetPaymentManagementMethods = () => { + Promise.resolve(JSON.Encode.null) +} + let defaultInitPaymentSession: initPaymentSession = { getCustomerSavedPaymentMethods: defaultGetCustomerSavedPaymentMethods, + getPaymentManagementMethods: defaultGetPaymentManagementMethods, } let defaultHyperInstance = { @@ -158,6 +180,7 @@ let defaultHyperInstance = { widgets: _ev => defaultElement, paymentRequest: _ev => JSON.Encode.null, initPaymentSession: _ev => defaultInitPaymentSession, + paymentMethodsManagementElements: _ev => defaultElement, } type eventType = @@ -193,3 +216,20 @@ type rec ele = { @scope("document") @val external createElement: string => ele = "createElement" @send external appendChild: (Dom.element, ele) => unit = "appendChild" + +type hyperComponentName = Elements | PaymentMethodsManagementElements + +let getStrFromHyperComponentName = hyperComponentName => { + switch hyperComponentName { + | Elements => "Elements" + | PaymentMethodsManagementElements => "PaymentMethodsManagementElements" + } +} + +let getHyperComponentNameFromStr = hyperComponentName => { + switch hyperComponentName { + | "Elements" => Elements + | "PaymentMethodsManagementElements" => PaymentMethodsManagementElements + | _ => Elements + } +} diff --git a/src/orca-log-catcher/ErrorBoundary.res b/src/orca-log-catcher/ErrorBoundary.res index 12661cf98..e32213282 100644 --- a/src/orca-log-catcher/ErrorBoundary.res +++ b/src/orca-log-catcher/ErrorBoundary.res @@ -156,6 +156,7 @@ module ErrorCard = { paymentMethod: "", firstEvent: false, metadata: JSON.Encode.null, + ephemeralKey: "", } beaconApiCall([errorLog]) } diff --git a/src/orca-log-catcher/OrcaLogger.res b/src/orca-log-catcher/OrcaLogger.res index 9ad920b36..f871d077c 100644 --- a/src/orca-log-catcher/OrcaLogger.res +++ b/src/orca-log-catcher/OrcaLogger.res @@ -21,7 +21,9 @@ type eventName = | SESSIONS_CALL_INIT | SESSIONS_CALL | PAYMENT_METHODS_CALL_INIT + | SAVED_PAYMENT_METHODS_CALL_INIT | PAYMENT_METHODS_CALL + | SAVED_PAYMENT_METHODS_CALL | CUSTOMER_PAYMENT_METHODS_CALL_INIT | CUSTOMER_PAYMENT_METHODS_CALL | CREATE_CUSTOMER_PAYMENT_METHODS_CALL_INIT @@ -77,6 +79,9 @@ type eventName = | PAYMENT_METHODS_AUTH_EXCHANGE_CALL | PAYMENT_METHODS_AUTH_LINK_CALL_INIT | PAYMENT_METHODS_AUTH_LINK_CALL + | PAYMENT_MANAGEMENT_ELEMENTS_CALLED + | DELETE_SAVED_PAYMENT_METHOD + | DELETE_PAYMENT_METHODS_CALL_INIT let eventNameToStrMapper = eventName => { switch eventName { @@ -154,6 +159,11 @@ let eventNameToStrMapper = eventName => { | PAYMENT_METHODS_AUTH_LINK_CALL => "PAYMENT_METHODS_AUTH_LINK_CALL" | PAYMENT_METHODS_AUTH_EXCHANGE_CALL_INIT => "PAYMENT_METHODS_AUTH_EXCHANGE_CALL_INIT" | PAYMENT_METHODS_AUTH_LINK_CALL_INIT => "PAYMENT_METHODS_AUTH_LINK_CALL_INIT" + | SAVED_PAYMENT_METHODS_CALL => "SAVED_PAYMENT_METHODS_CALL" + | SAVED_PAYMENT_METHODS_CALL_INIT => "SAVED_PAYMENT_METHODS_CALL_INIT" + | PAYMENT_MANAGEMENT_ELEMENTS_CALLED => "PAYMENT_MANAGEMENT_ELEMENTS_CALLED" + | DELETE_SAVED_PAYMENT_METHOD => "DELETE_SAVED_PAYMENT_METHOD" + | DELETE_PAYMENT_METHODS_CALL_INIT => "DELETE_PAYMENT_METHODS_CALL_INIT" } } @@ -194,6 +204,7 @@ type logFile = { firstEvent: bool, paymentMethod: string, metadata: JSON.t, + ephemeralKey: string, } type setlogApiValueType = @@ -235,11 +246,13 @@ type loggerMake = { setMerchantId: string => unit, setMetadata: JSON.t => unit, setSource: string => unit, + setEphemeralKey: string => unit, } let defaultLoggerConfig = { sendLogs: () => (), setClientSecret: _x => (), + setEphemeralKey: _x => (), setConfirmPaymentValue: (~paymentType as _) => {Dict.make()->JSON.Encode.object}, setLogError: ( ~value as _, @@ -466,7 +479,15 @@ let browserDetect = content => { let arrayOfNameAndVersion = String.split(Window.userAgent->browserDetect, "-") -let make = (~sessionId=?, ~source: source, ~clientSecret=?, ~merchantId=?, ~metadata=?, ()) => { +let make = ( + ~sessionId=?, + ~source: source, + ~clientSecret=?, + ~merchantId=?, + ~metadata=?, + ~ephemeralKey=?, + (), +) => { let loggingLevel = switch GlobalVars.loggingLevelStr { | "DEBUG" => DEBUG | "INFO" => INFO @@ -540,10 +561,16 @@ let make = (~sessionId=?, ~source: source, ~clientSecret=?, ~merchantId=?, ~meta } let clientSecret = getRefFromOption(clientSecret) + let ephemeralKey = getRefFromOption(ephemeralKey) + let setClientSecret = value => { clientSecret := value } + let setEphemeralKey = value => { + ephemeralKey := value + } + let sourceRef = ref(source->getSourceString) let setSource = value => { @@ -682,6 +709,7 @@ let make = (~sessionId=?, ~source: source, ~clientSecret=?, ~merchantId=?, ~meta paymentMethod, firstEvent, metadata: metadata.contents, + ephemeralKey: ephemeralKey.contents, } ->conditionalLogPush ->ignore @@ -739,6 +767,7 @@ let make = (~sessionId=?, ~source: source, ~clientSecret=?, ~merchantId=?, ~meta paymentMethod, firstEvent, metadata: metadata.contents, + ephemeralKey: ephemeralKey.contents, } ->conditionalLogPush ->ignore @@ -786,6 +815,7 @@ let make = (~sessionId=?, ~source: source, ~clientSecret=?, ~merchantId=?, ~meta paymentMethod, firstEvent, metadata: metadata.contents, + ephemeralKey: ephemeralKey.contents, } ->conditionalLogPush ->ignore @@ -819,6 +849,7 @@ let make = (~sessionId=?, ~source: source, ~clientSecret=?, ~merchantId=?, ~meta paymentMethod: "", firstEvent, metadata: metadata.contents, + ephemeralKey: ephemeralKey.contents, } ->conditionalLogPush ->ignore @@ -848,5 +879,6 @@ let make = (~sessionId=?, ~source: source, ~clientSecret=?, ~merchantId=?, ~meta setLogApi, setLogError, setSource, + setEphemeralKey, } }