diff --git a/Hardware/Hardware.xcodeproj/project.pbxproj b/Hardware/Hardware.xcodeproj/project.pbxproj index c846dda8385..f4633f623d6 100644 --- a/Hardware/Hardware.xcodeproj/project.pbxproj +++ b/Hardware/Hardware.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 028C39E028255CFE0007BA25 /* Models+Copiable.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028C39DF28255CFE0007BA25 /* Models+Copiable.generated.swift */; }; 02B5147A28254ED300750B71 /* Codegen in Frameworks */ = {isa = PBXBuildFile; productRef = 02B5147928254ED300750B71 /* Codegen */; }; 030338102705F7D400764131 /* ReceiptTotalLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0303380F2705F7D400764131 /* ReceiptTotalLine.swift */; }; + 035DBA3929251ED6003E5125 /* CardReaderInputOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035DBA3829251ED6003E5125 /* CardReaderInputOptions.swift */; }; 039D948B2760C0660044EF38 /* NoOpCardReaderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039D948A2760C0660044EF38 /* NoOpCardReaderService.swift */; }; 03B440AA2754DFC400759429 /* UnderlyingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B440A92754DFC400759429 /* UnderlyingError.swift */; }; 03CF78D327C6710C00523706 /* interac.svg in Resources */ = {isa = PBXBuildFile; fileRef = 03CF78D227C6710B00523706 /* interac.svg */; }; @@ -153,6 +154,7 @@ 02351FF56149ADCD11338B19 /* Pods-SampleReceiptPrinter.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleReceiptPrinter.release.xcconfig"; path = "Target Support Files/Pods-SampleReceiptPrinter/Pods-SampleReceiptPrinter.release.xcconfig"; sourceTree = ""; }; 028C39DF28255CFE0007BA25 /* Models+Copiable.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Models+Copiable.generated.swift"; sourceTree = ""; }; 0303380F2705F7D400764131 /* ReceiptTotalLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptTotalLine.swift; sourceTree = ""; }; + 035DBA3829251ED6003E5125 /* CardReaderInputOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardReaderInputOptions.swift; sourceTree = ""; }; 039D948A2760C0660044EF38 /* NoOpCardReaderService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoOpCardReaderService.swift; sourceTree = ""; }; 03B440A92754DFC400759429 /* UnderlyingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnderlyingError.swift; sourceTree = ""; }; 03CF78D227C6710B00523706 /* interac.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = interac.svg; sourceTree = ""; }; @@ -439,6 +441,7 @@ D88FDB3625DD21BE00CB0DBD /* StripeCardReader */, D88FDB2725DD21B000CB0DBD /* CardReader.swift */, D88FDB2425DD21B000CB0DBD /* CardReaderEvent.swift */, + 035DBA3829251ED6003E5125 /* CardReaderInputOptions.swift */, D88FDB2625DD21B000CB0DBD /* CardReaderService.swift */, D88FDB1F25DD21AF00CB0DBD /* CardReaderServiceDiscoveryStatus.swift */, D88FDB2125DD21AF00CB0DBD /* CardReaderServiceStatus.swift */, @@ -826,6 +829,7 @@ D845BE54262ED7CC00A3E40F /* PrinterService.swift in Sources */, D845BDDE262DAB8300A3E40F /* PaymentMethod+Stripe.swift in Sources */, D89B8F0C25DDC9D30001C726 /* ChargeStatus.swift in Sources */, + 035DBA3929251ED6003E5125 /* CardReaderInputOptions.swift in Sources */, E140F61C2668CDC900FDB5FF /* Logging.swift in Sources */, 03CF78D727DF9BE600523706 /* RefundParameters.swift in Sources */, 03B440AA2754DFC400759429 /* UnderlyingError.swift in Sources */, diff --git a/Hardware/Hardware/CardReader/CardReaderEvent.swift b/Hardware/Hardware/CardReader/CardReaderEvent.swift index 00d3c06c279..8875aa9e65e 100644 --- a/Hardware/Hardware/CardReader/CardReaderEvent.swift +++ b/Hardware/Hardware/CardReader/CardReaderEvent.swift @@ -2,7 +2,7 @@ public enum CardReaderEvent: Equatable { /// The reader begins waiting for input. /// The app should prompt the customer to present a payment method - case waitingForInput(String) + case waitingForInput(CardReaderInput) /// Request that a prompt be displayed in the app. /// For example, if the prompt is SwipeCard, diff --git a/Hardware/Hardware/CardReader/CardReaderInputOptions.swift b/Hardware/Hardware/CardReader/CardReaderInputOptions.swift new file mode 100644 index 00000000000..399427bc8de --- /dev/null +++ b/Hardware/Hardware/CardReader/CardReaderInputOptions.swift @@ -0,0 +1,26 @@ +import Foundation + +public struct CardReaderInput: OptionSet { + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + + public static let none = CardReaderInput([]) + public static let swipe = CardReaderInput(rawValue: 1 << 0) + public static let insert = CardReaderInput(rawValue: 1 << 1) + public static let tap = CardReaderInput(rawValue: 1 << 2) +} + + +#if !targetEnvironment(macCatalyst) +import StripeTerminal + +extension CardReaderInput { + init(stripeReaderInputOptions: ReaderInputOptions) { + let value = Int(stripeReaderInputOptions.rawValue) + self.init(rawValue: value) + } +} +#endif diff --git a/Hardware/Hardware/CardReader/StripeCardReader/CardReaderEvent+Stripe.swift b/Hardware/Hardware/CardReader/StripeCardReader/CardReaderEvent+Stripe.swift index 3355345698d..273e866dac4 100644 --- a/Hardware/Hardware/CardReader/StripeCardReader/CardReaderEvent+Stripe.swift +++ b/Hardware/Hardware/CardReader/StripeCardReader/CardReaderEvent+Stripe.swift @@ -4,8 +4,9 @@ import StripeTerminal extension CardReaderEvent { /// Factory method /// - Parameter readerInputOptions: An instance of a StripeTerminal.ReaderInputOptions - static func make(readerInputOptions: ReaderInputOptions) -> Self { - .waitingForInput(Terminal.stringFromReaderInputOptions(readerInputOptions)) + static func make(stripeReaderInputOptions: ReaderInputOptions) -> Self { + let inputOptions = CardReaderInput(stripeReaderInputOptions: stripeReaderInputOptions) + return .waitingForInput(inputOptions) } /// Factory method diff --git a/Hardware/Hardware/CardReader/StripeCardReader/StripeCardReaderService.swift b/Hardware/Hardware/CardReader/StripeCardReader/StripeCardReaderService.swift index 7d61e67e163..ecae21734c6 100644 --- a/Hardware/Hardware/CardReader/StripeCardReader/StripeCardReaderService.swift +++ b/Hardware/Hardware/CardReader/StripeCardReader/StripeCardReaderService.swift @@ -646,7 +646,7 @@ extension StripeCardReaderService: BluetoothReaderDelegate { /// This method is called by the Stripe Terminal SDK when it wants client apps /// to request users to tap / insert / swipe a card. public func reader(_ reader: Reader, didRequestReaderInput inputOptions: ReaderInputOptions = []) { - sendReaderEvent(CardReaderEvent.make(readerInputOptions: inputOptions)) + sendReaderEvent(CardReaderEvent.make(stripeReaderInputOptions: inputOptions)) } /// In this case the Stripe Terminal SDK wants us to present a string on screen diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 5e34f585e4d..35419a16ec4 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ ----- - [*] In-Person Payments: Show spinner while preparing reader for payment, instead of saying it's ready before it is. [https://github.com/woocommerce/woocommerce-ios/pull/8115] - [internal] In-Person Payments: update StripeTerminal from 2.7 to 2.14 [https://github.com/woocommerce/woocommerce-ios/pull/8132] +- [*] In-Person Payments: Fixed payment method prompt for WisePad 3 to show only Tap and Insert options [https://github.com/woocommerce/woocommerce-ios/pull/8136] 11.2 ----- diff --git a/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentModalReaderIsReady.swift b/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentModalReaderIsReady.swift deleted file mode 100644 index c972f4584ec..00000000000 --- a/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentModalReaderIsReady.swift +++ /dev/null @@ -1,98 +0,0 @@ -import UIKit -import Yosemite - -/// The card reader is ready. -final class CardPresentModalReaderIsReady: CardPresentPaymentsModalViewModel { - - /// Customer name - private let name: String - - /// Charge amount - private let amount: String - - let textMode: PaymentsModalTextMode = .fullInfo - let actionsMode: PaymentsModalActionsMode = .secondaryOnlyAction - - var topTitle: String { - name - } - - var topSubtitle: String? { - amount - } - - let image: UIImage = .cardPresentImage - - let primaryButtonTitle: String? = nil - - let secondaryButtonTitle: String? = Localization.cancel - - let auxiliaryButtonTitle: String? = nil - - let bottomTitle: String? = Localization.readerIsReady - - let bottomSubtitle: String? - - var accessibilityLabel: String? { - guard let bottomTitle = bottomTitle else { - return topTitle - } - - return topTitle + bottomTitle - } - - /// Closure to execute when cancel button is tapped - private let cancelAction: () -> Void - - init(name: String, - amount: String, - transactionType: CardPresentTransactionType, - cancelAction: @escaping () -> Void) { - self.name = name - self.amount = amount - self.bottomSubtitle = Localization.tapInsertOrSwipe(transactionType: transactionType) - self.cancelAction = cancelAction - } - - func didTapPrimaryButton(in viewController: UIViewController?) { - // - } - - func didTapSecondaryButton(in viewController: UIViewController?) { - cancelAction() - viewController?.dismiss(animated: true, completion: nil) - } - - func didTapAuxiliaryButton(in viewController: UIViewController?) { - // - } -} - -private extension CardPresentModalReaderIsReady { - enum Localization { - static let readerIsReady = NSLocalizedString( - "Reader is ready", - comment: "Indicates the status of a card reader. Presented to users when in-person payment collection or refund starts" - ) - - static func tapInsertOrSwipe(transactionType: CardPresentTransactionType) -> String { - switch transactionType { - case .collectPayment: - return NSLocalizedString( - "Tap, insert or swipe to pay", - comment: "Indicates the action expected from a user. Presented to users when payment collection starts" - ) - case .refund: - return NSLocalizedString( - "Tap, insert or swipe to refund", - comment: "Indicates the action expected from a user. Presented to users when in-person refund starts" - ) - } - } - - static let cancel = NSLocalizedString( - "Cancel", - comment: "Button to cancel an in-person payment or refund" - ) - } -} diff --git a/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentModalTapCard.swift b/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentModalTapCard.swift index 64bb6b246f0..9a0c7152b02 100644 --- a/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentModalTapCard.swift +++ b/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentModalTapCard.swift @@ -38,10 +38,26 @@ final class CardPresentModalTapCard: CardPresentPaymentsModalViewModel { let accessibilityLabel: String? - init(name: String, amount: String, transactionType: CardPresentTransactionType, onCancel: @escaping () -> Void) { + init(name: String, + amount: String, + transactionType: CardPresentTransactionType, + inputMethods: CardReaderInput, + onCancel: @escaping () -> Void) { self.name = name self.amount = amount - self.bottomSubtitle = Localization.tapInsertOrSwipe(transactionType: transactionType) + + if inputMethods == [.swipe, .insert, .tap] { + self.bottomSubtitle = Localization.tapInsertOrSwipe(transactionType: transactionType) + } else if inputMethods == [.tap, .insert] { + self.bottomSubtitle = Localization.tapOrInsert(transactionType: transactionType) + } else if inputMethods.contains(.tap) { + self.bottomSubtitle = Localization.tap(transactionType: transactionType) + } else if inputMethods.contains(.insert) { + self.bottomSubtitle = Localization.insert(transactionType: transactionType) + } else { + self.bottomSubtitle = Localization.presentCard(transactionType: transactionType) + } + self.accessibilityLabel = Localization.readerIsReady + Localization.tapInsertOrSwipe(transactionType: transactionType) self.onCancel = onCancel } @@ -83,6 +99,66 @@ private extension CardPresentModalTapCard { } } + static func tapOrInsert(transactionType: CardPresentTransactionType) -> String { + switch transactionType { + case .collectPayment: + return NSLocalizedString( + "Tap or insert card to pay", + comment: "Label asking users to present a card. Presented to users when a payment is going to be collected" + ) + case .refund: + return NSLocalizedString( + "Tap or insert card to refund", + comment: "Label asking users to present a card. Presented to users when an in-person refund is going to be executed" + ) + } + } + + static func tap(transactionType: CardPresentTransactionType) -> String { + switch transactionType { + case .collectPayment: + return NSLocalizedString( + "Tap card to pay", + comment: "Label asking users to present a card. Presented to users when a payment is going to be collected" + ) + case .refund: + return NSLocalizedString( + "Tap card to refund", + comment: "Label asking users to present a card. Presented to users when an in-person refund is going to be executed" + ) + } + } + + static func insert(transactionType: CardPresentTransactionType) -> String { + switch transactionType { + case .collectPayment: + return NSLocalizedString( + "Insert card to pay", + comment: "Label asking users to present a card. Presented to users when a payment is going to be collected" + ) + case .refund: + return NSLocalizedString( + "Insert card to refund", + comment: "Label asking users to present a card. Presented to users when an in-person refund is going to be executed" + ) + } + } + + static func presentCard(transactionType: CardPresentTransactionType) -> String { + switch transactionType { + case .collectPayment: + return NSLocalizedString( + "Present card to pay", + comment: "Label asking users to present a card. Presented to users when a payment is going to be collected" + ) + case .refund: + return NSLocalizedString( + "Present card to refund", + comment: "Label asking users to present a card. Presented to users when an in-person refund is going to be executed" + ) + } + } + static let cancel = NSLocalizedString( "Cancel", comment: "Button to cancel a payment" diff --git a/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentRefundOrchestrator.swift b/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentRefundOrchestrator.swift index bb0b02164d8..425a85d1c41 100644 --- a/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentRefundOrchestrator.swift +++ b/WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentRefundOrchestrator.swift @@ -27,7 +27,7 @@ final class CardPresentRefundOrchestrator { func refund(amount: Decimal, charge: WCPayCharge, paymentGatewayAccount: PaymentGatewayAccount, - onWaitingForInput: @escaping () -> Void, + onWaitingForInput: @escaping (CardReaderInput) -> Void, onProcessingMessage: @escaping () -> Void, onDisplayMessage: @escaping (String) -> Void, onCompletion: @escaping (Result) -> Void) { @@ -40,8 +40,8 @@ final class CardPresentRefundOrchestrator { let refundAction = CardPresentPaymentAction.refundPayment(parameters: refundParameters, onCardReaderMessage: { event in switch event { - case .waitingForInput: - onWaitingForInput() + case .waitingForInput(let inputMethods): + onWaitingForInput(inputMethods) case .displayMessage(let message): onDisplayMessage(message) default: diff --git a/WooCommerce/Classes/ViewModels/CardPresentPayments/PaymentCaptureOrchestrator.swift b/WooCommerce/Classes/ViewModels/CardPresentPayments/PaymentCaptureOrchestrator.swift index d6d559e8a04..98ebbb361b1 100644 --- a/WooCommerce/Classes/ViewModels/CardPresentPayments/PaymentCaptureOrchestrator.swift +++ b/WooCommerce/Classes/ViewModels/CardPresentPayments/PaymentCaptureOrchestrator.swift @@ -39,7 +39,7 @@ final class PaymentCaptureOrchestrator { paymentGatewayAccount: PaymentGatewayAccount, paymentMethodTypes: [String], stripeSmallestCurrencyUnitMultiplier: Decimal, - onWaitingForInput: @escaping () -> Void, + onWaitingForInput: @escaping (CardReaderInput) -> Void, onProcessingMessage: @escaping () -> Void, onDisplayMessage: @escaping (String) -> Void, onProcessingCompletion: @escaping (PaymentIntent) -> Void, @@ -68,8 +68,8 @@ final class PaymentCaptureOrchestrator { parameters: parameters, onCardReaderMessage: { event in switch event { - case .waitingForInput: - onWaitingForInput() + case .waitingForInput(let inputMethods): + onWaitingForInput(inputMethods) case .displayMessage(let message): onDisplayMessage(message) case .cardRemovedAfterClientSidePaymentCapture: diff --git a/WooCommerce/Classes/ViewModels/Order Details/OrderDetailsPaymentAlerts.swift b/WooCommerce/Classes/ViewModels/Order Details/OrderDetailsPaymentAlerts.swift index 79d82460b57..8162d541280 100644 --- a/WooCommerce/Classes/ViewModels/Order Details/OrderDetailsPaymentAlerts.swift +++ b/WooCommerce/Classes/ViewModels/Order Details/OrderDetailsPaymentAlerts.swift @@ -1,6 +1,7 @@ import MessageUI import UIKit import WordPressUI +import Yosemite import enum Hardware.CardReaderServiceError import enum Hardware.UnderlyingError @@ -50,18 +51,16 @@ final class OrderDetailsPaymentAlerts: OrderDetailsPaymentAlertsProtocol { presentViewModel(viewModel: CardPresentModalPreparingReader(cancelAction: onCancel)) } - func readerIsReady(title: String, amount: String, onCancel: @escaping () -> Void) { + func tapOrInsertCard(title: String, + amount: String, + inputMethods: CardReaderInput, + onCancel: @escaping () -> Void) { self.name = title self.amount = amount // Initial presentation of the modal view controller. We need to provide // a customer name and an amount. - let viewModel = readerIsReady(onCancel: onCancel) - presentViewModel(viewModel: viewModel) - } - - func tapOrInsertCard(onCancel: @escaping () -> Void) { - let viewModel = tapOrInsert(onCancel: onCancel) + let viewModel = tapOrInsert(readerInputMethods: inputMethods, onCancel: onCancel) presentViewModel(viewModel: viewModel) } @@ -99,15 +98,12 @@ final class OrderDetailsPaymentAlerts: OrderDetailsPaymentAlertsProtocol { } private extension OrderDetailsPaymentAlerts { - func readerIsReady(onCancel: @escaping () -> Void) -> CardPresentPaymentsModalViewModel { - CardPresentModalReaderIsReady(name: name, - amount: amount, - transactionType: transactionType, - cancelAction: onCancel) - } - - func tapOrInsert(onCancel: @escaping () -> Void) -> CardPresentPaymentsModalViewModel { - CardPresentModalTapCard(name: name, amount: amount, transactionType: transactionType, onCancel: onCancel) + func tapOrInsert(readerInputMethods: CardReaderInput, onCancel: @escaping () -> Void) -> CardPresentPaymentsModalViewModel { + CardPresentModalTapCard(name: name, + amount: amount, + transactionType: transactionType, + inputMethods: readerInputMethods, + onCancel: onCancel) } func displayMessage(message: String) -> CardPresentPaymentsModalViewModel { diff --git a/WooCommerce/Classes/ViewModels/Order Details/OrderDetailsPaymentAlertsProtocol.swift b/WooCommerce/Classes/ViewModels/Order Details/OrderDetailsPaymentAlertsProtocol.swift index 88fe90e5981..6881ef89786 100644 --- a/WooCommerce/Classes/ViewModels/Order Details/OrderDetailsPaymentAlertsProtocol.swift +++ b/WooCommerce/Classes/ViewModels/Order Details/OrderDetailsPaymentAlertsProtocol.swift @@ -1,4 +1,5 @@ import UIKit +import Yosemite /// Protocol for `OrderDetailsPaymentAlerts` to enable unit testing. protocol OrderDetailsPaymentAlertsProtocol { @@ -6,9 +7,7 @@ protocol OrderDetailsPaymentAlertsProtocol { func preparingReader(onCancel: @escaping () -> Void) - func readerIsReady(title: String, amount: String, onCancel: @escaping () -> Void) - - func tapOrInsertCard(onCancel: @escaping () -> Void) + func tapOrInsertCard(title: String, amount: String, inputMethods: CardReaderInput, onCancel: @escaping () -> Void) func displayReaderMessage(message: String) diff --git a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift index cc2d0266716..7272af8d60a 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift @@ -286,11 +286,12 @@ private extension CollectOrderPaymentUseCase { paymentGatewayAccount: paymentGatewayAccount, paymentMethodTypes: configuration.paymentMethods.map(\.rawValue), stripeSmallestCurrencyUnitMultiplier: configuration.stripeSmallestCurrencyUnitMultiplier, - onWaitingForInput: { [weak self] in + onWaitingForInput: { [weak self] inputMethods in guard let self = self else { return } - self.alerts.readerIsReady(title: Localization.collectPaymentTitle(username: self.order.billingAddress?.firstName), - amount: self.formattedAmount, - onCancel: { [weak self] in + self.alerts.tapOrInsertCard(title: Localization.collectPaymentTitle(username: self.order.billingAddress?.firstName), + amount: self.formattedAmount, + inputMethods: inputMethods, + onCancel: { [weak self] in self?.cancelPayment { onCompletion(.failure(CollectOrderPaymentUseCaseError.flowCanceledByUser)) } diff --git a/WooCommerce/Classes/ViewRelated/Orders/Refund/RefundSubmissionUseCase.swift b/WooCommerce/Classes/ViewRelated/Orders/Refund/RefundSubmissionUseCase.swift index ca04865d73e..5dc89901706 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Refund/RefundSubmissionUseCase.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Refund/RefundSubmissionUseCase.swift @@ -303,12 +303,13 @@ private extension RefundSubmissionUseCase { cardPresentRefundOrchestrator.refund(amount: refundAmount, charge: charge, paymentGatewayAccount: paymentGatewayAccount, - onWaitingForInput: { [weak self] in + onWaitingForInput: { [weak self] inputMethods in // Requests card input. guard let self = self else { return } - self.alerts.readerIsReady(title: Localization.refundPaymentTitle(username: self.order.billingAddress?.firstName), - amount: self.formattedAmount, - onCancel: { [weak self] in + self.alerts.tapOrInsertCard(title: Localization.refundPaymentTitle(username: self.order.billingAddress?.firstName), + amount: self.formattedAmount, + inputMethods: inputMethods, + onCancel: { [weak self] in self?.cancelRefund(charge: charge, paymentGatewayAccount: paymentGatewayAccount, onCompletion: onCompletion) }) }, onProcessingMessage: { [weak self] in diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index ff8fd50f05e..6c428ef9248 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -463,8 +463,8 @@ 035C6DEB273EA12D00F70406 /* SoftwareUpdateTypeProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035C6DEA273EA12D00F70406 /* SoftwareUpdateTypeProperty.swift */; }; 035F2308275690970019E1B0 /* CardPresentModalConnectingFailedUpdatePostalCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035F2307275690970019E1B0 /* CardPresentModalConnectingFailedUpdatePostalCode.swift */; }; 0366EAE12909A37800B51755 /* JustInTimeMessageAnnouncementCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0366EAE02909A37800B51755 /* JustInTimeMessageAnnouncementCardViewModel.swift */; }; - 036CA6F129229C9E00E4DF4F /* IndefiniteCircularProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036CA6F029229C9E00E4DF4F /* IndefiniteCircularProgressViewStyle.swift */; }; 036CA6B9291E8D4B00E4DF4F /* CardPresentModalPreparingReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036CA6B8291E8D4B00E4DF4F /* CardPresentModalPreparingReader.swift */; }; + 036CA6F129229C9E00E4DF4F /* IndefiniteCircularProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036CA6F029229C9E00E4DF4F /* IndefiniteCircularProgressViewStyle.swift */; }; 036F6EA6281847D5006D84F8 /* PaymentCaptureOrchestratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036F6EA5281847D5006D84F8 /* PaymentCaptureOrchestratorTests.swift */; }; 0371C3682875E47B00277E2C /* FeatureAnnouncementCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0371C3672875E47B00277E2C /* FeatureAnnouncementCardViewModel.swift */; }; 0371C36A2876DBCA00277E2C /* FeatureAnnouncementCardViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0371C3692876DBCA00277E2C /* FeatureAnnouncementCardViewModelTests.swift */; }; @@ -1575,7 +1575,6 @@ D449C52926DFBCCC00D75B02 /* WhatsNewHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D449C52826DFBCCC00D75B02 /* WhatsNewHostingController.swift */; }; D449C52C26E02F2F00D75B02 /* WhatsNewFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D449C52B26E02F2F00D75B02 /* WhatsNewFactoryTests.swift */; }; D802541F2655137A001B2CC1 /* CardPresentModalNonRetryableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D802541E2655137A001B2CC1 /* CardPresentModalNonRetryableError.swift */; }; - D802546B2655180A001B2CC1 /* CardPresentModalReaderIsReadyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D802546A2655180A001B2CC1 /* CardPresentModalReaderIsReadyTests.swift */; }; D802547326551D0F001B2CC1 /* CardPresentModalTapCardTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D802547226551D0F001B2CC1 /* CardPresentModalTapCardTests.swift */; }; D802547826551DB8001B2CC1 /* CardPresentModalDisplayMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D802547726551DB8001B2CC1 /* CardPresentModalDisplayMessageTests.swift */; }; D802547D26551EF2001B2CC1 /* CardPresentModalSuccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D802547C26551EF2001B2CC1 /* CardPresentModalSuccessTests.swift */; }; @@ -1650,7 +1649,6 @@ D8815ADF26383EE700EDAD62 /* CardPresentPaymentsModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8815ADD26383EE600EDAD62 /* CardPresentPaymentsModalViewController.swift */; }; D8815AE026383EE700EDAD62 /* CardPresentPaymentsModalViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8815ADE26383EE700EDAD62 /* CardPresentPaymentsModalViewController.xib */; }; D8815AE726383FD600EDAD62 /* CardPresentPaymentsModalViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8815AE626383FD600EDAD62 /* CardPresentPaymentsModalViewModel.swift */; }; - D8815AFB26384A1F00EDAD62 /* CardPresentModalReaderIsReady.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8815AFA26384A1F00EDAD62 /* CardPresentModalReaderIsReady.swift */; }; D8815B0126385E3F00EDAD62 /* CardPresentModalTapCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8815B0026385E3F00EDAD62 /* CardPresentModalTapCard.swift */; }; D8815B0D263861A400EDAD62 /* CardPresentModalSuccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8815B0C263861A400EDAD62 /* CardPresentModalSuccess.swift */; }; D8815B132638686200EDAD62 /* CardPresentModalError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8815B122638686200EDAD62 /* CardPresentModalError.swift */; }; @@ -2433,8 +2431,8 @@ 035C6DEA273EA12D00F70406 /* SoftwareUpdateTypeProperty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftwareUpdateTypeProperty.swift; sourceTree = ""; }; 035F2307275690970019E1B0 /* CardPresentModalConnectingFailedUpdatePostalCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalConnectingFailedUpdatePostalCode.swift; sourceTree = ""; }; 0366EAE02909A37800B51755 /* JustInTimeMessageAnnouncementCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustInTimeMessageAnnouncementCardViewModel.swift; sourceTree = ""; }; - 036CA6F029229C9E00E4DF4F /* IndefiniteCircularProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndefiniteCircularProgressViewStyle.swift; sourceTree = ""; }; 036CA6B8291E8D4B00E4DF4F /* CardPresentModalPreparingReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalPreparingReader.swift; sourceTree = ""; }; + 036CA6F029229C9E00E4DF4F /* IndefiniteCircularProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndefiniteCircularProgressViewStyle.swift; sourceTree = ""; }; 036F6EA5281847D5006D84F8 /* PaymentCaptureOrchestratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentCaptureOrchestratorTests.swift; sourceTree = ""; }; 0371C3672875E47B00277E2C /* FeatureAnnouncementCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureAnnouncementCardViewModel.swift; sourceTree = ""; }; 0371C3692876DBCA00277E2C /* FeatureAnnouncementCardViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureAnnouncementCardViewModelTests.swift; sourceTree = ""; }; @@ -3554,7 +3552,6 @@ D449C52826DFBCCC00D75B02 /* WhatsNewHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewHostingController.swift; sourceTree = ""; }; D449C52B26E02F2F00D75B02 /* WhatsNewFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewFactoryTests.swift; sourceTree = ""; }; D802541E2655137A001B2CC1 /* CardPresentModalNonRetryableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalNonRetryableError.swift; sourceTree = ""; }; - D802546A2655180A001B2CC1 /* CardPresentModalReaderIsReadyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalReaderIsReadyTests.swift; sourceTree = ""; }; D802547226551D0F001B2CC1 /* CardPresentModalTapCardTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalTapCardTests.swift; sourceTree = ""; }; D802547726551DB8001B2CC1 /* CardPresentModalDisplayMessageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalDisplayMessageTests.swift; sourceTree = ""; }; D802547C26551EF2001B2CC1 /* CardPresentModalSuccessTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalSuccessTests.swift; sourceTree = ""; }; @@ -3629,7 +3626,6 @@ D8815ADD26383EE600EDAD62 /* CardPresentPaymentsModalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardPresentPaymentsModalViewController.swift; sourceTree = ""; }; D8815ADE26383EE700EDAD62 /* CardPresentPaymentsModalViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CardPresentPaymentsModalViewController.xib; sourceTree = ""; }; D8815AE626383FD600EDAD62 /* CardPresentPaymentsModalViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentPaymentsModalViewModel.swift; sourceTree = ""; }; - D8815AFA26384A1F00EDAD62 /* CardPresentModalReaderIsReady.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalReaderIsReady.swift; sourceTree = ""; }; D8815B0026385E3F00EDAD62 /* CardPresentModalTapCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalTapCard.swift; sourceTree = ""; }; D8815B0C263861A400EDAD62 /* CardPresentModalSuccess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalSuccess.swift; sourceTree = ""; }; D8815B122638686200EDAD62 /* CardPresentModalError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalError.swift; sourceTree = ""; }; @@ -8279,7 +8275,6 @@ D8025469265517F9001B2CC1 /* CardPresentPayments */ = { isa = PBXGroup; children = ( - D802546A2655180A001B2CC1 /* CardPresentModalReaderIsReadyTests.swift */, D802547226551D0F001B2CC1 /* CardPresentModalTapCardTests.swift */, D802547726551DB8001B2CC1 /* CardPresentModalDisplayMessageTests.swift */, D802547C26551EF2001B2CC1 /* CardPresentModalSuccessTests.swift */, @@ -8404,7 +8399,6 @@ children = ( D8815AE626383FD600EDAD62 /* CardPresentPaymentsModalViewModel.swift */, 311237ED2714DA240033C44E /* CardPresentModalDisplayMessage.swift */, - D8815AFA26384A1F00EDAD62 /* CardPresentModalReaderIsReady.swift */, D8815B0026385E3F00EDAD62 /* CardPresentModalTapCard.swift */, D8815B0C263861A400EDAD62 /* CardPresentModalSuccess.swift */, E16715CA26663B0B00326230 /* CardPresentModalSuccessWithoutEmail.swift */, @@ -10209,7 +10203,6 @@ 027D67D1245ADDF40036B8DB /* FilterTypeViewModel+Helpers.swift in Sources */, 581D5052274AA2480089B6AD /* View+AutofocusTextModifier.swift in Sources */, E15FC74126BC1CED00CF83E6 /* AttributedText.swift in Sources */, - D8815AFB26384A1F00EDAD62 /* CardPresentModalReaderIsReady.swift in Sources */, 31316F9C25CB20FD00D9F129 /* OrderStatusListViewModel.swift in Sources */, DE6906E727D74A1900735E3B /* WooSplitViewController.swift in Sources */, 0386CFEB2852108B00134466 /* PaymentMethodsHostingController.swift in Sources */, @@ -11121,7 +11114,6 @@ 02279590237A5DC900787C63 /* AztecUnorderedListFormatBarCommandTests.swift in Sources */, B958A7D128B5281800823EEF /* UniversalLinkRouterTests.swift in Sources */, B5F571AB21BEECB60010D1B8 /* NoteWooTests.swift in Sources */, - D802546B2655180A001B2CC1 /* CardPresentModalReaderIsReadyTests.swift in Sources */, 03EF250428C6283B006A033E /* InPersonPaymentsMenuViewModelTests.swift in Sources */, 45DB706C26161F970064A6CF /* DecimalWooTests.swift in Sources */, CCE4CD172667EBB100E09FD4 /* ShippingLabelPaymentMethodsViewModelTests.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/Mocks/MockOrderDetailsPaymentAlerts.swift b/WooCommerce/WooCommerceTests/Mocks/MockOrderDetailsPaymentAlerts.swift index b179c810263..4307b5014d1 100644 --- a/WooCommerce/WooCommerceTests/Mocks/MockOrderDetailsPaymentAlerts.swift +++ b/WooCommerce/WooCommerceTests/Mocks/MockOrderDetailsPaymentAlerts.swift @@ -1,11 +1,10 @@ @testable import WooCommerce +import Yosemite import UIKit /// Mock for `OrderDetailsPaymentAlertsProtocol`. final class MockOrderDetailsPaymentAlerts { // Public closures to mock alert actions and properties for assertions. - var cancelReaderIsReadyAlert: (() -> Void)? - var cancelPreparingReaderAlert: (() -> Void)? var cancelTapOrInsertCardAlert: (() -> Void)? @@ -29,11 +28,7 @@ extension MockOrderDetailsPaymentAlerts: OrderDetailsPaymentAlertsProtocol { // no-op } - func readerIsReady(title: String, amount: String, onCancel: @escaping () -> Void) { - cancelReaderIsReadyAlert = onCancel - } - - func tapOrInsertCard(onCancel: @escaping () -> Void) { + func tapOrInsertCard(title: String, amount: String, inputMethods: Yosemite.CardReaderInput, onCancel: @escaping () -> Void) { cancelTapOrInsertCardAlert = onCancel } diff --git a/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CardPresentModalReaderIsReadyTests.swift b/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CardPresentModalReaderIsReadyTests.swift deleted file mode 100644 index fcc365f3c4f..00000000000 --- a/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CardPresentModalReaderIsReadyTests.swift +++ /dev/null @@ -1,65 +0,0 @@ -import XCTest -import TestKit -@testable import WooCommerce -@testable import Yosemite - -final class CardPresentModalReaderIsReadyTests: XCTestCase { - private var viewModel: CardPresentModalReaderIsReady! - - override func setUp() { - super.setUp() - viewModel = CardPresentModalReaderIsReady(name: Expectations.name, - amount: Expectations.amount, - transactionType: .collectPayment, - cancelAction: {}) - } - - override func tearDown() { - viewModel = nil - super.tearDown() - } - - func test_viewmodel_provides_expected_image() { - XCTAssertEqual(viewModel.image, Expectations.image) - } - - func test_topTitle_provides_expected_title() { - XCTAssertEqual(viewModel.topTitle, Expectations.name) - } - - func test_topSubtitle_provides_expected_title() { - XCTAssertEqual(viewModel.topSubtitle, Expectations.amount) - } - - func test_primary_button_title_is_nil() { - XCTAssertNil(viewModel.primaryButtonTitle) - } - - func test_secondary_button_title_is_not_nil() { - XCTAssertNotNil(viewModel.secondaryButtonTitle) - } - - func test_auxiliary_button_title_is_nil() { - XCTAssertNil(viewModel.auxiliaryButtonTitle) - } - - func test_bottom_title_is_not_nil() { - XCTAssertNotNil(viewModel.bottomTitle) - } - - func test_bottom_subTitle_is_not_nil() { - XCTAssertNotNil(viewModel.bottomSubtitle) - } -} - - -private extension CardPresentModalReaderIsReadyTests { - enum Expectations { - static let name = "name" - static let amount = "amount" - static let image = UIImage.cardPresentImage - static let cardReaderModel = "WISEPAD_3" - static let countryCode = "CA" - static let paymentGatewayAccountID = "woocommerce-payments" - } -} diff --git a/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CardPresentModalTapCardTests.swift b/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CardPresentModalTapCardTests.swift index ff3ad858b5e..4d0e7c59705 100644 --- a/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CardPresentModalTapCardTests.swift +++ b/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CardPresentModalTapCardTests.swift @@ -14,6 +14,7 @@ final class CardPresentModalTapCardTests: XCTestCase { name: Expectations.name, amount: Expectations.amount, transactionType: .collectPayment, + inputMethods: [.tap, .swipe, .insert], onCancel: closures.onCancel() ) } diff --git a/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CollectOrderPaymentUseCaseTests.swift b/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CollectOrderPaymentUseCaseTests.swift index 266f6c226cf..89ea69b8089 100644 --- a/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CollectOrderPaymentUseCaseTests.swift +++ b/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/CollectOrderPaymentUseCaseTests.swift @@ -244,7 +244,7 @@ private extension CollectOrderPaymentUseCaseTests { } else if case let .cancelPayment(completion) = action { completion?(.success(())) } else if case let .collectPayment(_, _, _, onCardReaderMessage, _, _) = action { - onCardReaderMessage(.waitingForInput("")) + onCardReaderMessage(.waitingForInput([])) } } diff --git a/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/PaymentCaptureOrchestratorTests.swift b/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/PaymentCaptureOrchestratorTests.swift index 7a416e9679d..8e3d4ce2b09 100644 --- a/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/PaymentCaptureOrchestratorTests.swift +++ b/WooCommerce/WooCommerceTests/ViewModels/CardPresentPayments/PaymentCaptureOrchestratorTests.swift @@ -45,7 +45,7 @@ final class PaymentCaptureOrchestratorTests: XCTestCase { paymentGatewayAccount: account, paymentMethodTypes: ["card_present"], stripeSmallestCurrencyUnitMultiplier: 100, - onWaitingForInput: {}, + onWaitingForInput: { _ in }, onProcessingMessage: {}, onDisplayMessage: { _ in }, onProcessingCompletion: { _ in }, @@ -80,7 +80,7 @@ final class PaymentCaptureOrchestratorTests: XCTestCase { paymentGatewayAccount: account, paymentMethodTypes: ["card_present"], stripeSmallestCurrencyUnitMultiplier: 100, - onWaitingForInput: {}, + onWaitingForInput: { _ in }, onProcessingMessage: {}, onDisplayMessage: { _ in }, onProcessingCompletion: { _ in }, diff --git a/Yosemite/Yosemite/Model/Model.swift b/Yosemite/Yosemite/Model/Model.swift index c226e0e7c9e..f5419d23dba 100644 --- a/Yosemite/Yosemite/Model/Model.swift +++ b/Yosemite/Yosemite/Model/Model.swift @@ -138,6 +138,7 @@ public typealias WooAPIVersion = Networking.WooAPIVersion public typealias StoredProductSettings = Networking.StoredProductSettings public typealias CardReader = Hardware.CardReader public typealias CardReaderEvent = Hardware.CardReaderEvent +public typealias CardReaderInput = Hardware.CardReaderInput public typealias CardReaderSoftwareUpdateState = Hardware.CardReaderSoftwareUpdateState public typealias CardReaderServiceDiscoveryStatus = Hardware.CardReaderServiceDiscoveryStatus public typealias CardReaderServiceError = Hardware.CardReaderServiceError