Skip to content

Commit 7e59072

Browse files
committed
Add FC Example app integration
1 parent 3647c7e commit 7e59072

File tree

4 files changed

+132
-16
lines changed

4 files changed

+132
-16
lines changed

Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundConfiguration.swift

+2
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,13 @@ final class PlaygroundConfiguration {
3737
enum IntegrationType: String, CaseIterable, Identifiable, Hashable {
3838
case standalone = "standalone"
3939
case paymentElement = "payment_element"
40+
case fcLite = "fc_lite"
4041

4142
var displayName: String {
4243
switch self {
4344
case .standalone: "Standalone"
4445
case .paymentElement: "Payment Element"
46+
case .fcLite: "FC Lite"
4547
}
4648
}
4749

Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundView.swift

+14-12
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,22 @@ struct PlaygroundView: View {
4444
}
4545
.pickerStyle(.inline)
4646

47-
Section(header: Text("Select SDK Type")) {
48-
VStack(alignment: .leading, spacing: 4) {
49-
Picker("Select SDK Type", selection: viewModel.sdkType) {
50-
ForEach(PlaygroundConfiguration.SDKType.allCases) {
51-
Text($0.rawValue.capitalized)
52-
.tag($0)
47+
if viewModel.integrationType.wrappedValue != .fcLite {
48+
Section(header: Text("Select SDK Type")) {
49+
VStack(alignment: .leading, spacing: 4) {
50+
Picker("Select SDK Type", selection: viewModel.sdkType) {
51+
ForEach(PlaygroundConfiguration.SDKType.allCases) {
52+
Text($0.rawValue.capitalized)
53+
.tag($0)
54+
}
5355
}
54-
}
55-
.pickerStyle(.segmented)
56+
.pickerStyle(.segmented)
5657

57-
if viewModel.sdkType.wrappedValue == .automatic {
58-
Text("'Automatic' will let the server choose between 'Web' or 'Native'.")
59-
.font(.caption)
60-
.italic()
58+
if viewModel.sdkType.wrappedValue == .automatic {
59+
Text("'Automatic' will let the server choose between 'Web' or 'Native'.")
60+
.font(.caption)
61+
.italic()
62+
}
6163
}
6264
}
6365
}

Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundViewModel.swift

+113-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Combine
1010
import Foundation
1111
@_spi(STP) import StripeCore
1212
@_spi(STP) @_spi(v25) import StripeFinancialConnections
13-
import StripePaymentSheet
13+
@_spi(STP) import StripePaymentSheet
1414
import SwiftUI
1515
import UIKit
1616

@@ -292,6 +292,8 @@ final class PlaygroundViewModel: ObservableObject {
292292
setupStandalone()
293293
case .paymentElement:
294294
setupPaymentElement()
295+
case .fcLite:
296+
setupFcLite()
295297
}
296298
}
297299

@@ -460,6 +462,116 @@ final class PlaygroundViewModel: ObservableObject {
460462
}
461463
}
462464

465+
private func setupFcLite() {
466+
isLoading = true
467+
SetupPlayground(
468+
configurationDictionary: playgroundConfiguration.configurationDictionary
469+
) { [weak self] setupPlaygroundResponse in
470+
guard let self else { return }
471+
if let setupPlaygroundResponse {
472+
if let error = setupPlaygroundResponse["error"] {
473+
print("**** Error in playground setup response: \(error)")
474+
return
475+
}
476+
477+
guard let clientSecret = setupPlaygroundResponse["client_secret"] else {
478+
print("**** No client_secret in response")
479+
return
480+
}
481+
guard let publishableKey = setupPlaygroundResponse["publishable_key"] else {
482+
print("**** No publishable_key in response")
483+
return
484+
}
485+
486+
STPAPIClient.shared.publishableKey = publishableKey
487+
DispatchQueue.main.async {
488+
let topMostViewController = UIViewController.topMostViewController()!
489+
let fc = FinancialConnectionsLite(
490+
clientSecret: clientSecret,
491+
returnUrl: URL(string: "financial-connections-example://redirect")!
492+
)
493+
fc.present(from: topMostViewController) { [weak self] result in
494+
switch result {
495+
case .completed(let completed):
496+
switch completed {
497+
case .financialConnections(let linkedBank):
498+
let sessionId = linkedBank.sessionId
499+
let accountId = linkedBank.accountId
500+
let bankAccount: String
501+
if let bankName = linkedBank.bankName, let last4 = linkedBank.last4 {
502+
bankAccount = "\(bankName) ....\(last4)"
503+
} else {
504+
bankAccount = "Bank details unavailable"
505+
}
506+
507+
let sessionInfo =
508+
"""
509+
session_id=\(sessionId)
510+
account_id=\(accountId)
511+
"""
512+
513+
let message = "\(bankAccount)\n\n\(sessionInfo)"
514+
self?.sessionOutput[.message] = message
515+
self?.sessionOutput[.sessionId] = sessionId
516+
self?.sessionOutput[.accountIds] = accountId
517+
518+
UIAlertController.showAlert(
519+
title: "Success",
520+
message: message
521+
)
522+
case .instantDebits(let linkedBank):
523+
let sessionId = linkedBank.linkAccountSessionId ?? "N/a"
524+
let paymentMethodId = linkedBank.paymentMethod.id
525+
let bankAccount: String
526+
if let bankName = linkedBank.bankName, let last4 = linkedBank.last4 {
527+
bankAccount = "\(bankName) ....\(last4)"
528+
} else {
529+
bankAccount = "Bank details unavailable"
530+
}
531+
532+
let sessionInfo =
533+
"""
534+
session_id=\(sessionId)
535+
payment_method_id=\(paymentMethodId)
536+
"""
537+
538+
let message = "\(bankAccount)\n\n\(sessionInfo)"
539+
self?.sessionOutput[.message] = message
540+
self?.sessionOutput[.sessionId] = sessionId
541+
542+
UIAlertController.showAlert(
543+
title: "Success",
544+
message: message
545+
)
546+
@unknown default:
547+
UIAlertController.showAlert(
548+
message: "Unknown payment method flow"
549+
)
550+
}
551+
case .cancelled:
552+
UIAlertController.showAlert(
553+
title: "Cancelled"
554+
)
555+
case .failed(let error):
556+
UIAlertController.showAlert(
557+
title: "Failed",
558+
message: error.localizedDescription
559+
)
560+
}
561+
}
562+
}
563+
} else {
564+
UIAlertController.showAlert(
565+
title: "Playground App Setup Failed",
566+
message: "Try clearing 'Custom Keys' or delete & re-install the app."
567+
)
568+
}
569+
DispatchQueue.main.async {
570+
self.isLoading = false
571+
}
572+
}
573+
}
574+
463575
func didSelectClearCaches() {
464576
URLSession.shared.reset(completionHandler: {})
465577
}

StripePaymentSheet/StripePaymentSheet/Source/Internal/FCLite/FinancialConnectionsLite.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ enum FCLiteError: Error {
1313
case linkedBankUnavailable
1414
}
1515

16-
final class FinancialConnectionsLite {
16+
@_spi(STP) public final class FinancialConnectionsLite {
1717
/// The client secret of a Stripe `FinancialConnectionsSession` object.
1818
let clientSecret: String
1919

@@ -36,15 +36,15 @@ final class FinancialConnectionsLite {
3636
/// - Parameters:
3737
/// - clientSecret: The client secret of a Stripe `FinancialConnectionsSession` object.
3838
/// - returnUrl: A URL that that `FinancialConnectionsLite` can use to redirect back to your app after completing authentication in another app (such as a bank's app or Safari).
39-
init(
39+
@_spi(STP) public init(
4040
clientSecret: String,
4141
returnUrl: URL
4242
) {
4343
self.clientSecret = clientSecret
4444
self.returnUrl = returnUrl
4545
}
4646

47-
func present(
47+
@_spi(STP) public func present(
4848
from viewController: UIViewController,
4949
completion: @escaping (FinancialConnectionsSDKResult) -> Void
5050
) {

0 commit comments

Comments
 (0)