@@ -58,7 +58,8 @@ extension StripeCardReaderService: CardReaderService {
5858
5959 // MARK: - CardReaderService conformance. Commands
6060
61- public func start( _ configProvider: CardReaderConfigProvider ) throws {
61+ public func start( _ configProvider: CardReaderConfigProvider ,
62+ discoveryMethod: CardReaderDiscoveryMethod ) throws {
6263 setConfigProvider ( configProvider)
6364
6465 Terminal . setLogListener { message in
@@ -85,13 +86,12 @@ extension StripeCardReaderService: CardReaderService {
8586 }
8687
8788 let config = DiscoveryConfiguration (
88- discoveryMethod: . bluetoothScan ,
89+ discoveryMethod: discoveryMethod . toStripe ( ) ,
8990 simulated: shouldUseSimulatedCardReader
9091 )
9192
92- // If we're using the simulated reader, we don't want to check for Bluetooth permissions
93- // as the simulator won't have Bluetooth available.
94- guard shouldUseSimulatedCardReader || CBCentralManager . authorization != . denied else {
93+ guard shouldSkipBluetoothCheck ( discoveryConfiguration: config) ||
94+ CBCentralManager . authorization != . denied else {
9595 throw CardReaderServiceError . bluetoothDenied
9696 }
9797
@@ -123,6 +123,14 @@ extension StripeCardReaderService: CardReaderService {
123123 } )
124124 }
125125
126+
127+ // If we're using the simulated reader, we don't want to check for Bluetooth permissions
128+ // as the simulator won't have Bluetooth available.
129+ // If we're using the built-in reader, bluetooth is not required.
130+ private func shouldSkipBluetoothCheck( discoveryConfiguration: DiscoveryConfiguration ) -> Bool {
131+ shouldUseSimulatedCardReader || discoveryConfiguration. discoveryMethod == . localMobile
132+ }
133+
126134 public func cancelDiscovery( ) -> Future < Void , Error > {
127135 Future { [ weak self] promise in
128136 /**
@@ -311,9 +319,20 @@ extension StripeCardReaderService: CardReaderService {
311319 } . eraseToAnyPublisher ( )
312320 }
313321
314- return getBluetoothConfiguration ( stripeReader) . flatMap { configuration in
315- self . connect ( stripeReader, configuration: configuration)
316- } . eraseToAnyPublisher ( )
322+ switch stripeReader. deviceType {
323+ case . appleBuiltIn:
324+ return getLocalMobileConfiguration ( stripeReader) . flatMap { configuration in
325+ self . connect ( stripeReader, configuration: configuration)
326+ }
327+ . share ( )
328+ . eraseToAnyPublisher ( )
329+ default :
330+ return getBluetoothConfiguration ( stripeReader) . flatMap { configuration in
331+ self . connect ( stripeReader, configuration: configuration)
332+ }
333+ . share ( )
334+ . eraseToAnyPublisher ( )
335+ }
317336 }
318337
319338 private func getBluetoothConfiguration( _ reader: StripeTerminal . Reader ) -> Future < BluetoothConnectionConfiguration , Error > {
@@ -337,6 +356,27 @@ extension StripeCardReaderService: CardReaderService {
337356 }
338357 }
339358
359+ private func getLocalMobileConfiguration( _ reader: StripeTerminal . Reader ) -> Future < LocalMobileConnectionConfiguration , Error > {
360+ return Future ( ) { [ weak self] promise in
361+ guard let self = self else {
362+ promise ( . failure( CardReaderServiceError . connection ( ) ) )
363+ return
364+ }
365+
366+ // TODO - If we've recently connected to this reader, use the cached locationId from the
367+ // Terminal SDK instead of making this fetch. See #5116 and #5087
368+ self . readerLocationProvider? . fetchDefaultLocationID { result in
369+ switch result {
370+ case . success( let locationId) :
371+ return promise ( . success( LocalMobileConnectionConfiguration ( locationId: locationId) ) )
372+ case . failure( let error) :
373+ let underlyingError = UnderlyingError ( with: error)
374+ return promise ( . failure( CardReaderServiceError . connection ( underlyingError: underlyingError) ) )
375+ }
376+ }
377+ }
378+ }
379+
340380 public func connect( _ reader: StripeTerminal . Reader , configuration: BluetoothConnectionConfiguration ) -> Future < CardReader , Error > {
341381 // Keep a copy of the battery level in case the connection fails due to low battery
342382 // If that happens, the reader object won't be accessible anymore, and we want to show
@@ -376,6 +416,40 @@ extension StripeCardReaderService: CardReaderService {
376416 }
377417 }
378418
419+ public func connect( _ reader: StripeTerminal . Reader , configuration: LocalMobileConnectionConfiguration ) -> Future < CardReader , Error > {
420+ return Future { [ weak self] promise in
421+ guard let self = self else {
422+ promise ( . failure( CardReaderServiceError . connection ( ) ) )
423+ return
424+ }
425+
426+ Terminal . shared. connectLocalMobileReader ( reader, delegate: self , connectionConfig: configuration) { [ weak self] ( reader, error) in
427+ guard let self = self else {
428+ promise ( . failure( CardReaderServiceError . connection ( ) ) )
429+ return
430+ }
431+ // Clear cached readers, as per Stripe's documentation.
432+ self . discoveredStripeReadersCache. clear ( )
433+
434+ if let error = error {
435+ let underlyingError = UnderlyingError ( with: error)
436+ // Starting with StripeTerminal 2.0, required software updates happen transparently on connection
437+ // Any error related to that will be reported here, but we don't want to treat it as a connection error
438+ let serviceError : CardReaderServiceError = underlyingError. isSoftwareUpdateError ?
439+ . softwareUpdate( underlyingError: underlyingError, batteryLevel: nil ) :
440+ . connection( underlyingError: underlyingError)
441+ promise ( . failure( serviceError) )
442+ }
443+
444+ if let reader = reader {
445+ self . connectedReadersSubject. send ( [ CardReader ( reader: reader) ] )
446+ self . switchStatusToIdle ( )
447+ promise ( . success( CardReader ( reader: reader) ) )
448+ }
449+ }
450+ }
451+ }
452+
379453 public func installUpdate( ) -> Void {
380454 Terminal . shared. installAvailableUpdate ( )
381455 }
@@ -443,6 +517,7 @@ private extension StripeCardReaderService {
443517
444518 if underlyingError == . commandCancelled {
445519 DDLogWarn ( " 💳 Warning: collect payment error cancelled. We actively ignore this error \( error) " )
520+ promise ( . failure( CardReaderServiceError . paymentCancellation ( underlyingError: underlyingError) ) )
446521 }
447522
448523 }
@@ -696,6 +771,46 @@ extension StripeCardReaderService: BluetoothReaderDelegate {
696771 }
697772}
698773
774+ extension StripeCardReaderService : LocalMobileReaderDelegate {
775+ public func localMobileReader( _ reader: Reader , didRequestReaderInput inputOptions: ReaderInputOptions = [ ] ) {
776+ sendReaderEvent ( CardReaderEvent . make ( stripeReaderInputOptions: inputOptions) )
777+ }
778+
779+ public func localMobileReader( _ reader: Reader , didRequestReaderDisplayMessage displayMessage: ReaderDisplayMessage ) {
780+ sendReaderEvent ( CardReaderEvent . make ( displayMessage: displayMessage) )
781+ }
782+
783+
784+ // TODO: use a specific `deviceSetup` in these three functions instead of reusing the softwareUpdateSubject
785+ // https://github.com/woocommerce/woocommerce-ios/issues/8088
786+ public func localMobileReader( _ reader: Reader , didStartInstallingUpdate update: ReaderSoftwareUpdate , cancelable: Cancelable ? ) {
787+ softwareUpdateSubject. send ( . started( cancelable: cancelable. map ( StripeCancelable . init ( cancelable: ) ) ) )
788+ }
789+
790+ public func localMobileReader( _ reader: Reader , didReportReaderSoftwareUpdateProgress progress: Float ) {
791+ softwareUpdateSubject. send ( . installing( progress: progress) )
792+ }
793+
794+ public func localMobileReader( _ reader: Reader , didFinishInstallingUpdate update: ReaderSoftwareUpdate ? , error: Error ? ) {
795+ if let error = error {
796+ softwareUpdateSubject. send ( . failed(
797+ error: CardReaderServiceError . softwareUpdate ( underlyingError: UnderlyingError ( with: error) ,
798+ batteryLevel: reader. batteryLevel? . doubleValue) )
799+ )
800+ if let requiredDate = update? . requiredAt,
801+ requiredDate > Date ( ) {
802+ softwareUpdateSubject. send ( . available)
803+ } else {
804+ softwareUpdateSubject. send ( . none)
805+ }
806+ } else {
807+ softwareUpdateSubject. send ( . completed)
808+ connectedReadersSubject. send ( [ CardReader ( reader: reader) ] )
809+ softwareUpdateSubject. send ( . none)
810+ }
811+ }
812+ }
813+
699814// MARK: - Terminal delegate
700815extension StripeCardReaderService : TerminalDelegate {
701816 public func terminal( _ terminal: Terminal , didReportUnexpectedReaderDisconnect reader: Reader ) {
0 commit comments