Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/LoaderController.res
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime
let (configAtom, setConfig) = Recoil.useRecoilState(configAtom)
let (keys, setKeys) = Recoil.useRecoilState(keys)
let (paymentMethodList, setPaymentMethodList) = Recoil.useRecoilState(paymentMethodList)
let setSdkConfigs = Recoil.useSetRecoilState(sdkConfigs)
let (_, setSessions) = Recoil.useRecoilState(sessions)
let (options, setOptions) = Recoil.useRecoilState(elementOptions)
let (optionsPayment, setOptionsPayment) = Recoil.useRecoilState(optionAtom)
Expand Down Expand Up @@ -595,6 +596,37 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime
dict->UnifiedHelpersV2.createPaymentsObjArr("paymentManagementMethods")
setPaymentManagementList(_ => paymentManagementMethods)
}
if dict->getDictIsSome("sdkConfigs") {
let sdkConfigsJson = dict->getJsonObjectFromDict("sdkConfigs")
let sdkConfigsDict = sdkConfigsJson->getDictFromJson
let isSdkConfigsError =
sdkConfigsJson == Dict.make()->JSON.Encode.object ||
sdkConfigsDict->Dict.get("error")->Option.isSome
let updatedState: PaymentType.loadType = isSdkConfigsError
? LoadError(sdkConfigsJson)
: Loaded(sdkConfigsJson)
let finalLoadLatency = if launchTime <= 0.0 {
0.0
} else {
Date.now() -. launchTime
}
switch updatedState {
| Loaded(_) =>
logger.setLogInfo(
~value="Loaded",
~eventName=SDK_CONFIGS_CALL,
~latency=finalLoadLatency,
)
| LoadError(x) =>
logger.setLogError(
~value="LoadError: " ++ x->JSON.stringify,
~eventName=SDK_CONFIGS_CALL,
~latency=finalLoadLatency,
)
Comment thread
sakksham7 marked this conversation as resolved.
| _ => ()
}
setSdkConfigs(_ => updatedState)
}
if dict->Dict.get("applePayCanMakePayments")->Option.isSome {
setIsApplePayReady(_ => true)
}
Expand Down
5 changes: 4 additions & 1 deletion src/Payment.res
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ let make = (~paymentMode, ~integrateError, ~logger) => {
setCardError(_ => localeString.cardNumberEmptyText)
setUserError(localeString.enterFieldsText)
} else if cardEligibilityError->Option.isSome {
let msg = EligibilityHelpers.getCardEligibilityErrorText(~cardEligibilityError, ~localeString)
let msg = EligibilityHelpers.getCardEligibilityErrorText(
~cardEligibilityError,
~localeString,
)
setCardError(_ => msg)
setUserError(msg)
}
Expand Down
26 changes: 22 additions & 4 deletions src/Payments/PreMountLoader.res
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,20 @@ let getMessageHandlerV1Elements = (
~isTestMode=false,
~isSdkParamsEnabled=false,
) => {
let (paymentMethodsPromise, customerPaymentMethodsPromise, sessionTokensPromise) = if (
isTestMode || isSdkParamsEnabled
) {
let (
paymentMethodsPromise,
customerPaymentMethodsPromise,
sessionTokensPromise,
sdkConfigsPromise,
) = if isTestMode || isSdkParamsEnabled {
let mockResponse = Dict.make()->JSON.Encode.object

(Promise.resolve(mockResponse), Promise.resolve(mockResponse), Promise.resolve(mockResponse))
(
Promise.resolve(mockResponse),
Promise.resolve(mockResponse),
Promise.resolve(mockResponse),
Promise.resolve(mockResponse),
)
} else {
(
PaymentHelpers.fetchPaymentMethodList(
Expand Down Expand Up @@ -99,6 +107,14 @@ let getMessageHandlerV1Elements = (
~merchantHostname,
~sdkAuthorization=Some(sdkAuthorization),
),
PaymentHelpers.fetchSdkConfigs(
~clientSecret,
~publishableKey,
~logger,
~customPodUri,
~endpoint,
~sdkAuthorization=Some(sdkAuthorization),
),
Comment thread
aritro2002 marked this conversation as resolved.
)
}

Expand All @@ -111,6 +127,8 @@ let getMessageHandlerV1Elements = (
customerPaymentMethodsPromise->sendPromiseData("customer_payment_methods")
} else if dict->isKeyPresentInDict("sendSessionTokensResponse") {
sessionTokensPromise->sendPromiseData("session_tokens")
} else if dict->isKeyPresentInDict("sendSdkConfigsResponse") {
sdkConfigsPromise->sendPromiseData("sdk_configs")
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Types/HyperLoggerTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type eventName =
| CUSTOMER_PAYMENT_METHODS_CALL
| CREATE_CUSTOMER_PAYMENT_METHODS_CALL_INIT
| CREATE_CUSTOMER_PAYMENT_METHODS_CALL
| SDK_CONFIGS_CALL_INIT
| SDK_CONFIGS_CALL
| TRUSTPAY_SCRIPT
| PM_AUTH_CONNECTOR_SCRIPT
| GOOGLE_PAY_SCRIPT
Expand Down
5 changes: 4 additions & 1 deletion src/Utilities/APIHelpers/APIUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type apiCallV1 =
| FetchEligibilityCheck
| FetchAuthenticationSync
| FetchPaymentMethodEligibility
| FetchSdkConfigs

type commonApiParams = {
publishableKey: option<string>,
Expand Down Expand Up @@ -101,7 +102,8 @@ let generateApiUrlV1 = (~params: apiParamsV1, ~apiCallType: apiCallV1) => {
| FetchEnabledAuthnMethodsToken
| FetchEligibilityCheck
| FetchAuthenticationSync
| FetchPaymentMethodEligibility =>
| FetchPaymentMethodEligibility
| FetchSdkConfigs =>
list{}
}

Expand All @@ -122,6 +124,7 @@ let generateApiUrlV1 = (~params: apiParamsV1, ~apiCallType: apiCallV1) => {
| FetchEligibilityCheck => `authentication/${authenticationIdVal}/eligibility-check`
| FetchAuthenticationSync => `authentication/${merchantId}/${authenticationIdVal}/sync`
| FetchPaymentMethodEligibility => `payments/${paymentIntentId}/eligibility`
| FetchSdkConfigs => "v1/sdk/configs/web/sdk_config.json"
}

`${baseUrl}/${path}${CommonUtils.buildQueryParams(queryParams)}`
Expand Down
1 change: 1 addition & 0 deletions src/Utilities/LoggerUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ let apiEventInitMapper = (eventName: HyperLoggerTypes.eventName): option<
| AUTHENTICATION_CALL => Some(AUTHENTICATION_CALL_INIT)
| CONFIRM_PAYOUT_CALL => Some(CONFIRM_PAYOUT_CALL_INIT)
| PAYMENT_METHOD_ELIGIBILITY_CALL => Some(PAYMENT_METHOD_ELIGIBILITY_CALL_INIT)
| SDK_CONFIGS_CALL => Some(SDK_CONFIGS_CALL_INIT)
| ENABLED_AUTHN_METHODS_TOKEN_CALL => Some(ENABLED_AUTHN_METHODS_TOKEN_CALL)
| ELIGIBILITY_CHECK_CALL => Some(ELIGIBILITY_CHECK_CALL)
| AUTHENTICATION_SYNC_CALL => Some(AUTHENTICATION_SYNC_CALL)
Expand Down
43 changes: 43 additions & 0 deletions src/Utilities/PaymentHelpers.res
Original file line number Diff line number Diff line change
Expand Up @@ -2400,3 +2400,46 @@ let getConstructedPaymentMethodName = (~paymentMethod, ~paymentMethodType) => {
| _ => paymentMethodType
}
}

let fetchSdkConfigs = async (
~clientSecret,
~publishableKey,
~logger,
~customPodUri,
~endpoint,
~sdkAuthorization=None,
) => {
let uri = APIUtils.generateApiUrlV1(
~apiCallType=FetchSdkConfigs,
~params={
clientSecret: Some(clientSecret),
customBackendBaseUrl: Some(endpoint),
publishableKey: Some(publishableKey),
forceSync: None,
pollId: None,
payoutId: None,
sdkAuthorization,
},
)

let headers = switch sdkAuthorization->Utils.getNonEmptyOption {
| None => [("client-secret", clientSecret)]->Dict.fromArray
| Some(_) => Dict.make()
}

let onSuccess = data => data
let onFailure = _ => JSON.Encode.null

await fetchApiWithLogging(
uri,
~eventName=SDK_CONFIGS_CALL,
~headers,
~logger,
~method=#GET,
~customPodUri=Some(customPodUri),
~publishableKey=Some(publishableKey),
~onSuccess,
~onFailure,
~sdkAuthorization,
)
}
1 change: 1 addition & 0 deletions src/Utilities/RecoilAtoms.res
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ let optionAtom = Recoil.atom("options", PaymentType.defaultOptions)
let sessions = Recoil.atom("sessions", PaymentType.Loading)
let updateSession = Recoil.atom("updateSession", false)
let paymentMethodList = Recoil.atom("paymentMethodList", PaymentType.Loading)
let sdkConfigs = Recoil.atom("sdkConfigs", PaymentType.Loading)
let loggerAtom = Recoil.atom("component", LoggerUtils.defaultLoggerConfig)
let sessionId = Recoil.atom("sessionId", "")
let isConfirmBlocked = Recoil.atom("isConfirmBlocked", false)
Expand Down
20 changes: 20 additions & 0 deletions src/hyper-loader/Elements.res
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ let make = (
~paymentMethodsDataPromise: ref<promise<JSON.t>>,
~customerPaymentMethodsDataPromise: ref<promise<JSON.t>>,
~sessionTokensDataPromise: ref<promise<JSON.t>>,
~sdkConfigsDataPromise: ref<promise<JSON.t>>,
) => {
try {
let iframeRef = []
Expand Down Expand Up @@ -101,10 +102,15 @@ let make = (
let isSdkParamsEnabled = preloadSDKWithParams->Dict.toArray->Array.length > 0

// --- Initial preMountLoader setup ---
// TODO(sdk-configs): For consumers who provide profileId at Hyper.init time (before
// elements() is called), the sdk-configs API call could be prefetched early in Hyper.make()
// and the result passed in here, avoiding the round-trip through PreMountLoader.
// Currently deferred to PreMountLoader for consistency with the other 3 pre-mount calls.
let (
initialPaymentMethodsPromise,
initialCustomerPaymentMethodsPromise,
initialSessionTokensPromise,
initialSdkConfigsPromise,
) = UpdateIntentHelpersNew.setupPreMountLoaderPromises(
~publishableKey,
~sdkSessionId,
Expand All @@ -131,6 +137,7 @@ let make = (
paymentMethodsDataPromise.contents = initialPaymentMethodsPromise
customerPaymentMethodsDataPromise.contents = initialCustomerPaymentMethodsPromise
sessionTokensDataPromise.contents = initialSessionTokensPromise
sdkConfigsDataPromise.contents = initialSdkConfigsPromise

let onPlaidCallback = mountedIframeRef => {
(ev: Types.event) => {
Expand Down Expand Up @@ -252,6 +259,16 @@ let make = (
Promise.resolve()
})
}

let forwardSdkConfigsDataToIframe = mountedIframeRef => {
sdkConfigsDataPromise.contents->Promise.then(json => {
let sdkConfigs = preloadSDKWithParams->getJsonFromDict("sdkConfigs", json)
let msg = [("sdkConfigs", sdkConfigs)]->Dict.fromArray
mountedIframeRef->Window.iframePostMessage(msg)
Promise.resolve()
})
}

if !isTestMode && !hasSdkAuthorization && !clientSecretReMatch {
manageErrorWarning(
INVALID_FORMAT,
Expand Down Expand Up @@ -317,6 +334,7 @@ let make = (
~paymentMethodsDataPromise,
~customerPaymentMethodsDataPromise,
~sessionTokensDataPromise,
~sdkConfigsDataPromise,
~iframes=iframeRef,
~callback,
~publishableKey,
Expand Down Expand Up @@ -350,6 +368,7 @@ let make = (
forwardPaymentMethodsToIframe(iframe),
forwardCustomerPaymentMethodsToIframe(iframe, false),
forwardSessionTokensDataToIframe(iframe),
forwardSdkConfigsDataToIframe(iframe),
])
}),
)
Expand Down Expand Up @@ -1528,6 +1547,7 @@ let make = (
->catch(_ => resolve())
->ignore
forwardSessionTokensToIframe(mountedIframeRef)->catch(_ => resolve())->ignore
forwardSdkConfigsDataToIframe(mountedIframeRef)->catch(_ => resolve())->ignore

mountedIframeRef->Window.iframePostMessage(message)
}
Expand Down
6 changes: 6 additions & 0 deletions src/hyper-loader/Hyper.res
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ let make = (keys, options: option<JSON.t>, analyticsInfo: option<JSON.t>) => {
let paymentMethodsDataPromise = ref(emptyJsonPromise)
let customerPaymentMethodsDataPromise = ref(emptyJsonPromise)
let sessionTokensDataPromise = ref(emptyJsonPromise)
let sdkConfigsDataPromise = ref(emptyJsonPromise)
// TODO(sdk-configs): profileId is available here at init time for consumers who provide
// it at Hyper.init stage. sdk-configs could be prefetched early (before elements() is
// called) for a latency optimisation. Currently deferred to PreMountLoader for consistency.

let retrievePaymentIntentFn = async clientSecretOrSdkAuth => {
// Try to decode as base64 — if decodable, it's an SDK authorization token.
Expand Down Expand Up @@ -577,6 +581,7 @@ let make = (keys, options: option<JSON.t>, analyticsInfo: option<JSON.t>) => {
~paymentMethodsDataPromise,
~customerPaymentMethodsDataPromise,
~sessionTokensDataPromise,
~sdkConfigsDataPromise,
)
}

Expand Down Expand Up @@ -764,6 +769,7 @@ let make = (keys, options: option<JSON.t>, analyticsInfo: option<JSON.t>) => {
~paymentMethodsDataPromise,
~customerPaymentMethodsDataPromise,
~sessionTokensDataPromise,
~sdkConfigsDataPromise,
)
}

Expand Down
3 changes: 2 additions & 1 deletion src/hyper-loader/PaymentSession.res
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
open Types
open Utils

let make = (
options,
Expand All @@ -15,6 +14,7 @@ let make = (
~paymentMethodsDataPromise: ref<promise<JSON.t>>,
~customerPaymentMethodsDataPromise: ref<promise<JSON.t>>,
~sessionTokensDataPromise: ref<promise<JSON.t>>,
~sdkConfigsDataPromise: ref<promise<JSON.t>>,
) => {
let logger = logger->Option.getOr(LoggerUtils.defaultLoggerConfig)
let customPodUri =
Expand All @@ -35,6 +35,7 @@ let make = (
~paymentMethodsDataPromise,
~customerPaymentMethodsDataPromise,
~sessionTokensDataPromise,
~sdkConfigsDataPromise,
~iframes=iframeRef.contents,
~callback,
~publishableKey,
Expand Down
Loading
Loading