Skip to content

Commit 47c0bd6

Browse files
authored
Merge pull request #8595 from woocommerce/issue/8314-built-in-card-reader-requirements
[Mobile Payments] Check built in card reader requirements
2 parents 54d567c + b2a9b81 commit 47c0bd6

File tree

11 files changed

+153
-28
lines changed

11 files changed

+153
-28
lines changed

Hardware/Hardware/CardReader/CardReaderService.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ public protocol CardReaderService {
1818

1919
// MARK: - Commands
2020

21+
/// Checks for support of a given reader type and discovery method combination. Does not start discovery.
22+
///
23+
func checkSupport(for cardReaderType: CardReaderType,
24+
configProvider: CardReaderConfigProvider,
25+
discoveryMethod: CardReaderDiscoveryMethod) -> Bool
26+
2127
/// Starts the service.
2228
/// That could imply, for example, that the reader discovery process starts
2329
func start(_ configProvider: CardReaderConfigProvider, discoveryMethod: CardReaderDiscoveryMethod) throws

Hardware/Hardware/CardReader/StripeCardReader/CardReaderType+Stripe.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,20 @@ extension CardReaderType {
1919
return .other
2020
}
2121
}
22+
23+
func toStripe() -> DeviceType? {
24+
switch self {
25+
case .chipper:
26+
return .chipper2X
27+
case .stripeM2:
28+
return .stripeM2
29+
case .wisepad3:
30+
return .wisePad3
31+
case .appleBuiltIn:
32+
return .appleBuiltIn
33+
case .other:
34+
return nil
35+
}
36+
}
2237
}
2338
#endif

