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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Hardware/Hardware/CardReader/CardReaderService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ public protocol CardReaderService {

// MARK: - Commands

/// Checks for support of a given reader type and discovery method combination. Does not start discovery.
///
func checkSupport(for cardReaderType: CardReaderType,
configProvider: CardReaderConfigProvider,
discoveryMethod: CardReaderDiscoveryMethod) -> Bool

/// Starts the service.
/// That could imply, for example, that the reader discovery process starts
func start(_ configProvider: CardReaderConfigProvider, discoveryMethod: CardReaderDiscoveryMethod) throws
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,20 @@ extension CardReaderType {
return .other
}
}

func toStripe() -> DeviceType? {
switch self {
case .chipper:
return .chipper2X
case .stripeM2:
return .stripeM2
case .wisepad3:
return .wisePad3
case .appleBuiltIn:
return .appleBuiltIn
case .other:
return nil
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Combine
/// The adapter wrapping the Stripe Terminal SDK
/// A no-op replacement for the adapter wrapping the Stripe Terminal SDK
public struct NoOpCardReaderService: CardReaderService {
// MARK: - Queries
/// The publisher that emits the list of discovered readers whenever the service discovers a new reader.
Expand All @@ -18,6 +18,12 @@ public struct NoOpCardReaderService: CardReaderService {
public init() {}
// MARK: - Commands

public func checkSupport(for cardReaderType: CardReaderType,
configProvider: CardReaderConfigProvider,
discoveryMethod: CardReaderDiscoveryMethod) -> Bool {
return false
Copy link
Contributor

Choose a reason for hiding this comment

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

A non blocker here, you can omit return if you want

}

/// Starts the service.
/// That could imply, for example, that the reader discovery process starts
public func start(_ configProvider: CardReaderConfigProvider, discoveryMethod: CardReaderDiscoveryMethod) throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,27 @@ extension StripeCardReaderService: CardReaderService {

// MARK: - CardReaderService conformance. Commands

public func start(_ configProvider: CardReaderConfigProvider,
discoveryMethod: CardReaderDiscoveryMethod) throws {
public func checkSupport(for cardReaderType: CardReaderType,
configProvider: CardReaderConfigProvider,
discoveryMethod: CardReaderDiscoveryMethod) -> Bool {
guard let deviceType = cardReaderType.toStripe() else {
return false
}

prepare(using: configProvider)

let result = Terminal.shared.supportsReaders(of: deviceType,
discoveryMethod: discoveryMethod.toStripe(),
simulated: shouldUseSimulatedCardReader)
switch result {
case .success:
return true
case .failure:
return false
}
}

func prepare(using configProvider: CardReaderConfigProvider) {
setConfigProvider(configProvider)

Terminal.setLogListener { message in
Expand All @@ -75,6 +94,11 @@ extension StripeCardReaderService: CardReaderService {
DDLogDebug("💳 [StripeTerminal] \(message)")
}
Terminal.shared.logLevel = terminalLogLevel
}

public func start(_ configProvider: CardReaderConfigProvider,
discoveryMethod: CardReaderDiscoveryMethod) throws {
prepare(using: configProvider)

if shouldUseSimulatedCardReader {
// You can test with different reader software update scenarios.
Expand Down
2 changes: 1 addition & 1 deletion WooCommerce/Classes/Model/BetaFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ private extension BetaFeature {
"phone's built in reader")
static let tapToPayOnIPhoneDescription = NSLocalizedString(
"Test out In-Person Payments using your phone's built-in card reader, as we get ready to launch. " +
"Supported on iPhone XS and newer phones, running iOS 15.5 or above.",
"Supported on iPhone XS and newer phones, running iOS 16 or above, for US-based stores.",
comment: "Cell description on beta features screen to enable in-app purchases")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ final class CardPresentPaymentPreflightController {
analyticsTracker: analyticsTracker)
}

func start() {
@MainActor
func start() async {
configureBackend()
observeConnectedReaders()
// If we're already connected to a reader, return it
Expand All @@ -94,7 +95,7 @@ final class CardPresentPaymentPreflightController {
// TODO: Run onboarding if needed

// Ask for a Reader type if supported by device/in country
guard localMobileReaderSupported(),
guard await localMobileReaderSupported(),
configuration.supportedReaders.contains(.appleBuiltIn)
else {
// Attempt to find a bluetooth reader and connect
Expand All @@ -120,16 +121,16 @@ final class CardPresentPaymentPreflightController {
}))
}

private func localMobileReaderSupported() -> Bool {
#if targetEnvironment(simulator)
return true
#else
if #available(iOS 15.4, *) {
return PaymentCardReader.isSupported
} else {
return false
@MainActor
private func localMobileReaderSupported() async -> Bool {
await withCheckedContinuation { continuation in
let action = CardPresentPaymentAction.checkDeviceSupport(siteID: siteID,
cardReaderType: .appleBuiltIn,
discoveryMethod: .localMobile) { result in
continuation.resume(returning: result)
}
stores.dispatch(action)
}
#endif
}

private func handleConnectionResult(_ result: Result<CardReaderConnectionResult, Error>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ final class CollectOrderPaymentUseCase: NSObject, CollectOrderPaymentProtocol {
}
.store(in: &cancellables)

preflightController?.start()
Task {
await preflightController?.start()
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions Yosemite/Yosemite/Actions/CardPresentPaymentAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public enum CardPresentPaymentAction: Action {
///
case loadAccounts(siteID: Int64, onCompletion: (Result<Void, Error>) -> Void)

case checkDeviceSupport(siteID: Int64,
cardReaderType: CardReaderType,
discoveryMethod: CardReaderDiscoveryMethod,
onCompletion: (Bool) -> Void)

/// Start the Card Reader discovery process.
///
case startCardReaderDiscovery(siteID: Int64,
Expand Down
40 changes: 28 additions & 12 deletions Yosemite/Yosemite/Stores/CardPresentPaymentStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ public final class CardPresentPaymentStore: Store {
case .loadAccounts(let siteID, let onCompletion):
loadAccounts(siteID: siteID,
onCompletion: onCompletion)
case .checkDeviceSupport(let siteID, let cardReaderType, let discoveryMethod, let completion):
checkDeviceSupport(siteID: siteID,
cardReaderType: cardReaderType,
discoveryMethod: discoveryMethod,
onCompletion: completion)
case .startCardReaderDiscovery(let siteID, let discoveryMethod, let onReaderDiscovered, let onError):
startCardReaderDiscovery(siteID: siteID,
discoveryMethod: discoveryMethod,
Expand Down Expand Up @@ -128,19 +133,30 @@ public final class CardPresentPaymentStore: Store {
// MARK: - Services
//
private extension CardPresentPaymentStore {
func startCardReaderDiscovery(siteID: Int64,
discoveryMethod: CardReaderDiscoveryMethod,
onReaderDiscovered: @escaping (_ readers: [CardReader]) -> Void,
onError: @escaping (Error) -> Void) {
func checkDeviceSupport(siteID: Int64,
cardReaderType: CardReaderType,
discoveryMethod: CardReaderDiscoveryMethod,
onCompletion: (Bool) -> Void) {
prepareConfigProvider(siteID: siteID)
onCompletion(cardReaderService.checkSupport(for: cardReaderType, configProvider: commonReaderConfigProvider, discoveryMethod: discoveryMethod))
}

func prepareConfigProvider(siteID: Int64) {
switch usingBackend {
case .wcpay:
commonReaderConfigProvider.setContext(siteID: siteID, remote: self.remote)
case .stripe:
commonReaderConfigProvider.setContext(siteID: siteID, remote: self.stripeRemote)
}
}

func startCardReaderDiscovery(siteID: Int64,
discoveryMethod: CardReaderDiscoveryMethod,
onReaderDiscovered: @escaping (_ readers: [CardReader]) -> Void,
onError: @escaping (Error) -> Void) {
prepareConfigProvider(siteID: siteID)
do {
switch usingBackend {
case .wcpay:
commonReaderConfigProvider.setContext(siteID: siteID, remote: self.remote)
try cardReaderService.start(commonReaderConfigProvider, discoveryMethod: discoveryMethod)
case .stripe:
commonReaderConfigProvider.setContext(siteID: siteID, remote: self.stripeRemote)
try cardReaderService.start(commonReaderConfigProvider, discoveryMethod: discoveryMethod)
}
try cardReaderService.start(commonReaderConfigProvider, discoveryMethod: discoveryMethod)
} catch {
return onError(error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ final class MockCardReaderService: CardReaderService {
/// The publisher to return in `capturePayment`.
private var capturePaymentPublisher: AnyPublisher<PaymentIntent, Error>?


var didCheckSupport = false
var spyCheckSupportCardReaderType: CardReaderType? = nil
var spyCheckSupportConfigProvider: CardReaderConfigProvider? = nil
var spyCheckSupportDiscoveryMethod: CardReaderDiscoveryMethod? = nil

/// The future to return in `waitForInsertedCardToBeRemoved`.
private var waitForInsertedCardToBeRemovedFuture: Future<Void, Never>?

Expand All @@ -61,6 +67,17 @@ final class MockCardReaderService: CardReaderService {

}

func checkSupport(for cardReaderType: Hardware.CardReaderType,
configProvider: Hardware.CardReaderConfigProvider,
discoveryMethod: Hardware.CardReaderDiscoveryMethod) -> Bool {
didCheckSupport = true
spyCheckSupportCardReaderType = cardReaderType
spyCheckSupportConfigProvider = configProvider
spyCheckSupportDiscoveryMethod = discoveryMethod

return true
}

func start(_ configProvider: Hardware.CardReaderConfigProvider, discoveryMethod: Hardware.CardReaderDiscoveryMethod) throws {
didHitStart = true
didReceiveAConfigurationProvider = true
Expand Down
33 changes: 33 additions & 0 deletions Yosemite/YosemiteTests/Stores/CardPresentPaymentStoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -775,4 +775,37 @@ final class CardPresentPaymentStoreTests: XCTestCase {
// Then
XCTAssertEqual(result, account)
}

func test_checkDeviceSupport_action_passes_configuration_provider_to_service() {
let cardPresentStore = CardPresentPaymentStore(dispatcher: dispatcher,
storageManager: storageManager,
network: network,
cardReaderService: mockCardReaderService)

let action = CardPresentPaymentAction.checkDeviceSupport(siteID: sampleSiteID,
cardReaderType: .appleBuiltIn,
discoveryMethod: .localMobile,
onCompletion: { _ in })

cardPresentStore.onAction(action)

XCTAssertNotNil(mockCardReaderService.spyCheckSupportConfigProvider)
}

func test_checkDeviceSupport_action_passes_reader_type_and_discovery_method_to_service() {
let cardPresentStore = CardPresentPaymentStore(dispatcher: dispatcher,
storageManager: storageManager,
network: network,
cardReaderService: mockCardReaderService)

let action = CardPresentPaymentAction.checkDeviceSupport(siteID: sampleSiteID,
cardReaderType: .chipper,
discoveryMethod: .bluetoothScan,
onCompletion: { _ in })

cardPresentStore.onAction(action)

assertEqual(.bluetoothScan, mockCardReaderService.spyCheckSupportDiscoveryMethod)
assertEqual(.chipper, mockCardReaderService.spyCheckSupportCardReaderType)
}
}