From d8d06bc232d02f80908961c92ff2258277da4243 Mon Sep 17 00:00:00 2001 From: Mat Schmid Date: Mon, 20 Jan 2025 11:25:04 -0500 Subject: [PATCH] Add attestation event logging to FC API clients --- .../project.pbxproj | 4 ++ .../FinancialConnectionsAPIClient.swift | 50 +++++++++----- .../FinancialConnectionsAPIClientLogger.swift | 65 +++++++++++++++++++ ...cialConnectionsAsyncAPIClient+Legacy.swift | 21 ++++-- .../FinancialConnectionsAsyncAPIClient.swift | 47 ++++++++++---- .../FinancialConnectionsAnalyticsClient.swift | 1 + .../Source/Common/HostViewController.swift | 3 +- .../LinkLogin/LinkLoginDataSource.swift | 17 +++-- .../NetworkingLinkSignupDataSource.swift | 14 ++-- ...NetworkingLinkVerificationDataSource.swift | 3 +- .../PartnerAuth/PartnerAuthDataSource.swift | 3 +- .../NetworkingOTPDataSource.swift | 5 +- 12 files changed, 184 insertions(+), 49 deletions(-) create mode 100644 StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClientLogger.swift diff --git a/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj b/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj index 8f6e6c66f25..0c36c4a2e14 100644 --- a/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj +++ b/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 495539EE2C484DC200543D18 /* FinancialConnectionsTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495539ED2C484DC200543D18 /* FinancialConnectionsTheme.swift */; }; 496A6AE72C29E0BB00D34F8E /* testmode@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 496A6AE62C29E0BB00D34F8E /* testmode@3x.png */; }; 497142BC2C514B08000DFA64 /* FlowRouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 497142BB2C514B08000DFA64 /* FlowRouterTests.swift */; }; + 499EEAFD2D3E948B00E1BE85 /* FinancialConnectionsAPIClientLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499EEAFC2D3E948B00E1BE85 /* FinancialConnectionsAPIClientLogger.swift */; }; 49A0B5862C5D2F3C00D697D9 /* FinancialConnectionsAPIClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A0B5852C5D2F3C00D697D9 /* FinancialConnectionsAPIClientTests.swift */; }; 49AC518C2C52DE2C00B712CC /* FinancialConnectionsLinkLoginPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49AC518B2C52DE2C00B712CC /* FinancialConnectionsLinkLoginPane.swift */; }; 49C911372C597EAF00589E0D /* LinkLoginDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C911332C597EAF00589E0D /* LinkLoginDataSource.swift */; }; @@ -330,6 +331,7 @@ 495539ED2C484DC200543D18 /* FinancialConnectionsTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinancialConnectionsTheme.swift; sourceTree = ""; }; 496A6AE62C29E0BB00D34F8E /* testmode@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testmode@3x.png"; sourceTree = ""; }; 497142BB2C514B08000DFA64 /* FlowRouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowRouterTests.swift; sourceTree = ""; }; + 499EEAFC2D3E948B00E1BE85 /* FinancialConnectionsAPIClientLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinancialConnectionsAPIClientLogger.swift; sourceTree = ""; }; 49A0B5852C5D2F3C00D697D9 /* FinancialConnectionsAPIClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinancialConnectionsAPIClientTests.swift; sourceTree = ""; }; 49AC518B2C52DE2C00B712CC /* FinancialConnectionsLinkLoginPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinancialConnectionsLinkLoginPane.swift; sourceTree = ""; }; 49C911332C597EAF00589E0D /* LinkLoginDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkLoginDataSource.swift; sourceTree = ""; }; @@ -657,6 +659,7 @@ 4E2EAD7059FF8358E674774A /* FinancialConnectionsAPIClient.swift */, 49F1B8392D2DAE7100136303 /* FinancialConnectionsAsyncAPIClient.swift */, 49F1B83D2D2EC82300136303 /* FinancialConnectionsAsyncAPIClient+Legacy.swift */, + 499EEAFC2D3E948B00E1BE85 /* FinancialConnectionsAPIClientLogger.swift */, ); path = "API Bindings"; sourceTree = ""; @@ -1319,6 +1322,7 @@ CBF7BE2271D309F2B1E794CC /* FinancialConnectionsDataAccessNotice.swift in Sources */, F67624595BD2CD7B6793BFDA /* FinancialConnectionsImage.swift in Sources */, 07712610C7D2F484AAB96982 /* FinancialConnectionsInstitution.swift in Sources */, + 499EEAFD2D3E948B00E1BE85 /* FinancialConnectionsAPIClientLogger.swift in Sources */, 7386E1F9256B23CE29BF996D /* FinancialConnectionsInstitutionSearchResultResource.swift in Sources */, C7D2763ACCE2CC71E788E18F /* FinancialConnectionsLegalDetailsNotice.swift in Sources */, B271AAF41C9FE6AE392B88D3 /* FinancialConnectionsMixedOAuthParams.swift in Sources */, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift index 45867638152..f6867a0f555 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift @@ -24,6 +24,8 @@ final class FinancialConnectionsAPIClient { var consumerPublishableKey: String? var consumerSession: ConsumerSessionData? + private lazy var logger = FinancialConnectionsAPIClientLogger() + var requestSurface: String { isLinkWithStripe ? "ios_instant_debits" : "ios_connections" } @@ -46,17 +48,20 @@ final class FinancialConnectionsAPIClient { /// Applies attestation-related parameters to the given base parameters /// In case of an assertion error, returns the unmodified base parameters func applyAttestationParameters( - to baseParameters: [String: Any] + to baseParameters: [String: Any], + pane: FinancialConnectionsSessionManifest.NextPane ) -> Future<[String: Any]> { let promise = Promise<[String: Any]>() Task { do { let attest = backingAPIClient.stripeAttest let handle = try await attest.assert() + logger.log(.attestationRequestTokenSucceeded, pane: pane) let newParameters = baseParameters.merging(handle.assertion.requestFields) { (_, new) in new } promise.resolve(with: newParameters) } catch { // Fail silently if we can't get an assertion, we'll try the request anyway. It may fail. + logger.log(.attestationRequestTokenFailed, pane: pane) promise.resolve(with: baseParameters) } } @@ -64,10 +69,11 @@ final class FinancialConnectionsAPIClient { } /// Marks the assertion as completed and forwards attestation errors to the `StripeAttest` client for logging. - func completeAssertion(possibleError: Error?) { + func completeAssertion(possibleError: Error?, pane: FinancialConnectionsSessionManifest.NextPane) { let attest = backingAPIClient.stripeAttest Task { if let error = possibleError, StripeAttest.isLinkAssertionError(error: error) { + logger.log(.attestationVerdictFailed, pane: pane) await attest.receivedAssertionError(error) } await attest.assertionCompleted() @@ -139,11 +145,15 @@ protocol FinancialConnectionsAPI { var consumerPublishableKey: String? { get set } var consumerSession: ConsumerSessionData? { get set } - func completeAssertion(possibleError: Error?) + func completeAssertion( + possibleError: Error?, + pane: FinancialConnectionsSessionManifest.NextPane + ) func synchronize( clientSecret: String, - returnURL: String? + returnURL: String?, + initialSynchronize: Bool ) -> Future func fetchFinancialConnectionsAccounts( @@ -256,7 +266,8 @@ protocol FinancialConnectionsAPI { clientSecret: String, sessionId: String, emailSource: FinancialConnectionsAPIClient.EmailSource, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) -> Future // MARK: - Link API's @@ -285,7 +296,8 @@ protocol FinancialConnectionsAPI { amount: Int?, currency: String?, incentiveEligibilitySession: ElementsSessionContext.IntentID?, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) -> Future func attachLinkConsumerToLinkAccountSession( @@ -348,7 +360,8 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI { func synchronize( clientSecret: String, - returnURL: String? + returnURL: String?, + initialSynchronize: Bool = false ) -> Future { var parameters: [String: Any] = [ "expand": ["manifest.active_auth_session"], @@ -363,10 +376,15 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI { ] mobileParameters["app_return_url"] = returnURL - let attest = backingAPIClient.stripeAttest - if attest.isSupported { - mobileParameters["supports_app_verification"] = true - mobileParameters["verified_app_id"] = Bundle.main.bundleIdentifier + if initialSynchronize { + let attest = backingAPIClient.stripeAttest + if attest.isSupported { + mobileParameters["supports_app_verification"] = true + mobileParameters["verified_app_id"] = Bundle.main.bundleIdentifier + logger.log(.attestationInitSucceeded, pane: .consent) + } else { + logger.log(.attestationInitFailed, pane: .consent) + } } parameters["mobile"] = mobileParameters @@ -917,7 +935,8 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI { clientSecret: String, sessionId: String, emailSource: FinancialConnectionsAPIClient.EmailSource, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) -> Future { var parameters: [String: Any] = [ "email_address": @@ -930,7 +949,7 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI { parameters["request_surface"] = requestSurface parameters["session_id"] = sessionId parameters["email_source"] = emailSource.rawValue - return applyAttestationParameters(to: parameters) + return applyAttestationParameters(to: parameters, pane: pane) .chained { [weak self] updatedParameters in guard let self else { return Promise(error: FinancialConnectionsSheetError.unknown(debugDescription: "FinancialConnectionsAPIClient was deallocated.")) @@ -1003,7 +1022,8 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI { amount: Int?, currency: String?, incentiveEligibilitySession: ElementsSessionContext.IntentID?, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) -> Future { var parameters: [String: Any] = [ "request_surface": requestSurface, @@ -1040,7 +1060,7 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI { } if useMobileEndpoints { - return applyAttestationParameters(to: parameters) + return applyAttestationParameters(to: parameters, pane: pane) .chained { [weak self] updatedParameters in guard let self else { return Promise(error: FinancialConnectionsSheetError.unknown(debugDescription: "FinancialConnectionsAPIClient was deallocated.")) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClientLogger.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClientLogger.swift new file mode 100644 index 00000000000..b7a70236d1e --- /dev/null +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClientLogger.swift @@ -0,0 +1,65 @@ +// +// FinancialConnectionsAPIClientLogger.swift +// StripeFinancialConnections +// +// Created by Mat Schmid on 2025-01-20. +// + +import Foundation + +struct FinancialConnectionsAPIClientLogger { + private var analyticsClient = FinancialConnectionsAnalyticsClient() + + enum Event { + /// When checking if generating attestation is supported succeeds. + case attestationInitSucceeded + /// When checking if generating attestation is supported does not succeed. + case attestationInitFailed + /// When an attestation token gets generated successfully. + case attestationRequestTokenSucceeded + /// When a token generation attempt fails client-side. + case attestationRequestTokenFailed + /// When an attestation verdict fails backend side and we get an attestation related error. + case attestationVerdictFailed + + var name: String { + switch self { + case .attestationInitSucceeded: + return "attestation.init_succeeded" + case .attestationInitFailed: + return "attestation.init_failed" + case .attestationRequestTokenSucceeded: + return "attestation.request_token_succeeded" + case .attestationRequestTokenFailed: + return "attestation.request_token_failed" + case .attestationVerdictFailed: + return "attestation.verdict_failed" + } + } + + var parameters: [String: Any] { + switch self { + case .attestationInitFailed: + var reason: String + if #available(iOS 14.0, *) { + // If the iOS version is supported, we assume the device is unsupported (i.e. simulator). + reason = "ios_device_unsupported" + } else { + // Otherwise, attestation is unavailable due to the OS version being unsupported. + reason = "ios_os_version_unsupported" + } + return ["reason": reason] + default: + return [:] + } + } + } + + func log(_ event: Event, pane: FinancialConnectionsSessionManifest.NextPane) { + analyticsClient.log( + eventName: event.name, + parameters: event.parameters, + pane: pane + ) + } +} diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAsyncAPIClient+Legacy.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAsyncAPIClient+Legacy.swift index 7602da6df4a..443b52a358f 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAsyncAPIClient+Legacy.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAsyncAPIClient+Legacy.swift @@ -44,10 +44,15 @@ extension FinancialConnectionsAsyncAPIClient { extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAPI { func synchronize( clientSecret: String, - returnURL: String? + returnURL: String?, + initialSynchronize: Bool ) -> Future { wrapAsyncToFuture { - try await self.synchronize(clientSecret: clientSecret, returnURL: returnURL) + try await self.synchronize( + clientSecret: clientSecret, + returnURL: returnURL, + initialSynchronize: initialSynchronize + ) } } @@ -311,7 +316,8 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAPI { clientSecret: String, sessionId: String, emailSource: FinancialConnectionsAPIClient.EmailSource, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) -> Future { wrapAsyncToFuture { try await self.consumerSessionLookup( @@ -319,7 +325,8 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAPI { clientSecret: clientSecret, sessionId: sessionId, emailSource: emailSource, - useMobileEndpoints: useMobileEndpoints + useMobileEndpoints: useMobileEndpoints, + pane: pane ) } } @@ -369,7 +376,8 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAPI { amount: Int?, currency: String?, incentiveEligibilitySession: ElementsSessionContext.IntentID?, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) -> Future { wrapAsyncToFuture { try await self.linkAccountSignUp( @@ -379,7 +387,8 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAPI { amount: amount, currency: currency, incentiveEligibilitySession: incentiveEligibilitySession, - useMobileEndpoints: useMobileEndpoints + useMobileEndpoints: useMobileEndpoints, + pane: pane ) } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAsyncAPIClient.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAsyncAPIClient.swift index 9209f4f3b06..9c4cb4fac48 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAsyncAPIClient.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAsyncAPIClient.swift @@ -23,6 +23,8 @@ final class FinancialConnectionsAsyncAPIClient { var consumerPublishableKey: String? var consumerSession: ConsumerSessionData? + private lazy var logger = FinancialConnectionsAPIClientLogger() + var requestSurface: String { isLinkWithStripe ? "ios_instant_debits" : "ios_connections" } @@ -43,10 +45,11 @@ final class FinancialConnectionsAsyncAPIClient { } /// Marks the assertion as completed and forwards attestation errors to the `StripeAttest` client for logging. - func completeAssertion(possibleError: Error?) { + func completeAssertion(possibleError: Error?, pane: FinancialConnectionsSessionManifest.NextPane) { let attest = backingAPIClient.stripeAttest Task { if let error = possibleError, StripeAttest.isLinkAssertionError(error: error) { + logger.log(.attestationVerdictFailed, pane: pane) await attest.receivedAssertionError(error) } await attest.assertionCompleted() @@ -55,14 +58,19 @@ final class FinancialConnectionsAsyncAPIClient { /// Applies attestation-related parameters to the given base parameters /// In case of an assertion error, returns the unmodified base parameters - func applyAttestationParameters(to baseParameters: [String: Any]) async -> [String: Any] { + func applyAttestationParameters( + to baseParameters: [String: Any], + pane: FinancialConnectionsSessionManifest.NextPane + ) async -> [String: Any] { do { let attest = backingAPIClient.stripeAttest let handle = try await attest.assert() + logger.log(.attestationRequestTokenSucceeded, pane: pane) let newParameters = baseParameters.merging(handle.assertion.requestFields) { (_, new) in new } return newParameters } catch { // Fail silently if we can't get an assertion, we'll try the request anyway. It may fail. + logger.log(.attestationRequestTokenFailed, pane: pane) return baseParameters } } @@ -169,7 +177,8 @@ final class FinancialConnectionsAsyncAPIClient { protocol FinancialConnectionsAsyncAPI { func synchronize( clientSecret: String, - returnURL: String? + returnURL: String?, + initialSynchronize: Bool ) async throws -> FinancialConnectionsSynchronize func fetchFinancialConnectionsAccounts( @@ -283,7 +292,8 @@ protocol FinancialConnectionsAsyncAPI { clientSecret: String, sessionId: String, emailSource: FinancialConnectionsAPIClient.EmailSource, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) async throws -> LookupConsumerSessionResponse // MARK: - Link API's @@ -312,7 +322,8 @@ protocol FinancialConnectionsAsyncAPI { amount: Int?, currency: String?, incentiveEligibilitySession: ElementsSessionContext.IntentID?, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) async throws -> LinkSignUpResponse func attachLinkConsumerToLinkAccountSession( @@ -351,7 +362,8 @@ protocol FinancialConnectionsAsyncAPI { extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI { func synchronize( clientSecret: String, - returnURL: String? + returnURL: String?, + initialSynchronize: Bool = false ) async throws -> FinancialConnectionsSynchronize { var parameters: [String: Any] = [ "expand": ["manifest.active_auth_session"], @@ -366,10 +378,15 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI { ] mobileParameters["app_return_url"] = returnURL - let attest = backingAPIClient.stripeAttest - if attest.isSupported { - mobileParameters["supports_app_verification"] = true - mobileParameters["verified_app_id"] = Bundle.main.bundleIdentifier + if initialSynchronize { + let attest = backingAPIClient.stripeAttest + if attest.isSupported { + mobileParameters["supports_app_verification"] = true + mobileParameters["verified_app_id"] = Bundle.main.bundleIdentifier + logger.log(.attestationInitSucceeded, pane: .consent) + } else { + logger.log(.attestationInitFailed, pane: .consent) + } } parameters["mobile"] = mobileParameters return try await post(endpoint: .synchronize, parameters: parameters) @@ -817,7 +834,8 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI { clientSecret: String, sessionId: String, emailSource: FinancialConnectionsAPIClient.EmailSource, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) async throws -> LookupConsumerSessionResponse { var parameters: [String: Any] = [ "email_address": @@ -829,7 +847,7 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI { parameters["request_surface"] = requestSurface parameters["session_id"] = sessionId parameters["email_source"] = emailSource.rawValue - let updatedParameters = await applyAttestationParameters(to: parameters) + let updatedParameters = await applyAttestationParameters(to: parameters, pane: pane) return try await post(endpoint: .mobileConsumerSessionLookup, parameters: updatedParameters) } else { parameters["client_secret"] = clientSecret @@ -891,7 +909,8 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI { amount: Int?, currency: String?, incentiveEligibilitySession: ElementsSessionContext.IntentID?, - useMobileEndpoints: Bool + useMobileEndpoints: Bool, + pane: FinancialConnectionsSessionManifest.NextPane ) async throws -> LinkSignUpResponse { var parameters: [String: Any] = [ "request_surface": requestSurface, @@ -927,7 +946,7 @@ extension FinancialConnectionsAsyncAPIClient: FinancialConnectionsAsyncAPI { } } if useMobileEndpoints { - let updatedParameters = await applyAttestationParameters(to: parameters) + let updatedParameters = await applyAttestationParameters(to: parameters, pane: pane) return try await post(endpoint: .mobileLinkAccountSignup, parameters: updatedParameters) } else { return try await post(endpoint: .linkAccountsSignUp, parameters: parameters) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Analytics/FinancialConnectionsAnalyticsClient.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Analytics/FinancialConnectionsAnalyticsClient.swift index 7526b07ef97..108d0fca504 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Analytics/FinancialConnectionsAnalyticsClient.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Analytics/FinancialConnectionsAnalyticsClient.swift @@ -174,6 +174,7 @@ extension FinancialConnectionsAnalyticsClient { additionalParameters["single_account"] = manifest.singleAccount additionalParameters["allow_manual_entry"] = manifest.allowManualEntry additionalParameters["account_holder_id"] = manifest.accountholderToken + additionalParameters["app_verification_enabled"] = manifest.appVerificationEnabled } static func paneFromViewController( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostViewController.swift index 55b0e941fb5..9aaeaa4f1e0 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostViewController.swift @@ -112,7 +112,8 @@ extension HostViewController { apiClient .synchronize( clientSecret: clientSecret, - returnURL: returnURL + returnURL: returnURL, + initialSynchronize: true ) .observe { [weak self] result in guard let self = self else { return } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginDataSource.swift index 0e3139b9843..be73a578e7e 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginDataSource.swift @@ -56,7 +56,8 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource { func synchronize() -> Future { apiClient.synchronize( clientSecret: clientSecret, - returnURL: returnURL + returnURL: returnURL, + initialSynchronize: false ) .chained { synchronize in if let linkLoginPane = synchronize.text?.linkLoginPane { @@ -73,7 +74,8 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource { clientSecret: clientSecret, sessionId: manifest.id, emailSource: manuallyEntered ? .userAction : .customerObject, - useMobileEndpoints: manifest.verified + useMobileEndpoints: manifest.verified, + pane: .linkLogin ) } @@ -89,7 +91,8 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource { amount: elementsSessionContext?.amount, currency: elementsSessionContext?.currency, incentiveEligibilitySession: elementsSessionContext?.incentiveEligibilitySession, - useMobileEndpoints: manifest.verified + useMobileEndpoints: manifest.verified, + pane: .linkLogin ) } @@ -107,7 +110,8 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource { return apiClient.synchronize( clientSecret: self.clientSecret, - returnURL: self.returnURL + returnURL: self.returnURL, + initialSynchronize: false ) } } @@ -125,6 +129,9 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource { // Marks the assertion as completed and logs possible errors during verified flows. func completeAssertionIfNeeded(possibleError: Error?) { guard manifest.verified else { return } - apiClient.completeAssertion(possibleError: possibleError) + apiClient.completeAssertion( + possibleError: possibleError, + pane: .linkLogin + ) } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupDataSource.swift index d5ddefa7e37..8831d650f6f 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupDataSource.swift @@ -54,7 +54,8 @@ final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDa func synchronize() -> Future { return apiClient.synchronize( clientSecret: clientSecret, - returnURL: returnURL + returnURL: returnURL, + initialSynchronize: false ) .chained { synchronize in if let networkingLinkSignup = synchronize.text?.networkingLinkSignupPane { @@ -71,7 +72,8 @@ final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDa clientSecret: clientSecret, sessionId: manifest.id, emailSource: manuallyEntered ? .userAction : .customerObject, - useMobileEndpoints: manifest.verified + useMobileEndpoints: manifest.verified, + pane: .networkingLinkSignupPane ) } @@ -90,7 +92,8 @@ final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDa amount: nil, currency: nil, incentiveEligibilitySession: nil, - useMobileEndpoints: manifest.verified + useMobileEndpoints: manifest.verified, + pane: .networkingLinkSignupPane ).chained { [weak self] response -> Future in guard let self else { return Promise(error: FinancialConnectionsSheetError.unknown( @@ -129,6 +132,9 @@ final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDa // Marks the assertion as completed and logs possible errors during verified flows. func completeAssertionIfNeeded(possibleError: Error?) { guard manifest.verified else { return } - apiClient.completeAssertion(possibleError: possibleError) + apiClient.completeAssertion( + possibleError: possibleError, + pane: .networkingLinkSignupPane + ) } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationDataSource.swift index 1cb9b6502fd..e635cd1f142 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationDataSource.swift @@ -106,7 +106,8 @@ final class NetworkingLinkVerificationDataSourceImplementation: NetworkingLinkVe return self.apiClient.synchronize( clientSecret: self.clientSecret, - returnURL: self.returnURL + returnURL: self.returnURL, + initialSynchronize: false ) } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthDataSource.swift index 186a6dcd02a..39d848cad1c 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthDataSource.swift @@ -77,7 +77,8 @@ final class PartnerAuthDataSourceImplementation: PartnerAuthDataSource { apiClient .synchronize( clientSecret: clientSecret, - returnURL: nil + returnURL: nil, + initialSynchronize: false ) .observe { [weak self] result in guard let self = self else { return } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPDataSource.swift index 2f072e8ce74..d72de902a76 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPDataSource.swift @@ -80,7 +80,8 @@ final class NetworkingOTPDataSourceImplementation: NetworkingOTPDataSource { clientSecret: clientSecret, sessionId: manifest.id, emailSource: .customerObject, - useMobileEndpoints: manifest.verified + useMobileEndpoints: manifest.verified, + pane: pane ) .chained { [weak self] lookupConsumerSessionResponse in self?.consumerSession = lookupConsumerSessionResponse.consumerSession @@ -120,7 +121,7 @@ final class NetworkingOTPDataSourceImplementation: NetworkingOTPDataSource { // Marks the assertion as completed and logs possible errors during verified flows. func completeAssertionIfNeeded(possibleError: Error?) { guard manifest.verified else { return } - apiClient.completeAssertion(possibleError: possibleError) + apiClient.completeAssertion(possibleError: possibleError, pane: pane) } }