diff --git a/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj b/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj index 8daffd5b739..4d549f34e51 100644 --- a/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj +++ b/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj @@ -80,6 +80,7 @@ 6A2318D428B3C36000F2A7D8 /* AccountPickerSelectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A2318D328B3C36000F2A7D8 /* AccountPickerSelectionListView.swift */; }; 6A2318D728B571E000F2A7D8 /* ManualEntryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A2318D628B571E000F2A7D8 /* ManualEntryViewController.swift */; }; 6A2318D928B57E5100F2A7D8 /* ManualEntryTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A2318D828B57E5100F2A7D8 /* ManualEntryTextField.swift */; }; + 6A370F322901CA2C00A6DB9B /* FinancialConnectionsSynchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A370F312901CA2C00A6DB9B /* FinancialConnectionsSynchronize.swift */; }; 6A4FA13928E11B3D00F07D42 /* CloseConfirmationAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A4FA13828E11B3D00F07D42 /* CloseConfirmationAlertHandler.swift */; }; 6A4FA13B28E3340B00F07D42 /* check@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6A4FA13A28E3340B00F07D42 /* check@3x.png */; }; 6A4FA13D28E346E600F07D42 /* warning_triangle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6A4FA13C28E346E600F07D42 /* warning_triangle@3x.png */; }; @@ -122,7 +123,6 @@ 6A8B4B0328CFC7C600128356 /* PaneLayoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A8B4B0228CFC7C600128356 /* PaneLayoutView.swift */; }; 6A8B4B0528CFD31800128356 /* PaneWithHeaderLayoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A8B4B0428CFD31800128356 /* PaneWithHeaderLayoutView.swift */; }; 6A8B4B0728D0BE4600128356 /* ConsentDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A8B4B0628D0BE4600128356 /* ConsentDataSource.swift */; }; - 6A9117E5287CA2A5007633D4 /* ConsentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A9117E4287CA2A5007633D4 /* ConsentModel.swift */; }; 6A9117E7287CB20D007633D4 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A9117E6287CB20D007633D4 /* String+Extensions.swift */; }; 6A9117EC287F4D87007633D4 /* DataAccessNoticeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A9117EB287F4D87007633D4 /* DataAccessNoticeViewController.swift */; }; 6A9117EE287F535C007633D4 /* DataAccessNoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A9117ED287F535C007633D4 /* DataAccessNoticeView.swift */; }; @@ -295,6 +295,7 @@ 6A2318D328B3C36000F2A7D8 /* AccountPickerSelectionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountPickerSelectionListView.swift; sourceTree = ""; }; 6A2318D628B571E000F2A7D8 /* ManualEntryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualEntryViewController.swift; sourceTree = ""; }; 6A2318D828B57E5100F2A7D8 /* ManualEntryTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualEntryTextField.swift; sourceTree = ""; }; + 6A370F312901CA2C00A6DB9B /* FinancialConnectionsSynchronize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinancialConnectionsSynchronize.swift; sourceTree = ""; }; 6A4FA13828E11B3D00F07D42 /* CloseConfirmationAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseConfirmationAlertHandler.swift; sourceTree = ""; }; 6A4FA13A28E3340B00F07D42 /* check@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "check@3x.png"; sourceTree = ""; }; 6A4FA13C28E346E600F07D42 /* warning_triangle@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "warning_triangle@3x.png"; sourceTree = ""; }; @@ -337,7 +338,6 @@ 6A8B4B0228CFC7C600128356 /* PaneLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaneLayoutView.swift; sourceTree = ""; }; 6A8B4B0428CFD31800128356 /* PaneWithHeaderLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaneWithHeaderLayoutView.swift; sourceTree = ""; }; 6A8B4B0628D0BE4600128356 /* ConsentDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentDataSource.swift; sourceTree = ""; }; - 6A9117E4287CA2A5007633D4 /* ConsentModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentModel.swift; sourceTree = ""; }; 6A9117E6287CB20D007633D4 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; 6A9117EB287F4D87007633D4 /* DataAccessNoticeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataAccessNoticeViewController.swift; sourceTree = ""; }; 6A9117ED287F535C007633D4 /* DataAccessNoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataAccessNoticeView.swift; sourceTree = ""; }; @@ -527,6 +527,7 @@ 3C34E0E82798EB33002618E4 /* FinancialConnectionsSession.swift */, 3C3ADCB92800C1E0008C24EF /* BankAccountToken.swift */, 3CAD99BA284E381100B163EB /* FinancialConnectionsInstitution.swift */, + 6A370F312901CA2C00A6DB9B /* FinancialConnectionsSynchronize.swift */, ); path = Models; sourceTree = ""; @@ -689,7 +690,6 @@ children = ( 6A8B4B0628D0BE4600128356 /* ConsentDataSource.swift */, 6A1BA4AB2858D5F100759697 /* ConsentViewController.swift */, - 6A9117E4287CA2A5007633D4 /* ConsentModel.swift */, 6ABE2D03285A2DEF0064B3A4 /* ConsentBodyView.swift */, 6A1BA4AD2858D76800759697 /* ConsentFooterView.swift */, ); @@ -1003,7 +1003,6 @@ 6A4FA14D28E477EA00F07D42 /* SpinnerIconView.swift in Sources */, 6AE2E5B528DE132300623523 /* AccountPickerAccountLoadErrorView.swift in Sources */, 6A7C861728D273940025B8DF /* SuccessIconView.swift in Sources */, - 6A9117E5287CA2A5007633D4 /* ConsentModel.swift in Sources */, 6A1E299E289DB83E00F99E9D /* AccountPickerDataSource.swift in Sources */, 6A99EF6628E708D200C76293 /* Button+Extensions.swift in Sources */, 6AD448B928C25F85002CABB0 /* ResetFlowDataSource.swift in Sources */, @@ -1074,6 +1073,7 @@ 6A2318D428B3C36000F2A7D8 /* AccountPickerSelectionListView.swift in Sources */, 6A1E29A228A1BB9100F99E9D /* PartnerAuthDataSource.swift in Sources */, 3C2432F3275AB8140031E9B9 /* StripeCore+Import.swift in Sources */, + 6A370F322901CA2C00A6DB9B /* FinancialConnectionsSynchronize.swift in Sources */, 6ABE2D06285B72A30064B3A4 /* ClickableLabel.swift in Sources */, 3CAD99C7285032E600B163EB /* InstitutionPicker.swift in Sources */, 6AE2E5AD28DB916E00623523 /* MerchantDataAccessView.swift in Sources */, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/APIVersion.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/APIVersion.swift index 78e2928ab74..3add7c5a826 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/APIVersion.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/APIVersion.swift @@ -15,7 +15,7 @@ struct APIVersion { - Note: Update this value when a new API version is ready for use in production. */ - private static let apiVersion: Int = 1 + static let apiVersion: Int = 1 // WARNING: this is also referenced in other places, so double check changes! private static let header = "financial_connections_client_api_beta=v\(apiVersion)" static func configureFinancialConnectionsAPIVersion(apiClient: STPAPIClient) { diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift index 1efdf003872..ed0bd65bf80 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift @@ -10,7 +10,7 @@ import Foundation protocol FinancialConnectionsAPIClient { - func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise + func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise func fetchFinancialConnectionsAccounts(clientSecret: String, startingAfterAccountId: String?) -> Promise @@ -76,11 +76,26 @@ extension STPAPIClient: FinancialConnectionsAPIClient { return self.get(resource: APIEndpointSessionReceipt, parameters: ["client_secret": clientSecret]) } - - func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise { - let body = FinancialConnectionsSessionsGenerateHostedUrlBody(clientSecret: clientSecret, fullscreen: true, hideCloseButton: true, appReturnUrl: returnURL) - return self.post(resource: APIEndpointGenerateHostedURL, - object: body) + + func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise { + let parameters: [String: Any] = [ + "client_secret": clientSecret, + "mobile": { + var mobileParameters: [String:Any] = [ + "sdk_type": "ios", + "fullscreen": true, + "hide_close_button": true, + "sdk_version": APIVersion.apiVersion, + ] + mobileParameters["app_return_url"] = returnURL + return mobileParameters + }(), + "locale": Locale.current.identifier, + ] + return self.post( + resource: "financial_connections/sessions/synchronize", + parameters: parameters + ) } func markConsentAcquired(clientSecret: String) -> Promise { diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSessionsGenerateHostedUrlBody.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSessionsGenerateHostedUrlBody.swift index 5853841e08e..ddcc245132a 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSessionsGenerateHostedUrlBody.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSessionsGenerateHostedUrlBody.swift @@ -8,13 +8,6 @@ import Foundation @_spi(STP) import StripeCore -struct FinancialConnectionsSessionsGenerateHostedUrlBody: Encodable { - let clientSecret: String - let fullscreen: Bool - let hideCloseButton: Bool - let appReturnUrl: String? -} - struct FinancialConnectionsSessionsClientSecretBody: Encodable { let clientSecret: String } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSynchronize.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSynchronize.swift new file mode 100644 index 00000000000..12daba6b75e --- /dev/null +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSynchronize.swift @@ -0,0 +1,55 @@ +// +// FinancialConnectionsSynchronize.swift +// StripeFinancialConnections +// +// Created by Krisjanis Gaidis on 10/20/22. +// + +import Foundation + +struct FinancialConnectionsSynchronize: Decodable { + + let manifest: FinancialConnectionsSessionManifest + let text: Text + + struct Text: Decodable { + let consentPane: FinancialConnectionsConsent + } +} + +struct FinancialConnectionsConsent: Decodable { + + let title: String + let body: Body + let aboveCta: String + let cta: String + let belowCta: String? + + let dataAccessNotice: FinancialConnectionsDataAccessNotice + + struct Body: Decodable { + let bullets: [BulletItem] + + struct BulletItem: Decodable { + let icon: String + let content: String + } + } +} + +struct FinancialConnectionsDataAccessNotice: Decodable { + let title: String + let body: Body + let learnMore: String + let cta: String + + struct Body: Decodable { + let bullets: [BulletItem] + + struct BulletItem: Decodable { + let icon: String + let title: String? + let content: String + } + } +} diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostController.swift index 8a23150c7fb..e78f84e6c4d 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostController.swift @@ -69,16 +69,16 @@ extension HostController: HostViewControllerDelegate { delegate?.hostController(self, viewController: viewController, didFinish: .failed(error: error)) } - func hostViewController(_ viewController: HostViewController, didFetch manifest: FinancialConnectionsSessionManifest) { + func hostViewController(_ viewController: HostViewController, didFetch synchronizePayload: FinancialConnectionsSynchronize) { guard useNative else { - continueWithWebFlow(manifest) + continueWithWebFlow(synchronizePayload.manifest) return } navigationController.configureAppearanceForNative() let dataManager = AuthFlowAPIDataManager( - manifest: manifest, + synchronizePayload: synchronizePayload, apiClient: api, clientSecret: clientSecret, analyticsClient: analyticsClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostViewController.swift index 05f0d2ea9ad..ca6f2348af1 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostViewController.swift @@ -18,7 +18,7 @@ protocol HostViewControllerDelegate: AnyObject { func hostViewController( _ viewController: HostViewController, - didFetch manifest: FinancialConnectionsSessionManifest + didFetch synchronizePayload: FinancialConnectionsSynchronize ) } @@ -96,9 +96,9 @@ extension HostViewController { .observe { [weak self] result in guard let self = self else { return } switch result { - case .success(let manifest): + case .success(let synchronizePayload): self.lastError = nil - self.delegate?.hostViewController(self, didFetch: manifest) + self.delegate?.hostViewController(self, didFetch: synchronizePayload) case .failure(let error): self.loadingView.activityIndicatorView.stp_stopAnimatingAndHide() self.loadingView.errorView.isHidden = false diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AuthFlowController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AuthFlowController.swift index ad5ee2ac5e5..ba95e8ad128 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AuthFlowController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AuthFlowController.swift @@ -574,7 +574,7 @@ private func CreatePaneViewController( case .consent: let consentDataSource = ConsentDataSourceImplementation( manifest: dataManager.manifest, - consentModel: ConsentModel(businessName: dataManager.manifest.businessName), + consent: dataManager.consent, apiClient: dataManager.apiClient, clientSecret: dataManager.clientSecret, analyticsClient: dataManager.analyticsClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AuthFlowDataManager.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AuthFlowDataManager.swift index e94265766b1..653d7e5bfef 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AuthFlowDataManager.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AuthFlowDataManager.swift @@ -10,6 +10,7 @@ import Foundation protocol AuthFlowDataManager: AnyObject { var manifest: FinancialConnectionsSessionManifest { get set } + var consent: FinancialConnectionsConsent { get } var apiClient: FinancialConnectionsAPIClient { get } var clientSecret: String { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } @@ -32,6 +33,7 @@ class AuthFlowAPIDataManager: AuthFlowDataManager { didUpdateManifest() } } + let consent: FinancialConnectionsConsent let apiClient: FinancialConnectionsAPIClient let clientSecret: String let analyticsClient: FinancialConnectionsAnalyticsClient @@ -44,12 +46,13 @@ class AuthFlowAPIDataManager: AuthFlowDataManager { var accountNumberLast4: String? init( - manifest: FinancialConnectionsSessionManifest, + synchronizePayload: FinancialConnectionsSynchronize, apiClient: FinancialConnectionsAPIClient, clientSecret: String, analyticsClient: FinancialConnectionsAnalyticsClient ) { - self.manifest = manifest + self.manifest = synchronizePayload.manifest + self.consent = synchronizePayload.text.consentPane self.apiClient = apiClient self.clientSecret = clientSecret self.analyticsClient = analyticsClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentBodyView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentBodyView.swift index 879e3147650..f41143e13e8 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentBodyView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentBodyView.swift @@ -6,7 +6,6 @@ // import Foundation -import SafariServices import UIKit @_spi(STP) import StripeCore @_spi(STP) import StripeUICore @@ -14,41 +13,24 @@ import UIKit @available(iOSApplicationExtension, unavailable) class ConsentBodyView: UIView { - private let bulletItems: [ConsentModel.BodyBulletItem] - private let dataAccessNoticeModel: DataAccessNoticeModel + private let bulletItems: [FinancialConnectionsConsent.Body.BulletItem] init( - bulletItems: [ConsentModel.BodyBulletItem], - dataAccessNoticeModel: DataAccessNoticeModel + bulletItems: [FinancialConnectionsConsent.Body.BulletItem], + didSelectURL: @escaping (URL) -> Void ) { self.bulletItems = bulletItems - self.dataAccessNoticeModel = dataAccessNoticeModel super.init(frame: .zero) - backgroundColor = .customBackgroundColor let verticalStackView = UIStackView() verticalStackView.axis = .vertical verticalStackView.spacing = 16 - - let linkAction: (URL) -> Void = { url in - if let scheme = url.scheme, scheme.contains("stripe") { - let dataAccessNoticeViewController = DataAccessNoticeViewController(model: dataAccessNoticeModel) - dataAccessNoticeViewController.modalTransitionStyle = .crossDissolve - dataAccessNoticeViewController.modalPresentationStyle = .overCurrentContext - // `false` for animations because we do a custom animation inside VC logic - UIViewController - .topMostViewController()? - .present(dataAccessNoticeViewController, animated: false, completion: nil) - } else { - SFSafariViewController.present(url: url) - } - } - bulletItems.forEach { item in + bulletItems.forEach { bulletItem in verticalStackView.addArrangedSubview( CreateLabelView( - text: item.text, - action: linkAction + text: bulletItem.content, + action: didSelectURL ) ) } @@ -101,20 +83,20 @@ private struct ConsentBodyViewUIViewRepresentable: UIViewRepresentable { func makeUIView(context: Context) -> ConsentBodyView { ConsentBodyView( bulletItems: [ - ConsentModel.BodyBulletItem( - iconUrl: URL(string: "https://www.google.com/image.png")!, - text: "Stripe will allow Goldilocks to access only the [data requested](https://www.google.com). We never share your login details with them." + FinancialConnectionsConsent.Body.BulletItem( + icon: "...", + content: "Stripe will allow Goldilocks to access only the [data requested](https://www.stripe.com). We never share your login details with them." ), - ConsentModel.BodyBulletItem( - iconUrl: URL(string: "https://www.google.com/image.png")!, - text: "Your data is encrypted for your protection." + FinancialConnectionsConsent.Body.BulletItem( + icon: "...", + content: "Your data is encrypted for your protection." ), - ConsentModel.BodyBulletItem( - iconUrl: URL(string: "https://www.google.com/image.png")!, - text: "You can [disconnect](meow.com) your accounts at any time." + FinancialConnectionsConsent.Body.BulletItem( + icon: "...", + content: "You can [disconnect](https://www.stripe.com) your accounts at any time." ), ], - dataAccessNoticeModel: DataAccessNoticeModel(businessName: "Coca-Cola Inc") + didSelectURL: { _ in } ) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentDataSource.swift index 9b0d3dc280f..61bec73c3ad 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentDataSource.swift @@ -10,7 +10,7 @@ import Foundation protocol ConsentDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } - var consentModel: ConsentModel { get } + var consent: FinancialConnectionsConsent { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } func markConsentAcquired() -> Promise @@ -19,20 +19,20 @@ protocol ConsentDataSource: AnyObject { final class ConsentDataSourceImplementation: ConsentDataSource { let manifest: FinancialConnectionsSessionManifest - let consentModel: ConsentModel + let consent: FinancialConnectionsConsent private let apiClient: FinancialConnectionsAPIClient private let clientSecret: String let analyticsClient: FinancialConnectionsAnalyticsClient init( manifest: FinancialConnectionsSessionManifest, - consentModel: ConsentModel, + consent: FinancialConnectionsConsent, apiClient: FinancialConnectionsAPIClient, clientSecret: String, analyticsClient: FinancialConnectionsAnalyticsClient ) { self.manifest = manifest - self.consentModel = consentModel + self.consent = consent self.apiClient = apiClient self.clientSecret = clientSecret self.analyticsClient = analyticsClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentFooterView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentFooterView.swift index 3b4c0a0a304..1b4986d9962 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentFooterView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentFooterView.swift @@ -13,12 +13,12 @@ import UIKit @available(iOSApplicationExtension, unavailable) class ConsentFooterView: UIView { + private let agreeButtonText: String private let didSelectAgree: () -> Void - private let didSelectManuallyVerify: (() -> Void)? private lazy var agreeButton: StripeUICore.Button = { let agreeButton = Button(configuration: .financialConnectionsPrimary) - agreeButton.title = "Agree" + agreeButton.title = agreeButtonText agreeButton.addTarget(self, action: #selector(didSelectAgreeButton), for: .touchUpInside) agreeButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ @@ -28,15 +28,15 @@ class ConsentFooterView: UIView { }() init( - footerText: String, + aboveCtaText: String, + ctaText: String, + belowCtaText: String?, didSelectAgree: @escaping () -> Void, - didSelectManuallyVerify: (() -> Void)?, // null if manual entry disabled - showManualEntryBusinessDaysNotice: Bool + didSelectURL: @escaping (URL) -> Void ) { + self.agreeButtonText = ctaText self.didSelectAgree = didSelectAgree - self.didSelectManuallyVerify = didSelectManuallyVerify super.init(frame: .zero) - backgroundColor = .customBackgroundColor let termsAndPrivacyPolicyLabel = ClickableLabel( @@ -46,7 +46,10 @@ class ConsentFooterView: UIView { textColor: .textSecondary, alignCenter: true ) - termsAndPrivacyPolicyLabel.setText(footerText) + termsAndPrivacyPolicyLabel.setText( + aboveCtaText, + action: didSelectURL + ) let verticalStackView = UIStackView( arrangedSubviews: [ @@ -57,17 +60,7 @@ class ConsentFooterView: UIView { verticalStackView.axis = .vertical verticalStackView.spacing = 20 - if let didSelectManuallyVerify = didSelectManuallyVerify { - let text: String - if showManualEntryBusinessDaysNotice { - let localizedManuallyVerifyText = STPLocalizedString("Manually verify instead", "The title of a button that allows the user to press it to enter bank account details manually.") - let localizedBusinessDaysNotice = STPLocalizedString("(takes 1-2 business days)", "An extra notice next to a title of a button that allows the user to press it to enter bank account details manually. The full text looks like: 'Manually verify instead (takes 1-2 business days)'") - text = "[\(localizedManuallyVerifyText)](https://www.urlIsIgnored.com) \(localizedBusinessDaysNotice)" - } else { - let localizedText = STPLocalizedString("Enter account details manually instead", "The title of a button that allows the user to press it to enter bank account details manually.") - text = "[\(localizedText)](https://www.urlIsIgnored.com)" - } - + if let belowCtaText = belowCtaText { let manuallyVerifyLabel = ClickableLabel( font: UIFont.stripeFont(forTextStyle: .detail), boldFont: UIFont.stripeFont(forTextStyle: .detailEmphasized), @@ -76,10 +69,8 @@ class ConsentFooterView: UIView { alignCenter: true ) manuallyVerifyLabel.setText( - text, - action: { _ in - didSelectManuallyVerify() - } + belowCtaText, + action: didSelectURL ) verticalStackView.addArrangedSubview(manuallyVerifyLabel) verticalStackView.setCustomSpacing(24, after: agreeButton) @@ -111,10 +102,11 @@ private struct ConsentFooterViewUIViewRepresentable: UIViewRepresentable { func makeUIView(context: Context) -> ConsentFooterView { ConsentFooterView( - footerText: "You agree to Stripe's [Terms](https://stripe.com/legal/end-users#linked-financial-account-terms) and [Privacy Policy](https://stripe.com/privacy). [Learn more](https://stripe.com/privacy-center/legal#linking-financial-accounts)", + aboveCtaText: "You agree to Stripe's [Terms](https://stripe.com/legal/end-users#linked-financial-account-terms) and [Privacy Policy](https://stripe.com/privacy). [Learn more](https://stripe.com/privacy-center/legal#linking-financial-accounts)", + ctaText: "Agree", + belowCtaText: "[Manually verify instead](https://www.stripe.com) (takes 1-2 business days)", didSelectAgree: {}, - didSelectManuallyVerify: {}, - showManualEntryBusinessDaysNotice: false + didSelectURL: { _ in } ) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentModel.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentModel.swift deleted file mode 100644 index 9be49695205..00000000000 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentModel.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// ConsentModel.swift -// StripeFinancialConnections -// -// Created by Krisjanis Gaidis on 7/11/22. -// - -import Foundation - -// Temporary model until we get this data from backend. -struct ConsentModel { - - struct BodyBulletItem { - let iconUrl: URL - let text: String - } - - private let businessName: String - - var headerText: String { - return "\(businessName) works with **Stripe** to link your accounts." - } - - var bodyItems: [BodyBulletItem] { - return [ - BodyBulletItem( - iconUrl: URL(string: "https://www.cdn.stripe.com/image.png")!, - text: "Stripe will allow \(businessName) to access only the [data requested](stripe://bottom-sheet). We never share your login details with them." - ), - BodyBulletItem( - iconUrl: URL(string: "https://www.cdn.stripe.com/image.png")!, - text: "Your data is encrypted for your protection." - ), - BodyBulletItem( - iconUrl: URL(string: "https://www.cdn.stripe.com/image.png")!, - text: "You can [disconnect](https://support.stripe.com/user/how-do-i-disconnect-my-linked-financial-account) your accounts at any time." - ), - ] - } - - let footerText = "You agree to Stripe's [Terms](https://stripe.com/legal/end-users#linked-financial-account-terms) and [Privacy Policy](https://stripe.com/privacy). [Learn more](https://stripe.com/privacy-center/legal#linking-financial-accounts)" - - var dataAccessNoticeModel: DataAccessNoticeModel { - return DataAccessNoticeModel(businessName: businessName) - } - - init(businessName: String?) { - self.businessName = businessName ?? "Unknown" - } -} - -// Temporary model until we get this data from backend. -struct DataAccessNoticeModel { - - struct BodyBulletItem { - let title: String - let subtitle: String - } - - private let businessName: String - - var headerText: String { - return "Data requested by \(businessName) for the accounts you link:" - } - - let bodyItems: [BodyBulletItem] = [ - BodyBulletItem( - title: "Account owner information", - subtitle: "Account owner name and mailing address associated with your account" - ), - BodyBulletItem( - title: "Account details", - subtitle: "Account number, routing number, account type, account nickname" - ), - ] - - let footerText = "[Learn more about data access](https://support.stripe.com/user/questions/what-data-does-stripe-access-from-my-linked-financial-account)" - - init(businessName: String) { - self.businessName = businessName - } -} diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentViewController.swift index 9dfcfc84682..f9e91d980c5 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentViewController.swift @@ -7,6 +7,7 @@ import Foundation import UIKit +import SafariServices @_spi(STP) import StripeUICore @_spi(STP) import StripeCore @@ -29,19 +30,28 @@ class ConsentViewController: UIViewController { linkFont: .stripeFont(forTextStyle: .subtitle), textColor: .textPrimary ) + titleLabel.setText( + dataSource.consent.title, + action: { [weak self] url in + // there are no known cases where we add a link to the title + // but we add this handling regardless in case this changes + // in the future + self?.didSelectURL(url) + } + ) return titleLabel }() private lazy var footerView: ConsentFooterView = { return ConsentFooterView( - footerText: dataSource.consentModel.footerText, + aboveCtaText: dataSource.consent.aboveCta, + ctaText: dataSource.consent.cta, + belowCtaText: dataSource.consent.belowCta, didSelectAgree: { [weak self] in self?.didSelectAgree() }, - didSelectManuallyVerify: dataSource.manifest.allowManualEntry ? { [weak self] in - guard let self = self else { return } - self.delegate?.consentViewControllerDidSelectManuallyVerify(self) - } : nil, - showManualEntryBusinessDaysNotice: !dataSource.manifest.customManualEntryHandling && dataSource.manifest.manualEntryUsesMicrodeposits + didSelectURL: { [weak self] url in + self?.didSelectURL(url) + } ) }() @@ -58,12 +68,13 @@ class ConsentViewController: UIViewController { super.viewDidLoad() view.backgroundColor = .customBackgroundColor - titleLabel.setText(dataSource.consentModel.headerText) let paneLayoutView = PaneWithCustomHeaderLayoutView( headerView: titleLabel, contentView: ConsentBodyView( - bulletItems: dataSource.consentModel.bodyItems, - dataAccessNoticeModel: dataSource.consentModel.dataAccessNoticeModel + bulletItems: dataSource.consent.body.bullets, + didSelectURL: { [weak self] url in + self?.didSelectURL(url) + } ), footerView: footerView ) @@ -98,4 +109,55 @@ class ConsentViewController: UIViewController { self.footerView.setIsLoading(false) } } + + // this function will get called when user taps + // on ANY link returned from backend + private func didSelectURL(_ url: URL) { + if url.scheme == "stripe" { + if url.host == "manual-entry" { + dataSource + .analyticsClient + .log( + eventName: "click.manual_entry", + parameters: ["pane": FinancialConnectionsSessionManifest.NextPane.consent.rawValue] + ) + + delegate?.consentViewControllerDidSelectManuallyVerify(self) + } else if url.host == "data-access-notice" { + dataSource + .analyticsClient + .log( + eventName: "click.data_requested", + parameters: ["pane": FinancialConnectionsSessionManifest.NextPane.consent.rawValue] + ) + + let dataAccessNoticeViewController = DataAccessNoticeViewController( + model: dataSource.consent.dataAccessNotice, + didSelectURL: { [weak self] url in + self?.didSelectURL(url) + } + ) + dataAccessNoticeViewController.modalTransitionStyle = .crossDissolve + dataAccessNoticeViewController.modalPresentationStyle = .overCurrentContext + // `false` for animations because we do a custom animation inside VC logic + UIViewController + .topMostViewController()? + .present(dataAccessNoticeViewController, animated: false, completion: nil) + } + } else { + if + let urlParameters = URLComponents(url: url, resolvingAgainstBaseURL: true), + let eventName = urlParameters.queryItems?.first(where: { $0.name == "eventName" })?.value + { + dataSource + .analyticsClient + .log( + eventName: eventName, + parameters: ["pane": FinancialConnectionsSessionManifest.NextPane.consent.rawValue] + ) + } + + SFSafariViewController.present(url: url) + } + } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionPicker.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionPicker.swift index 0353211ddb9..6623c9386c7 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionPicker.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionPicker.swift @@ -283,7 +283,10 @@ extension InstitutionPicker: FeaturedInstitutionGridViewDelegate { ) { dataSource.analyticsClient.log( eventName: "search.featured_institution_selected", - parameters: ["pane": FinancialConnectionsSessionManifest.NextPane.institutionPicker.rawValue] + parameters: [ + "pane": FinancialConnectionsSessionManifest.NextPane.institutionPicker.rawValue, + "institution_id": institution.id, + ] ) didSelectInstitution(institution) } @@ -301,7 +304,10 @@ extension InstitutionPicker: InstitutionSearchTableViewDelegate { ) { dataSource.analyticsClient.log( eventName: "search.search_result_selected", - parameters: ["pane": FinancialConnectionsSessionManifest.NextPane.institutionPicker.rawValue] + parameters: [ + "pane": FinancialConnectionsSessionManifest.NextPane.institutionPicker.rawValue, + "institution_id": institution.id, + ] ) didSelectInstitution(institution) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeView.swift index cddeb335a7b..6e60e096c7a 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeView.swift @@ -13,38 +13,42 @@ import UIKit @available(iOSApplicationExtension, unavailable) final class DataAccessNoticeView: UIView { - private let model: DataAccessNoticeModel private let didSelectOKAction: () -> Void init( - model: DataAccessNoticeModel, - didSelectOK: @escaping () -> Void + model: FinancialConnectionsDataAccessNotice, + didSelectOK: @escaping () -> Void, + didSelectURL: @escaping (URL) -> Void ) { - self.model = model self.didSelectOKAction = didSelectOK super.init(frame: .zero) - backgroundColor = .customBackgroundColor let padding: CGFloat = 24 - let verticalStackView = UIStackView( arrangedSubviews: [ - createContentView(), - createFooterView(), + CreateContentView( + headerTitle: model.title, + bulletItems: model.body.bullets, + learnMoreText: model.learnMore, + didSelectURL: didSelectURL + ), + CreateFooterView( + cta: model.cta, + actionTarget: self + ), ] ) verticalStackView.axis = .vertical verticalStackView.spacing = 24 - addAndPinSubviewToSafeArea( - verticalStackView, - insets: NSDirectionalEdgeInsets( - top: padding, - leading: padding, - bottom: padding, - trailing: padding - ) + verticalStackView.isLayoutMarginsRelativeArrangement = true + verticalStackView.directionalLayoutMargins = NSDirectionalEdgeInsets( + top: padding, + leading: padding, + bottom: padding, + trailing: padding ) + addAndPinSubview(verticalStackView) } required init?(coder: NSCoder) { @@ -56,53 +60,6 @@ final class DataAccessNoticeView: UIView { roundCorners() // needs to be in `layoutSubviews` to get the correct size for the mask } - private func createContentView() -> UIView { - let verticalStackView = UIStackView( - arrangedSubviews: { - var subviews: [UIView] = [] - subviews.append(CreateHeaderView(text: model.headerText)) - model.bodyItems.forEach { item in - subviews.append( - CreateBulletinView( - title: item.title, - subtitle: item.subtitle - ) - ) - } - subviews.append(createLearnMoreLabel()) - return subviews - }() - ) - verticalStackView.axis = .vertical - verticalStackView.spacing = 16 - - return verticalStackView - } - - private func createLearnMoreLabel() -> UIView { - let label = ClickableLabel( - font: .stripeFont(forTextStyle: .caption), - boldFont: .stripeFont(forTextStyle: .captionEmphasized), - linkFont: .stripeFont(forTextStyle: .captionEmphasized), - textColor: .textSecondary - ) - label.setText(model.footerText) - return label - } - - private func createFooterView() -> UIView { - let okButton = Button(configuration: .financialConnectionsPrimary) - okButton.title = "OK" - - okButton.addTarget(self, action: #selector(didSelectOK), for: .touchUpInside) - okButton.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - okButton.heightAnchor.constraint(equalToConstant: 56), - ]) - - return okButton - } - private func roundCorners() { clipsToBounds = true let path = UIBezierPath( @@ -115,43 +72,94 @@ final class DataAccessNoticeView: UIView { layer.mask = mask } - @IBAction private func didSelectOK() { + @IBAction fileprivate func didSelectOK() { didSelectOKAction() } } -private func CreateHeaderView(text: String) -> UIView { - let headerLabel = UILabel() - headerLabel.numberOfLines = 0 - headerLabel.text = text - headerLabel.font = .stripeFont(forTextStyle: .bodyEmphasized) - headerLabel.textColor = UIColor.textPrimary - headerLabel.textAlignment = .left - return headerLabel +@available(iOSApplicationExtension, unavailable) +private func CreateContentView( + headerTitle: String, + bulletItems: [FinancialConnectionsDataAccessNotice.Body.BulletItem], + learnMoreText: String, + didSelectURL: @escaping (URL) -> Void +) -> UIView { + let verticalStackView = UIStackView( + arrangedSubviews: { + var subviews: [UIView] = [] + subviews.append( + CreateHeaderView( + text: headerTitle, + didSelectURL: didSelectURL + ) + ) + bulletItems.forEach { bulletItem in + subviews.append( + CreateBulletinView( + title: bulletItem.title, + subtitle: bulletItem.content, + didSelectURL: didSelectURL + ) + ) + } + subviews.append( + CreateLearnMoreLabel( + text: learnMoreText, + didSelectURL: didSelectURL + ) + ) + return subviews + }() + ) + verticalStackView.axis = .vertical + verticalStackView.spacing = 16 + return verticalStackView } -private func CreateBulletinView(title: String, subtitle: String) -> UIView { - let primaryLabel = UILabel() - primaryLabel.numberOfLines = 0 - primaryLabel.text = title - primaryLabel.font = .stripeFont(forTextStyle: .detailEmphasized) - primaryLabel.textColor = UIColor.textPrimary - primaryLabel.textAlignment = .left - let secondaryLabel = UILabel() - secondaryLabel.numberOfLines = 0 - secondaryLabel.text = subtitle - secondaryLabel.font = .stripeFont(forTextStyle: .caption) - secondaryLabel.textColor = UIColor.textSecondary - secondaryLabel.textAlignment = .left - let verticalStackView = UIStackView( - arrangedSubviews: [ - primaryLabel, - secondaryLabel, - ] +@available(iOSApplicationExtension, unavailable) +private func CreateHeaderView( + text: String, + didSelectURL: @escaping (URL) -> Void +) -> UIView { + let headerLabel = ClickableLabel( + font: .stripeFont(forTextStyle: .bodyEmphasized), + boldFont: .stripeFont(forTextStyle: .bodyEmphasized), + linkFont: .stripeFont(forTextStyle: .bodyEmphasized), + textColor: .textPrimary ) + headerLabel.setText(text, action: didSelectURL) + return headerLabel +} + +@available(iOSApplicationExtension, unavailable) +private func CreateBulletinView( + title: String?, + subtitle: String, + didSelectURL: @escaping (URL) -> Void +) -> UIView { + let verticalStackView = UIStackView() verticalStackView.axis = .vertical verticalStackView.spacing = 5 - + if let title = title { + let primaryLabel = ClickableLabel( + font: .stripeFont(forTextStyle: .detailEmphasized), + boldFont: .stripeFont(forTextStyle: .detailEmphasized), + linkFont: .stripeFont(forTextStyle: .detailEmphasized), + textColor: .textPrimary + ) + primaryLabel.setText(title, action: didSelectURL) + verticalStackView.addArrangedSubview(primaryLabel) + } + + let secondaryLabel = ClickableLabel( + font: .stripeFont(forTextStyle: .caption), + boldFont: .stripeFont(forTextStyle: .captionEmphasized), + linkFont: .stripeFont(forTextStyle: .captionEmphasized), + textColor: .textSecondary + ) + secondaryLabel.setText(subtitle, action: didSelectURL) + verticalStackView.addArrangedSubview(secondaryLabel) + let imageView = UIImageView(image: Image.close.makeImage(template: false)) imageView.contentMode = .scaleAspectFit imageView.translatesAutoresizingMaskIntoConstraints = false @@ -171,6 +179,35 @@ private func CreateBulletinView(title: String, subtitle: String) -> UIView { return horizontalStackView } +@available(iOSApplicationExtension, unavailable) +private func CreateLearnMoreLabel( + text: String, + didSelectURL: @escaping (URL) -> Void +) -> UIView { + let label = ClickableLabel( + font: .stripeFont(forTextStyle: .caption), + boldFont: .stripeFont(forTextStyle: .captionEmphasized), + linkFont: .stripeFont(forTextStyle: .captionEmphasized), + textColor: .textSecondary + ) + label.setText(text, action: didSelectURL) + return label +} + +@available(iOSApplicationExtension, unavailable) +private func CreateFooterView( + cta: String, + actionTarget: DataAccessNoticeView +) -> UIView { + let okButton = Button(configuration: .financialConnectionsPrimary) + okButton.title = cta + okButton.addTarget(actionTarget, action: #selector(DataAccessNoticeView.didSelectOK), for: .touchUpInside) + okButton.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + okButton.heightAnchor.constraint(equalToConstant: 56), + ]) + return okButton +} #if DEBUG @@ -182,8 +219,18 @@ private struct DataAccessNoticeViewUIViewRepresentable: UIViewRepresentable { func makeUIView(context: Context) -> DataAccessNoticeView { DataAccessNoticeView( - model: DataAccessNoticeModel(businessName: "Coca-Cola Inc"), - didSelectOK: {} + model: FinancialConnectionsDataAccessNotice( + title: "", + body: FinancialConnectionsDataAccessNotice.Body( + bullets: [ + FinancialConnectionsDataAccessNotice.Body.BulletItem(icon: "", title: "...", content: "...") + ] + ), + learnMore: "...", + cta: "..." + ), + didSelectOK: {}, + didSelectURL: { _ in } ) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeViewController.swift index 44288591714..e3f5747e500 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeViewController.swift @@ -11,12 +11,18 @@ import UIKit @available(iOSApplicationExtension, unavailable) final class DataAccessNoticeViewController: UIViewController { - private let model: DataAccessNoticeModel - private var openContraint: NSLayoutConstraint? = nil - private var closeContraint: NSLayoutConstraint? = nil + private let model: FinancialConnectionsDataAccessNotice + private let didSelectURL: (URL) -> Void + + private var openContraint: NSLayoutConstraint? + private var closeContraint: NSLayoutConstraint? - init(model: DataAccessNoticeModel) { + init( + model: FinancialConnectionsDataAccessNotice, + didSelectURL: @escaping (URL) -> Void + ) { self.model = model + self.didSelectURL = didSelectURL super.init(nibName: nil, bundle: nil) } @@ -34,8 +40,9 @@ final class DataAccessNoticeViewController: UIViewController { let dataAccessNoticeView = DataAccessNoticeView( model: model, didSelectOK: { [weak self] in - self?.dismiss(animated: true) - } + self?.dismiss(animated: true) + }, + didSelectURL: didSelectURL ) view.addSubview(dataAccessNoticeView) dataAccessNoticeView.translatesAutoresizingMaskIntoConstraints = false diff --git a/StripeFinancialConnections/StripeFinancialConnectionsTests/AccountFetcherTests.swift b/StripeFinancialConnections/StripeFinancialConnectionsTests/AccountFetcherTests.swift index c5e00bdbc8e..056a880442e 100644 --- a/StripeFinancialConnections/StripeFinancialConnectionsTests/AccountFetcherTests.swift +++ b/StripeFinancialConnections/StripeFinancialConnectionsTests/AccountFetcherTests.swift @@ -43,8 +43,8 @@ class PaginatedAPIClient: FinancialConnectionsAPIClient { // MARK: - FinancialConnectionsAPIClient - func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise { - return Promise() + func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise { + return Promise() } func fetchFinancialConnectionsAccounts(clientSecret: String, diff --git a/StripeFinancialConnections/StripeFinancialConnectionsTests/FinancialConnectionsSheetTests.swift b/StripeFinancialConnections/StripeFinancialConnectionsTests/FinancialConnectionsSheetTests.swift index 99b7c8445bd..615aadb4b00 100644 --- a/StripeFinancialConnections/StripeFinancialConnectionsTests/FinancialConnectionsSheetTests.swift +++ b/StripeFinancialConnections/StripeFinancialConnectionsTests/FinancialConnectionsSheetTests.swift @@ -19,8 +19,8 @@ class EmptyFinancialConnectionsAPIClient: FinancialConnectionsAPIClient { return Promise() } - func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise { - return Promise() + func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise { + return Promise() } func markConsentAcquired(clientSecret: String) -> Promise { diff --git a/StripeFinancialConnections/StripeFinancialConnectionsTests/SessionFetcherTests.swift b/StripeFinancialConnections/StripeFinancialConnectionsTests/SessionFetcherTests.swift index 6f18640ad48..51743635432 100644 --- a/StripeFinancialConnections/StripeFinancialConnectionsTests/SessionFetcherTests.swift +++ b/StripeFinancialConnections/StripeFinancialConnectionsTests/SessionFetcherTests.swift @@ -24,8 +24,8 @@ class NoMoreAccountSessionAPIClient: FinancialConnectionsAPIClient { // MARK: - FinancialConnectionsAPIClient - func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise { - return Promise() + func generateSessionManifest(clientSecret: String, returnURL: String?) -> Promise { + return Promise() } func fetchFinancialConnectionsAccounts(clientSecret: String, startingAfterAccountId: String?) -> Promise {