diff --git a/src/LoaderController.res b/src/LoaderController.res index 9834fa089..16ff0495c 100644 --- a/src/LoaderController.res +++ b/src/LoaderController.res @@ -13,6 +13,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime let (optionsPayment, setOptionsPayment) = Recoil.useRecoilState(optionAtom) let setPaymentManagementList = Recoil.useSetRecoilState(paymentManagementList) let setPaymentMethodsListV2 = Recoil.useSetRecoilState(paymentMethodsListV2) + let setIntentList = Recoil.useSetRecoilState(intentList) let setSessionId = Recoil.useSetRecoilState(sessionId) let setBlockConfirm = Recoil.useSetRecoilState(isConfirmBlocked) let setCustomPodUri = Recoil.useSetRecoilState(customPodUri) @@ -444,6 +445,11 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime } } } + if dict->getDictIsSome("getIntent") { + let getIntentDict = dict->getJsonObjectFromDict("getIntent")->getDictFromJson + let intentDetails = getIntentDict->UnifiedHelpersV2.createIntentDetails("response") + setIntentList(_ => intentDetails) + } if dict->getDictIsSome("paymentsListV2") { let paymentsListV2 = dict->getJsonObjectFromDict("paymentsListV2") let listDict = paymentsListV2->getDictFromJson diff --git a/src/Payments/PreMountLoader.res b/src/Payments/PreMountLoader.res index 4b1f6c422..b5b8d14cb 100644 --- a/src/Payments/PreMountLoader.res +++ b/src/Payments/PreMountLoader.res @@ -130,6 +130,16 @@ let getMessageHandlerV2Elements = ( ~endpoint, ) + let getIntentPromise = PaymentHelpersV2.fetchIntent( + ~clientSecret, + ~paymentId, + ~profileId, + ~publishableKey, + ~logger, + ~customPodUri, + ~endpoint, + ) + ev => { open Utils let dict = ev.data->safeParse->getDictFromJson @@ -137,6 +147,8 @@ let getMessageHandlerV2Elements = ( paymentMethodsListPromise->sendPromiseData("payment_methods_list_v2") } else if dict->isKeyPresentInDict("sendSessionTokensResponse") { sessionTokensPromise->sendPromiseData("session_tokens") + } else if dict->isKeyPresentInDict("sendGetIntentResponse") { + getIntentPromise->sendPromiseData("get_intent_v2") } } } diff --git a/src/Types/UnifiedPaymentsTypesV2.res b/src/Types/UnifiedPaymentsTypesV2.res index 1e4a7bce8..dcde92b0a 100644 --- a/src/Types/UnifiedPaymentsTypesV2.res +++ b/src/Types/UnifiedPaymentsTypesV2.res @@ -101,3 +101,7 @@ type paymentListLookupNew = { walletsList: array, otherPaymentList: array, } + +type intentCall = {paymentType: PaymentMethodsRecord.payment_type} + +type intentLoadState = LoadingIntent | LoadedIntent(intentCall) | Error(JSON.t) diff --git a/src/Utilities/APIHelpers/APIUtils.res b/src/Utilities/APIHelpers/APIUtils.res index 27d35056e..ff8c00886 100644 --- a/src/Utilities/APIHelpers/APIUtils.res +++ b/src/Utilities/APIHelpers/APIUtils.res @@ -14,23 +14,36 @@ type apiCallV1 = | ConfirmPayout | FetchBlockedBins -type apiCallV2 = FetchSessionsV2 +type apiCallV2 = FetchSessionsV2 | FetchIntent -type apiCall = - | V1(apiCallV1) - | V2(apiCallV2) - -type apiParams = { - clientSecret: option, +type commonApiParams = { publishableKey: option, customBackendBaseUrl: option, +} + +type apiParamsV2 = {...commonApiParams, paymentIdV2: option} + +type apiParamsV1 = { + ...commonApiParams, + clientSecret: option, paymentMethodId: option, forceSync: option, pollId: option, payoutId: option, } -let generateApiUrl = (apiCallType: apiCall, ~params: apiParams) => { +module CommonUtils = { + let buildQueryParams = params => + switch params { + | list{} => "" + | _ => + params + ->List.map(((key, value)) => `${key}=${value}`) + ->List.reduce("", (acc, param) => acc === "" ? `?${param}` : `${acc}&${param}`) + } +} + +let generateApiUrlV1 = (~params: apiParamsV1, ~apiCallType: apiCallV1) => { let { clientSecret, publishableKey, @@ -53,17 +66,8 @@ let generateApiUrl = (apiCallType: apiCall, ~params: apiParams) => { ApiEndpoint.getApiEndPoint(~publishableKey=publishableKeyVal), ) - let buildQueryParams = params => - switch params { - | list{} => "" - | _ => - params - ->List.map(((key, value)) => `${key}=${value}`) - ->List.reduce("", (acc, param) => acc === "" ? `?${param}` : `${acc}&${param}`) - } - let isRetrieveIntent = switch apiCallType { - | V1(RetrievePaymentIntent) => true + | RetrievePaymentIntent => true | _ => false } @@ -79,50 +83,62 @@ let generateApiUrl = (apiCallType: apiCall, ~params: apiParams) => { }->List.filterMap(x => x) let queryParams = switch apiCallType { - | V1(inner) => - switch inner { - | FetchPaymentMethodList - | FetchCustomerPaymentMethodList - | RetrievePaymentIntent => defaultParams - | FetchBlockedBins => list{("data_kind", "card_bin"), ...defaultParams} - | FetchSessions - | FetchThreeDsAuth - | FetchSavedPaymentMethodList - | DeletePaymentMethod - | CalculateTax - | CreatePaymentMethod - | CallAuthLink - | CallAuthExchange - | RetrieveStatus - | ConfirmPayout => - list{} - } - | V2(_) => list{} + | FetchPaymentMethodList + | FetchCustomerPaymentMethodList + | RetrievePaymentIntent => defaultParams + | FetchBlockedBins => list{("data_kind", "card_bin"), ...defaultParams} + | FetchSessions + | FetchThreeDsAuth + | FetchSavedPaymentMethodList + | DeletePaymentMethod + | CalculateTax + | CreatePaymentMethod + | CallAuthLink + | CallAuthExchange + | RetrieveStatus + | ConfirmPayout => + list{} } let path = switch apiCallType { - | V1(inner) => - switch inner { - | FetchPaymentMethodList => "account/payment_methods" - | FetchSessions => "payments/session_tokens" - | FetchThreeDsAuth => `payments/${paymentIntentID}/3ds/authentication` - | FetchCustomerPaymentMethodList - | FetchSavedPaymentMethodList => "customers/payment_methods" - | DeletePaymentMethod => `payment_methods/${paymentMethodIdVal}` - | CalculateTax => `payments/${paymentIntentID}/calculate_tax` - | CreatePaymentMethod => "payment_methods" - | RetrievePaymentIntent => `payments/${paymentIntentID}` - | CallAuthLink => "payment_methods/auth/link" - | CallAuthExchange => "payment_methods/auth/exchange" - | RetrieveStatus => `poll/status/${pollIdVal}` - | ConfirmPayout => `payouts/${payoutIdVal}/confirm` - | FetchBlockedBins => "blocklist" - } - | V2(inner) => - switch inner { - | FetchSessionsV2 => `v2/payments/${paymentIntentID}/create-external-sdk-tokens` - } + | FetchPaymentMethodList => "account/payment_methods" + | FetchSessions => "payments/session_tokens" + | FetchThreeDsAuth => `payments/${paymentIntentID}/3ds/authentication` + | FetchCustomerPaymentMethodList + | FetchSavedPaymentMethodList => "customers/payment_methods" + | DeletePaymentMethod => `payment_methods/${paymentMethodIdVal}` + | CalculateTax => `payments/${paymentIntentID}/calculate_tax` + | CreatePaymentMethod => "payment_methods" + | RetrievePaymentIntent => `payments/${paymentIntentID}` + | CallAuthLink => "payment_methods/auth/link" + | CallAuthExchange => "payment_methods/auth/exchange" + | RetrieveStatus => `poll/status/${pollIdVal}` + | ConfirmPayout => `payouts/${payoutIdVal}/confirm` + | FetchBlockedBins => "blocklist" + } + + `${baseUrl}/${path}${CommonUtils.buildQueryParams(queryParams)}` +} + +let generateApiUrlV2 = (~params: apiParamsV2, ~apiCallType: apiCallV2) => { + let {publishableKey, customBackendBaseUrl, paymentIdV2} = params + + let publishableKeyVal = publishableKey->Option.getOr("") + let paymentIdVal = paymentIdV2->Option.getOr("") + + let baseUrl = + customBackendBaseUrl->Option.getOr( + ApiEndpoint.getApiEndPoint(~publishableKey=publishableKeyVal), + ) + + let queryParams = switch apiCallType { + | _ => list{} + } + + let path = switch apiCallType { + | FetchSessionsV2 => `v2/payments/${paymentIdVal}/create-external-sdk-tokens` + | FetchIntent => `v2/payments/${paymentIdVal}/get-intent` } - `${baseUrl}/${path}${buildQueryParams(queryParams)}` + `${baseUrl}/${path}${CommonUtils.buildQueryParams(queryParams)}` } diff --git a/src/Utilities/PaymentHelpers.res b/src/Utilities/PaymentHelpers.res index faef45ee4..e7d4fef33 100644 --- a/src/Utilities/PaymentHelpers.res +++ b/src/Utilities/PaymentHelpers.res @@ -27,8 +27,8 @@ let retrievePaymentIntent = async ( ~customPodUri, ~isForceSync=false, ) => { - let uri = APIUtils.generateApiUrl( - V1(RetrievePaymentIntent), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=RetrievePaymentIntent, ~params={ clientSecret: Some(clientSecret), publishableKey: Some(publishableKey), @@ -69,8 +69,8 @@ let fetchBlockedBins = async ( ~customPodUri, ~endpoint, ) => { - let uri = APIUtils.generateApiUrl( - V1(FetchBlockedBins), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=FetchBlockedBins, ~params={ clientSecret: Some(clientSecret), publishableKey: None, @@ -99,8 +99,8 @@ let fetchBlockedBins = async ( } let threeDsAuth = async (~clientSecret, ~logger, ~threeDsMethodComp, ~headers) => { - let url = APIUtils.generateApiUrl( - V1(FetchThreeDsAuth), + let url = APIUtils.generateApiUrlV1( + ~apiCallType=FetchThreeDsAuth, ~params={ clientSecret: Some(clientSecret), publishableKey: None, @@ -201,8 +201,8 @@ let rec pollRetrievePaymentIntent = ( } let retrieveStatus = async (~publishableKey, ~customPodUri, pollID, logger) => { - let uri = APIUtils.generateApiUrl( - V1(RetrieveStatus), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=RetrieveStatus, ~params={ clientSecret: None, publishableKey: Some(publishableKey), @@ -1398,8 +1398,8 @@ let fetchSessions = async ( ("wallets", wallets->JSON.Encode.array), ("delayed_session_token", isDelayedSessionToken->JSON.Encode.bool), ]->getJsonFromArrayOfJson - let uri = APIUtils.generateApiUrl( - V1(FetchSessions), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=FetchSessions, ~params={ customBackendBaseUrl: Some(endpoint), clientSecret: None, @@ -1439,8 +1439,8 @@ let confirmPayout = async ( ~body, ~payoutId, ) => { - let uri = APIUtils.generateApiUrl( - V1(ConfirmPayout), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=ConfirmPayout, ~params={ clientSecret: Some(clientSecret), customBackendBaseUrl: Some(endpoint), @@ -1482,8 +1482,8 @@ let createPaymentMethod = async ( ~endpoint, ~body, ) => { - let uri = APIUtils.generateApiUrl( - V1(CreatePaymentMethod), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=CreatePaymentMethod, ~params={ clientSecret: Some(clientSecret), customBackendBaseUrl: Some(endpoint), @@ -1524,8 +1524,8 @@ let fetchPaymentMethodList = async ( ~customPodUri, ~endpoint, ) => { - let uri = APIUtils.generateApiUrl( - V1(FetchPaymentMethodList), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=FetchPaymentMethodList, ~params={ clientSecret: Some(clientSecret), customBackendBaseUrl: Some(endpoint), @@ -1561,8 +1561,8 @@ let fetchCustomerPaymentMethodList = async ( ~endpoint, ~isPaymentSession=false, ) => { - let uri = APIUtils.generateApiUrl( - V1(FetchCustomerPaymentMethodList), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=FetchCustomerPaymentMethodList, ~params={ clientSecret: Some(clientSecret), customBackendBaseUrl: Some(endpoint), @@ -1668,8 +1668,8 @@ let callAuthLink = async ( ~iframeId, ~logger, ) => { - let uri = APIUtils.generateApiUrl( - V1(CallAuthLink), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=CallAuthLink, ~params={ clientSecret: None, publishableKey: Some(publishableKey), @@ -1732,8 +1732,8 @@ let callAuthExchange = async ( ) => { open Promise open PaymentType - let uri = APIUtils.generateApiUrl( - V1(CallAuthExchange), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=CallAuthExchange, ~params={ clientSecret: None, publishableKey: Some(publishableKey), @@ -1805,8 +1805,8 @@ let fetchSavedPaymentMethodList = async ( ~customPodUri, ~isPaymentSession=false, ) => { - let uri = APIUtils.generateApiUrl( - V1(FetchSavedPaymentMethodList), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=FetchSavedPaymentMethodList, ~params={ customBackendBaseUrl: Some(endpoint), clientSecret: None, @@ -1836,8 +1836,8 @@ let fetchSavedPaymentMethodList = async ( } let deletePaymentMethod = async (~ephemeralKey, ~paymentMethodId, ~logger, ~customPodUri) => { - let uri = APIUtils.generateApiUrl( - V1(DeletePaymentMethod), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=DeletePaymentMethod, ~params={ customBackendBaseUrl: None, clientSecret: None, @@ -1874,8 +1874,8 @@ let calculateTax = async ( ~customPodUri, ~sessionId, ) => { - let uri = APIUtils.generateApiUrl( - V1(CalculateTax), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=CalculateTax, ~params={ customBackendBaseUrl: None, clientSecret: Some(clientSecret), diff --git a/src/Utilities/PaymentHelpersV2.res b/src/Utilities/PaymentHelpersV2.res index 2a6b96e1f..74b8b0946 100644 --- a/src/Utilities/PaymentHelpersV2.res +++ b/src/Utilities/PaymentHelpersV2.res @@ -585,3 +585,40 @@ let fetchSessions = ( JSON.Encode.null->resolve }) } + +let fetchIntent = async ( + ~clientSecret, + ~publishableKey, + ~paymentId, + ~logger, + ~customPodUri, + ~endpoint, + ~profileId, +) => { + let uri = APIUtils.generateApiUrlV2( + ~apiCallType=FetchIntent, + ~params={ + customBackendBaseUrl: Some(endpoint), + publishableKey: Some(publishableKey), + paymentIdV2: Some(paymentId), + }, + ) + + let onSuccess = data => data + + let onFailure = _ => JSON.Encode.null + + // Todo: Add logger + await fetchApiWithLogging( + uri, + ~eventName=CUSTOMER_PAYMENT_METHODS_CALL, + ~logger, + ~method=#GET, + ~customPodUri=Some(customPodUri), + ~publishableKey=Some(publishableKey), + ~clientSecret=Some(clientSecret), + ~profileId=Some(profileId), + ~onSuccess, + ~onFailure, + ) +} diff --git a/src/Utilities/RecoilAtomsV2.res b/src/Utilities/RecoilAtomsV2.res index 23a1f6e0e..8fae504f9 100644 --- a/src/Utilities/RecoilAtomsV2.res +++ b/src/Utilities/RecoilAtomsV2.res @@ -4,6 +4,7 @@ let vaultMode = Recoil.atom("vaultMode", VaultHelpers.None) let managePaymentMethod = Recoil.atom("managePaymentMethod", "") let savedMethodsV2 = Recoil.atom("savedMethodsV2", [UnifiedHelpersV2.defaultCustomerMethods]) let paymentMethodsListV2 = Recoil.atom("paymentMethodsListV2", UnifiedPaymentsTypesV2.LoadingV2) +let intentList = Recoil.atom("intentList", UnifiedPaymentsTypesV2.LoadingIntent) let paymentMethodListValueV2 = Recoil.atom( "paymentMethodListValueV2", UnifiedHelpersV2.defaultPaymentsList, diff --git a/src/Utilities/UnifiedHelpersV2.res b/src/Utilities/UnifiedHelpersV2.res index bca227c51..bd0373d6f 100644 --- a/src/Utilities/UnifiedHelpersV2.res +++ b/src/Utilities/UnifiedHelpersV2.res @@ -149,6 +149,19 @@ let itemToPaymentDetails = cust => { } } +let itemToIntentObjMapper = dict => { + { + paymentType: getString(dict, "payment_type", "")->paymentTypeMapper, + } +} + +let createIntentDetails = (dict, key) => { + let intentDict = dict->Utils.getDictFromDict(key) + let response = intentDict->itemToIntentObjMapper + + LoadedIntent(response) +} + let defaultAddress = { city: "", country: "", diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res index befbc35a8..7e5e29708 100644 --- a/src/Utilities/Utils.res +++ b/src/Utilities/Utils.res @@ -848,10 +848,15 @@ let getHeaders = ( ~customPodUri=None, ~headers=Dict.make(), ~publishableKey=None, + ~clientSecret=None, + ~profileId=None, ): Fetch.Headers.t => { + let publishableKeyVal = publishableKey->Option.map(key => key)->Option.getOr("invalid_key") + let profileIdVal = profileId->Option.getOr("invalid_key") + let clientSecretVal = clientSecret->Option.getOr("invalid_key") + let defaultHeaders = [ ("Content-Type", "application/json"), - ("api-key", publishableKey->Option.map(key => key)->Option.getOr("invalid_key")), ("X-Client-Version", Window.version), ("X-Payment-Confirm-Source", "sdk"), ("X-Browser-Name", HyperLogger.arrayOfNameAndVersion->Array.get(0)->Option.getOr("Others")), @@ -859,6 +864,14 @@ let getHeaders = ( ("X-Client-Platform", "web"), ] + let authorizationHeaders = switch GlobalVars.sdkVersion { + | V2 => [ + ("x-profile-id", profileIdVal), + ("Authorization", `publishable-key=${publishableKeyVal},client-secret=${clientSecretVal}`), + ] + | V1 => [("api-key", publishableKey->Option.map(key => key)->Option.getOr("invalid_key"))] + } + let authHeader = switch (token, uri) { | (Some(tok), Some(_)) => [("Authorization", tok)] | _ => [] @@ -871,6 +884,7 @@ let getHeaders = ( let finalHeaders = [ ...defaultHeaders, + ...authorizationHeaders, ...authHeader, ...customPodHeader, ...Dict.toArray(headers), @@ -949,16 +963,20 @@ let fetchApiWithLogging = async ( ~publishableKey=None, ~isPaymentSession=false, ~onCatchCallback=None, + ~clientSecret=None, + ~profileId=None, ) => { open LoggerUtils // * Log request initiation - LogAPIResponse.logApiResponse( - ~logger, - ~uri, - ~eventName=apiEventInitMapper(eventName), - ~status=Request, - ) + if GlobalVars.sdkVersion != V2 { + LogAPIResponse.logApiResponse( + ~logger, + ~uri, + ~eventName=apiEventInitMapper(eventName), + ~status=Request, + ) + } try { let body = switch method { @@ -976,6 +994,8 @@ let fetchApiWithLogging = async ( ~uri, ~customPodUri, ~publishableKey, + ~clientSecret, + ~profileId, ), }, ) @@ -984,26 +1004,30 @@ let fetchApiWithLogging = async ( if resp->Fetch.Response.ok { let data = await Fetch.Response.json(resp) - LogAPIResponse.logApiResponse( - ~logger, - ~uri, - ~eventName=Some(eventName), - ~status=Success, - ~statusCode, - ~isPaymentSession, - ) + if GlobalVars.sdkVersion != V2 { + LogAPIResponse.logApiResponse( + ~logger, + ~uri, + ~eventName=Some(eventName), + ~status=Success, + ~statusCode, + ~isPaymentSession, + ) + } onSuccess(data) } else { let data = await resp->Fetch.Response.json - LogAPIResponse.logApiResponse( - ~logger, - ~uri, - ~eventName=Some(eventName), - ~status=Error, - ~statusCode, - ~data, - ~isPaymentSession, - ) + if GlobalVars.sdkVersion != V2 { + LogAPIResponse.logApiResponse( + ~logger, + ~uri, + ~eventName=Some(eventName), + ~status=Error, + ~statusCode, + ~data, + ~isPaymentSession, + ) + } onFailure(data) } } catch { @@ -1017,14 +1041,16 @@ let fetchApiWithLogging = async ( "error": exceptionMessage, }, ) - LogAPIResponse.logApiResponse( - ~logger, - ~uri, - ~eventName=Some(eventName), - ~status=Exception, - ~data=exceptionMessage, - ~isPaymentSession, - ) + if GlobalVars.sdkVersion != V2 { + LogAPIResponse.logApiResponse( + ~logger, + ~uri, + ~eventName=Some(eventName), + ~status=Exception, + ~data=exceptionMessage, + ~isPaymentSession, + ) + } switch onCatchCallback { | Some(fun) => fun(exceptionMessage) | None => onFailure(exceptionMessage) diff --git a/src/hyper-loader/Elements.res b/src/hyper-loader/Elements.res index 0f7327246..d3aff7055 100644 --- a/src/hyper-loader/Elements.res +++ b/src/hyper-loader/Elements.res @@ -216,6 +216,24 @@ let make = ( }) } + let fetchIntent = (mountedIframeRef, componentType) => { + Promise.make((resolve, _) => { + let handleIntentLoaded = (event: Types.event) => { + let json = event.data->anyTypeToJson + let dict = json->getDictFromJson + let isGetIntentData = dict->getString("data", "") === "get_intent_v2" + if isGetIntentData { + resolve() + let msg = [("getIntent", json)]->Dict.fromArray + mountedIframeRef->Window.iframePostMessage(msg) + } + } + let msg = [("sendGetIntentResponse", true->JSON.Encode.bool)]->Dict.fromArray + addSmartEventListener("message", handleIntentLoaded, `onGetIntentLoaded-${componentType}`) + preMountLoaderIframeDiv->Window.iframePostMessage(msg) + }) + } + let fetchPaymentsListV2 = (mountedIframeRef, componentType) => { Promise.make((resolve, _) => { let handlePaymentMethodsLoaded = (event: Types.event) => { @@ -1413,7 +1431,11 @@ let make = ( fetchBlockedBins(mountedIframeRef, componentType), sessionTokensPromise, ] - | V2 => [fetchPaymentsListV2(mountedIframeRef, componentType), sessionTokensPromise] + | V2 => [ + fetchPaymentsListV2(mountedIframeRef, componentType), + sessionTokensPromise, + fetchIntent(mountedIframeRef, componentType), + ] } Promise.all(promises)->then(_ => { diff --git a/src/hyper-loader/Hyper.res b/src/hyper-loader/Hyper.res index 5a5a45800..f3a53acc7 100644 --- a/src/hyper-loader/Hyper.res +++ b/src/hyper-loader/Hyper.res @@ -331,8 +331,8 @@ let make = (keys, options: option, analyticsInfo: option) => { } let retrievePaymentIntentFn = async clientSecret => { - let uri = APIUtils.generateApiUrl( - V1(RetrievePaymentIntent), + let uri = APIUtils.generateApiUrlV1( + ~apiCallType=RetrievePaymentIntent, ~params={ clientSecret: Some(clientSecret), publishableKey: Some(publishableKey),