Hardware/Hardware/CardReader/StripeCardReader/NoOpCardReaderService.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Combine
2-
/// The adapter wrapping the Stripe Terminal SDK
2+
/// A no-op replacement for the adapter wrapping the Stripe Terminal SDK
33
public struct NoOpCardReaderService: CardReaderService {
44
// MARK: - Queries
55
/// The publisher that emits the list of discovered readers whenever the service discovers a new reader.
@@ -18,6 +18,12 @@ public struct NoOpCardReaderService: CardReaderService {
1818
public init() {}
1919
// MARK: - Commands
2020

21+
public func checkSupport(for cardReaderType: CardReaderType,
22+
configProvider: CardReaderConfigProvider,
23+
discoveryMethod: CardReaderDiscoveryMethod) -> Bool {
24+
return false
25+
}
26+
2127
/// Starts the service.
2228
/// That could imply, for example, that the reader discovery process starts
2329
public func start(_ configProvider: CardReaderConfigProvider, discoveryMethod: CardReaderDiscoveryMethod) throws {

Hardware/Hardware/CardReader/StripeCardReader/StripeCardReaderService.swift

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,27 @@ extension StripeCardReaderService: CardReaderService {
6262

6363
// MARK: - CardReaderService conformance. Commands
6464

65-
public func start(_ configProvider: CardReaderConfigProvider,
66-
discoveryMethod: CardReaderDiscoveryMethod) throws {
65+
public func checkSupport(for cardReaderType: CardReaderType,
66+
configProvider: CardReaderConfigProvider,
67+
discoveryMethod: CardReaderDiscoveryMethod) -> Bool {
68+
guard let deviceType = cardReaderType.toStripe() else {
69+
return false
70+
}
71+
72+
prepare(using: configProvider)
73+
74+
let result = Terminal.shared.supportsReaders(of: deviceType,
75+
discoveryMethod: discoveryMethod.toStripe(),
76+
simulated: shouldUseSimulatedCardReader)
77+
switch result {
78+
case .success:
79+
return true
80+
case .failure:
81+
return false
82+
}
83+
}
84+
85+
func prepare(using configProvider: CardReaderConfigProvider) {
6786
setConfigProvider(configProvider)
6887

6988
Terminal.setLogListener { message in
@@ -75,6 +94,11 @@ extension StripeCardReaderService: CardReaderService {
7594
DDLogDebug("💳 [StripeTerminal] \(message)")
7695
}
7796
Terminal.shared.logLevel = terminalLogLevel
97+
}
98+
99+
public func start(_ configProvider: CardReaderConfigProvider,
100+
discoveryMethod: CardReaderDiscoveryMethod) throws {
101+
prepare(using: configProvider)
78102

79103
if shouldUseSimulatedCardReader {
80104
// You can test with different reader software update scenarios.

WooCommerce/Classes/Model/BetaFeature.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ private extension BetaFeature {
155155
"phone's built in reader")
156156
static let tapToPayOnIPhoneDescription = NSLocalizedString(
157157
"Test out In-Person Payments using your phone's built-in card reader, as we get ready to launch. " +
158-
"Supported on iPhone XS and newer phones, running iOS 15.5 or above.",
158+
"Supported on iPhone XS and newer phones, running iOS 16 or above, for US-based stores.",
159159
comment: "Cell description on beta features screen to enable in-app purchases")
160160
}
161161
}

WooCommerce/Classes/ViewRelated/CardPresentPayments/CardPresentPaymentPreflightController.swift

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ final class CardPresentPaymentPreflightController {
8282
analyticsTracker: analyticsTracker)
8383
}
8484

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

9697
// Ask for a Reader type if supported by device/in country
97-
guard localMobileReaderSupported(),
98+
guard await localMobileReaderSupported(),
9899
configuration.supportedReaders.contains(.appleBuiltIn)
99100
else {
100101
// Attempt to find a bluetooth reader and connect
@@ -120,16 +121,16 @@ final class CardPresentPaymentPreflightController {
120121
}))
121122
}
122123

123-
private func localMobileReaderSupported() -> Bool {
124-
#if targetEnvironment(simulator)
125-
return true
126-
#else
127-
if #available(iOS 15.4, *) {
128-
return PaymentCardReader.isSupported
129-
} else {
130-
return false
124+
@MainActor
125+
private func localMobileReaderSupported() async -> Bool {
126+
await withCheckedContinuation { continuation in
127+
let action = CardPresentPaymentAction.checkDeviceSupport(siteID: siteID,
128+
cardReaderType: .appleBuiltIn,
129+
discoveryMethod: .localMobile) { result in
130+
continuation.resume(returning: result)
131+
}
132+
stores.dispatch(action)
131133
}
132-
#endif
133134
}
134135

135136
private func handleConnectionResult(_ result: Result<CardReaderConnectionResult, Error>) {

WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,9 @@ final class CollectOrderPaymentUseCase: NSObject, CollectOrderPaymentProtocol {
178178
}
179179
.store(in: &cancellables)
180180

181-
preflightController?.start()
181+
Task {
182+
await preflightController?.start()
183+
}
182184
}
183185
}
184186

Yosemite/Yosemite/Actions/CardPresentPaymentAction.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ public enum CardPresentPaymentAction: Action {
2323
///
2424
case loadAccounts(siteID: Int64, onCompletion: (Result<Void, Error>) -> Void)
2525

26+
case checkDeviceSupport(siteID: Int64,
27+
cardReaderType: CardReaderType,
28+
discoveryMethod: CardReaderDiscoveryMethod,
29+
onCompletion: (Bool) -> Void)
30+
2631
/// Start the Card Reader discovery process.
2732
///
2833
case startCardReaderDiscovery(siteID: Int64,

Yosemite/Yosemite/Stores/CardPresentPaymentStore.swift

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ public final class CardPresentPaymentStore: Store {
8484
case .loadAccounts(let siteID, let onCompletion):
8585
loadAccounts(siteID: siteID,
8686
onCompletion: onCompletion)
87+
case .checkDeviceSupport(let siteID, let cardReaderType, let discoveryMethod, let completion):
88+
checkDeviceSupport(siteID: siteID,
89+
cardReaderType: cardReaderType,
90+
discoveryMethod: discoveryMethod,
91+
onCompletion: completion)
8792
case .startCardReaderDiscovery(let siteID, let discoveryMethod, let onReaderDiscovered, let onError):
8893
startCardReaderDiscovery(siteID: siteID,
8994
discoveryMethod: discoveryMethod,
@@ -128,19 +133,30 @@ public final class CardPresentPaymentStore: Store {
128133
// MARK: - Services
129134
//
130135
private extension CardPresentPaymentStore {
131-
func startCardReaderDiscovery(siteID: Int64,
132-
discoveryMethod: CardReaderDiscoveryMethod,
133-
onReaderDiscovered: @escaping (_ readers: [CardReader]) -> Void,
134-
onError: @escaping (Error) -> Void) {
136+
func checkDeviceSupport(siteID: Int64,
137+
cardReaderType: CardReaderType,
138+
discoveryMethod: CardReaderDiscoveryMethod,
139+
onCompletion: (Bool) -> Void) {
140+
prepareConfigProvider(siteID: siteID)
141+
onCompletion(cardReaderService.checkSupport(for: cardReaderType, configProvider: commonReaderConfigProvider, discoveryMethod: discoveryMethod))
142+
}
143+
144+
func prepareConfigProvider(siteID: Int64) {
145+
switch usingBackend {
146+
case .wcpay:
147+
commonReaderConfigProvider.setContext(siteID: siteID, remote: self.remote)
148+
case .stripe:
149+
commonReaderConfigProvider.setContext(siteID: siteID, remote: self.stripeRemote)
150+
}
151+
}
152+
153+
func startCardReaderDiscovery(siteID: Int64,
154+
discoveryMethod: CardReaderDiscoveryMethod,
155+
onReaderDiscovered: @escaping (_ readers: [CardReader]) -> Void,
156+
onError: @escaping (Error) -> Void) {
157+
prepareConfigProvider(siteID: siteID)
135158
do {
136-
switch usingBackend {
137-
case .wcpay:
138-
commonReaderConfigProvider.setContext(siteID: siteID, remote: self.remote)
139-
try cardReaderService.start(commonReaderConfigProvider, discoveryMethod: discoveryMethod)
140-
case .stripe:
141-
commonReaderConfigProvider.setContext(siteID: siteID, remote: self.stripeRemote)
142-
try cardReaderService.start(commonReaderConfigProvider, discoveryMethod: discoveryMethod)
143-
}
159+
try cardReaderService.start(commonReaderConfigProvider, discoveryMethod: discoveryMethod)
144160
} catch {
145161
return onError(error)
146162
}

Yosemite/YosemiteTests/Mocks/CardPresentPayments/MockCardReaderService.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ final class MockCardReaderService: CardReaderService {
5050
/// The publisher to return in `capturePayment`.
5151
private var capturePaymentPublisher: AnyPublisher<PaymentIntent, Error>?
5252

53+
54+
var didCheckSupport = false
55+
var spyCheckSupportCardReaderType: CardReaderType? = nil
56+
var spyCheckSupportConfigProvider: CardReaderConfigProvider? = nil
57+
var spyCheckSupportDiscoveryMethod: CardReaderDiscoveryMethod? = nil
58+
5359
/// The future to return in `waitForInsertedCardToBeRemoved`.
5460
private var waitForInsertedCardToBeRemovedFuture: Future<Void, Never>?
5561

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

6268
}
6369

70+
func checkSupport(for cardReaderType: Hardware.CardReaderType,
71+
configProvider: Hardware.CardReaderConfigProvider,
72+
discoveryMethod: Hardware.CardReaderDiscoveryMethod) -> Bool {
73+
didCheckSupport = true
74+
spyCheckSupportCardReaderType = cardReaderType
75+
spyCheckSupportConfigProvider = configProvider
76+
spyCheckSupportDiscoveryMethod = discoveryMethod
77+
78+
return true
79+
}
80+
6481
func start(_ configProvider: Hardware.CardReaderConfigProvider, discoveryMethod: Hardware.CardReaderDiscoveryMethod) throws {
6582
didHitStart = true
6683
didReceiveAConfigurationProvider = true

0 commit comments

Comments
 (0)