Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update StripeConnect's FinancialConnections integration to work with public key override. #4379

Merged
merged 5 commits into from
Dec 21, 2024
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
4 changes: 4 additions & 0 deletions StripeConnect/StripeConnect.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
41814EEF2C6BEF2C0014EB5E /* FetchInitParamsMessageHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41814EEE2C6BEF2C0014EB5E /* FetchInitParamsMessageHandlerTests.swift */; };
41814EF12C6BF94B0014EB5E /* OnExitMessageHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41814EF02C6BF94B0014EB5E /* OnExitMessageHandlerTests.swift */; };
41814EF32C6BFA4B0014EB5E /* OnLoaderStartMessageHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41814EF22C6BFA4B0014EB5E /* OnLoaderStartMessageHandlerTests.swift */; };
418570562D13CA3700DE3FBE /* FinancialConnectionsPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418570552D13CA3400DE3FBE /* FinancialConnectionsPresenterTests.swift */; };
4186664A2C66AC66003DB62E /* OnSetterFunctionCalledMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418666492C66AC66003DB62E /* OnSetterFunctionCalledMessageHandler.swift */; };
4186664C2C66AC8C003DB62E /* OnLoaderStartMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4186664B2C66AC8C003DB62E /* OnLoaderStartMessageHandler.swift */; };
4186664E2C66ACB3003DB62E /* OnLoadErrorMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4186664D2C66ACB3003DB62E /* OnLoadErrorMessageHandler.swift */; };
Expand Down Expand Up @@ -214,6 +215,7 @@
41814EEE2C6BEF2C0014EB5E /* FetchInitParamsMessageHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchInitParamsMessageHandlerTests.swift; sourceTree = "<group>"; };
41814EF02C6BF94B0014EB5E /* OnExitMessageHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnExitMessageHandlerTests.swift; sourceTree = "<group>"; };
41814EF22C6BFA4B0014EB5E /* OnLoaderStartMessageHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnLoaderStartMessageHandlerTests.swift; sourceTree = "<group>"; };
418570552D13CA3400DE3FBE /* FinancialConnectionsPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinancialConnectionsPresenterTests.swift; sourceTree = "<group>"; };
418666492C66AC66003DB62E /* OnSetterFunctionCalledMessageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnSetterFunctionCalledMessageHandler.swift; sourceTree = "<group>"; };
4186664B2C66AC8C003DB62E /* OnLoaderStartMessageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnLoaderStartMessageHandler.swift; sourceTree = "<group>"; };
4186664D2C66ACB3003DB62E /* OnLoadErrorMessageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnLoadErrorMessageHandler.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -495,6 +497,7 @@
41814EE62C6BC8690014EB5E /* Internal */ = {
isa = PBXGroup;
children = (
418570552D13CA3400DE3FBE /* FinancialConnectionsPresenterTests.swift */,
E6660DBA2CDDC5BC002A7631 /* Analytics */,
E640C9D12CBF4790009D0C6E /* AuthenticatedWebView */,
E6660DBF2CDE7D48002A7631 /* Extensions */,
Expand Down Expand Up @@ -966,6 +969,7 @@
41814EF12C6BF94B0014EB5E /* OnExitMessageHandlerTests.swift in Sources */,
4161C2732C9D0A8A005BD67C /* AccountOnboardingViewControllerTests.swift in Sources */,
E6F485FE2C9E36B2000D914F /* PaymentDetailsViewControllerTests.swift in Sources */,
418570562D13CA3700DE3FBE /* FinancialConnectionsPresenterTests.swift in Sources */,
E6660CFB2CC2F438002A7631 /* SetCollectMobileFinancialConnectionsResultTests.swift in Sources */,
416E9E782C753B7900A0B917 /* ConnectComponentWebViewControllerTests.swift in Sources */,
410D0FD42C6D051B009B0E26 /* OpenAuthenticatedWebViewMessageHandlerTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,42 @@ import UIKit

/// Wraps `FinancialConnectionsSheet` for easy dependency injection in tests
class FinancialConnectionsPresenter {

@MainActor
func presentForToken(
apiClient: STPAPIClient,
@available(iOS 15, *)
func makeSheet(
componentManager: EmbeddedComponentManager,
clientSecret: String,
connectedAccountId: String,
from presentingViewController: UIViewController
) async -> FinancialConnectionsSheet.TokenResult {
let financialConnectionsSheet = FinancialConnectionsSheet(
financialConnectionsSessionClientSecret: clientSecret
)
) -> FinancialConnectionsSheet {
let financialConnectionsSheet: FinancialConnectionsSheet = .init(financialConnectionsSessionClientSecret: clientSecret)
// FC needs the connected account ID to be configured on the API Client
// Make a copy before modifying so we don't unexpectedly modify the shared API client
financialConnectionsSheet.apiClient = apiClient.makeCopy()
financialConnectionsSheet.apiClient = componentManager.apiClient.makeCopy()

// FC expects a public key and not a UK. If there is a public key override we should use that.
if let publicKeyOverride = componentManager.publicKeyOverride {
financialConnectionsSheet.apiClient.publishableKey = publicKeyOverride
}

financialConnectionsSheet.apiClient.stripeAccount = connectedAccountId

return financialConnectionsSheet
}

@MainActor
@available(iOS 15, *)
func presentForToken(
componentManager: EmbeddedComponentManager,
clientSecret: String,
connectedAccountId: String,
from presentingViewController: UIViewController
) async -> FinancialConnectionsSheet.TokenResult {
let financialConnectionsSheet = makeSheet(componentManager: componentManager,
clientSecret: clientSecret,
connectedAccountId: connectedAccountId,
from: presentingViewController)
return await withCheckedContinuation { continuation in
financialConnectionsSheet.presentForToken(from: presentingViewController) { result in
continuation.resume(returning: result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ private extension ConnectComponentWebViewController {
func openFinancialConnections(_ args: OpenFinancialConnectionsMessageHandler.Payload) {
Task { @MainActor in
let result = await financialConnectionsPresenter.presentForToken(
apiClient: componentManager.apiClient,
componentManager: componentManager,
clientSecret: args.clientSecret,
connectedAccountId: args.connectedAccountId,
from: self
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// FinancialConnectionsPresenterTests.swift
// StripeConnect
//
// Created by Chris Mays on 12/18/24.
//
@_spi(PrivateBetaConnect) @_spi(DashboardOnly) @testable import StripeConnect
@testable import StripeFinancialConnections

import UIKit
import XCTest

class FinancialConnectionsPresenterTests: XCTestCase {

@MainActor
func testStandardPresent() {
let presenter = FinancialConnectionsPresenter()

let clientSecret = "client_secret"
let connectedAccountId = "account_1234"
let publishableKey = "pk_12"

let componentManager = EmbeddedComponentManager(apiClient: .init(publishableKey: publishableKey),
appearance: .default,
fonts: [],
fetchClientSecret: {return nil})

let sheet = presenter.makeSheet(componentManager: componentManager, clientSecret: clientSecret, connectedAccountId: connectedAccountId, from: .init())

XCTAssertEqual(sheet.apiClient.publishableKey, publishableKey)
XCTAssertEqual(sheet.apiClient.stripeAccount, connectedAccountId)
}

@MainActor
func testPresentWithPublicKeyOverride() {
let clientSecret = "client_secret"
let connectedAccountId = "account_1234"
let ukKey = "uk_123"
let publishableKey = "pk_12"

let presenter = FinancialConnectionsPresenter()
let componentManager = EmbeddedComponentManager(apiClient: .init(publishableKey: ukKey),
appearance: .default,
publicKeyOverride: publishableKey,
baseURLOverride: nil)

let sheet = presenter.makeSheet(componentManager: componentManager, clientSecret: clientSecret, connectedAccountId: connectedAccountId, from: .init())


XCTAssertEqual(sheet.apiClient.publishableKey, publishableKey)
XCTAssertEqual(sheet.apiClient.stripeAccount, connectedAccountId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -463,8 +463,9 @@ class ConnectComponentWebViewControllerTests: XCTestCase {
let componentManager = componentManagerAssertingOnFetch()
let session = try FinancialConnectionsSessionMock.default.make()

let financialConnectionsPresenter = MockFinancialConnectionsPresenter { apiClient, secret, connectedAccountId, vc in
XCTAssert(apiClient === componentManager.apiClient)
let financialConnectionsPresenter = MockFinancialConnectionsPresenter { compManager, secret, connectedAccountId, vc in
XCTAssert(compManager.apiClient == componentManager.apiClient)
XCTAssert(compManager.publicKeyOverride == componentManager.publicKeyOverride)
Comment on lines +467 to +468
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doublechecking – STPAPIClient subclasses NSObject and doesn't override isEqual, so I think this should just do pointer-comparison, right? Just want to make sure the equality comparison does what we think it should.

Non-blocking: Also, if we're using == anyway, this could be XCTAssertEqual which would provide a better failure message if the test fails.

Suggested change
XCTAssert(compManager.apiClient == componentManager.apiClient)
XCTAssert(compManager.publicKeyOverride == componentManager.publicKeyOverride)
XCTAssertEqual(compManager.apiClient, componentManager.apiClient)
XCTAssertEqual(compManager.publicKeyOverride, componentManager.publicKeyOverride)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this should be doing pointer comparison.

XCTAssertEqual(secret, "client_secret_123")
XCTAssertEqual(connectedAccountId, "acct_1234")
XCTAssert(vc is ConnectComponentWebViewController)
Expand Down Expand Up @@ -588,14 +589,14 @@ private class MockAuthenticatedWebViewManager: AuthenticatedWebViewManager {

private class MockFinancialConnectionsPresenter: FinancialConnectionsPresenter {
var overridePresentForToken: (
_ apiClient: STPAPIClient,
_ componentManager: EmbeddedComponentManager,
_ clientSecret: String,
_ connectedAccountId: String,
_ presentingViewController: UIViewController
) async -> FinancialConnectionsSheet.TokenResult

init(overridePresentForToken: @escaping (
_ apiClient: STPAPIClient,
_ componentManager: EmbeddedComponentManager,
_ clientSecret: String,
_ connectedAccountId: String,
_ presentingViewController: UIViewController
Expand All @@ -604,11 +605,11 @@ private class MockFinancialConnectionsPresenter: FinancialConnectionsPresenter {
}

override func presentForToken(
apiClient: STPAPIClient,
componentManager: EmbeddedComponentManager,
clientSecret: String,
connectedAccountId: String,
from presentingViewController: UIViewController
) async -> FinancialConnectionsSheet.TokenResult {
await overridePresentForToken(apiClient, clientSecret, connectedAccountId, presentingViewController)
await overridePresentForToken(componentManager, clientSecret, connectedAccountId, presentingViewController)
}
}
Loading