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
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@

import Foundation
import protocol Storage.StorageManagerType
import protocol NetworkingCore.Network

/// Determines whether an order is eligible for card present payment or not
///
public final class OrderCardPresentPaymentEligibilityStore: Store {
private let currentSite: () -> Site?
private lazy var siteCIABEligibilityChecker: CIABEligibilityCheckerProtocol = CIABEligibilityChecker(
currentSite: currentSite
)

public init(
dispatcher: Dispatcher,
storageManager: StorageManagerType,
network: Network,
currentSite: @escaping () -> Site?
) {
self.currentSite = currentSite
super.init(
dispatcher: dispatcher,
storageManager: storageManager,
network: network
)
}

/// Registers for supported Actions.
///
override public func registerSupportedActions(in dispatcher: Dispatcher) {
Expand Down Expand Up @@ -35,11 +55,27 @@ private extension OrderCardPresentPaymentEligibilityStore {
cardPresentPaymentsConfiguration: CardPresentPaymentsConfiguration,
onCompletion: (Result<Bool, Error>) -> Void) {
let storage = storageManager.viewStorage

guard let site = storage.loadSite(siteID: siteID)?.toReadOnly() else {
return onCompletion(
.failure(
OrderIsEligibleForCardPresentPaymentError.siteNotFoundInStorage
)
)
}

guard siteCIABEligibilityChecker.isFeatureSupported(.cardReader, for: site) else {
return onCompletion(
.failure(
OrderIsEligibleForCardPresentPaymentError.cardReaderPaymentOptionIsNotSupportedForCIABSites
)
)
}

guard let order = storage.loadOrder(siteID: siteID, orderID: orderID)?.toReadOnly() else {
return onCompletion(.failure(OrderIsEligibleForCardPresentPaymentError.orderNotFoundInStorage))
}


let orderProductsIDs = order.items.map(\.productID)
let products = storage.loadProducts(siteID: siteID, productsIDs: orderProductsIDs).map { $0.toReadOnly() }

Expand All @@ -50,5 +86,7 @@ private extension OrderCardPresentPaymentEligibilityStore {
extension OrderCardPresentPaymentEligibilityStore {
enum OrderIsEligibleForCardPresentPaymentError: Error {
case orderNotFoundInStorage
case siteNotFoundInStorage
case cardReaderPaymentOptionIsNotSupportedForCIABSites
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
/// Describes the feature set affected by CIAB sites
/// By the moment of introduction the features aren't supported by CIAB sites

/// periphery: ignore:all - Used through `.allCases`
enum CIABAffectedFeature: CaseIterable {
public enum CIABAffectedFeature: CaseIterable {
case blaze
case payments
case splitShipments
case groupedProducts
case variableProducts
case giftCardEditing
case productsStockDashboardCard
case pointOfSale
case cardReader
}

extension CIABAffectedFeature {
Expand Down
48 changes: 48 additions & 0 deletions Modules/Sources/Yosemite/Tools/CIAB/CIABEligibilityChecker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Foundation

public final class CIABEligibilityChecker {
public typealias ObtainSiteClosure = () -> Site?

public let currentSite: ObtainSiteClosure

public init(currentSite: @escaping ObtainSiteClosure) {
self.currentSite = currentSite
}
}

extension CIABEligibilityChecker: CIABEligibilityCheckerProtocol {
public var isCurrentSiteCIAB: Bool {
guard let currentSite = currentSite() else {
return false
}
return isSiteCIAB(currentSite)
}

public func isSiteCIAB(_ site: Site) -> Bool {
return site.isCIAB
}

public func isFeatureSupportedForCurrentSite(_ feature: CIABAffectedFeature) -> Bool {
return !isCurrentSiteCIAB || !CIABAffectedFeature.unsupportedFeatures.contains(feature)
}

public func isFeatureSupported(
_ feature: CIABAffectedFeature,
for site: Site
) -> Bool {
return !isSiteCIAB(site) || !CIABAffectedFeature.unsupportedFeatures.contains(feature)
}
}

// MARK: - Site checks

private extension Site {
var isCIAB: Bool {
return isGarden && gardenName == GardenName.commerce.rawValue
}
}

private enum GardenName: String {
/// Garden name for CIAB sites
case commerce
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation
import Yosemite

protocol CIABEligibilityCheckerProtocol {
/// periphery: ignore
public protocol CIABEligibilityCheckerProtocol {
var isCurrentSiteCIAB: Bool { get }

func isSiteCIAB(_ site: Site) -> Bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,26 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
///
private var store: OrderCardPresentPaymentEligibilityStore!

private var currentSite: Site?

override func setUp() {
super.setUp()
dispatcher = Dispatcher()
network = MockNetwork(useResponseQueue: true)
storageManager = MockStorageManager()
store = OrderCardPresentPaymentEligibilityStore(dispatcher: dispatcher, storageManager: storageManager, network: network)
store = OrderCardPresentPaymentEligibilityStore(
dispatcher: dispatcher,
storageManager: storageManager,
network: network,
currentSite: { [weak self] in
return self?.currentSite
}
)
}

override func tearDown() {
currentSite = nil
super.tearDown()
}

// Other behavioural tests are in Order_CardPresentPaymentTests
Expand All @@ -55,6 +69,14 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
name: "Chocolate cake",
productTypeKey: "simple")

let regularSite = Site.fake().copy(
siteID: sampleSiteID,
isGarden: false,
gardenName: nil
)
self.currentSite = regularSite

storageManager.insertSampleSite(readOnlySite: regularSite)
storageManager.insertSampleProduct(readOnlyProduct: nonSubscriptionProduct)
storageManager.insertSampleOrder(readOnlyOrder: cppEligibleOrder)

Expand All @@ -75,4 +97,54 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
let eligibility = try XCTUnwrap(result.get())
XCTAssertTrue(eligibility)
}

func test_orderIsEligibleForCardPresentPayment_returns_failure_for_CIAB_sites() throws {
// Given
let orderItem = OrderItem.fake().copy(itemID: 1234,
name: "Chocolate cake",
productID: 678,
quantity: 1.0)
let cppEligibleOrder = Order.fake().copy(siteID: sampleSiteID,
orderID: 111,
status: .pending,
currency: "USD",
datePaid: nil,
total: "5.00",
paymentMethodID: "woocommerce_payments",
items: [orderItem])
let nonSubscriptionProduct = Product.fake().copy(siteID: sampleSiteID,
productID: 678,
name: "Chocolate cake",
productTypeKey: "simple")

let ciabSite = Site.fake().copy(
siteID: sampleSiteID,
isGarden: true,
gardenName: "commerce"
)
self.currentSite = ciabSite

storageManager.insertSampleSite(readOnlySite: ciabSite)
storageManager.insertSampleProduct(readOnlyProduct: nonSubscriptionProduct)
storageManager.insertSampleOrder(readOnlyOrder: cppEligibleOrder)

let configuration = CardPresentPaymentsConfiguration(country: .US)

// When
let result = waitFor { promise in
let action = OrderCardPresentPaymentEligibilityAction
.orderIsEligibleForCardPresentPayment(orderID: 111,
siteID: self.sampleSiteID,
cardPresentPaymentsConfiguration: configuration) { result in
promise(result)
}
self.store.onAction(action)
}

// Then
XCTAssertThrowsError(try result.get()) { error in
XCTAssertEqual(error as? OrderCardPresentPaymentEligibilityStore.OrderIsEligibleForCardPresentPaymentError,
.cardReaderPaymentOptionIsNotSupportedForCIABSites)
}
}
}
49 changes: 7 additions & 42 deletions WooCommerce/Classes/CIAB/CIABEligibilityChecker.swift
Original file line number Diff line number Diff line change
@@ -1,47 +1,12 @@
import Foundation
import Yosemite

final class CIABEligibilityChecker {
private let stores: StoresManager

init(stores: StoresManager = ServiceLocator.stores) {
self.stores = stores
}
}

extension CIABEligibilityChecker: CIABEligibilityCheckerProtocol {
var isCurrentSiteCIAB: Bool {
guard let currentSite = stores.sessionManager.defaultSite else {
return false
}
return isSiteCIAB(currentSite)
}

func isSiteCIAB(_ site: Site) -> Bool {
return site.isCIAB
}

func isFeatureSupportedForCurrentSite(_ feature: CIABAffectedFeature) -> Bool {
return !isCurrentSiteCIAB || !CIABAffectedFeature.unsupportedFeatures.contains(feature)
extension CIABEligibilityChecker {
convenience init() {
self.init(
currentSite: {
return ServiceLocator.stores.sessionManager.defaultSite
}
)
}

func isFeatureSupported(
_ feature: CIABAffectedFeature,
for site: Site
) -> Bool {
return !isSiteCIAB(site) || !CIABAffectedFeature.unsupportedFeatures.contains(feature)
}
}

// MARK: - Site checks

private extension Site {
var isCIAB: Bool {
return isGarden && gardenName == GardenName.commerce.rawValue
}
}

private enum GardenName: String {
/// Garden name for CIAB sites
case commerce
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import protocol Yosemite.StoresManager
import class Yosemite.POSEligibilityService
import enum Yosemite.FeatureFlagAction
import class Yosemite.SiteAddress
import protocol Yosemite.CIABEligibilityCheckerProtocol
import class Yosemite.CIABEligibilityChecker
import enum Yosemite.POSCountryCurrencyValidator

final class POSTabVisibilityChecker: POSTabVisibilityCheckerProtocol {
Expand Down
9 changes: 8 additions & 1 deletion WooCommerce/Classes/Yosemite/AuthenticatedState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@ class AuthenticatedState: StoresManagerState {
MediaStore(dispatcher: dispatcher, storageManager: storageManager, network: network),
NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network),
NotificationCountStore(dispatcher: dispatcher, storageManager: storageManager, fileStorage: PListFileStorage()),
OrderCardPresentPaymentEligibilityStore(dispatcher: dispatcher, storageManager: storageManager, network: network),
OrderCardPresentPaymentEligibilityStore(
dispatcher: dispatcher,
storageManager: storageManager,
network: network,
currentSite: {
ServiceLocator.stores.sessionManager.defaultSite
}
),
OrderNoteStore(dispatcher: dispatcher, storageManager: storageManager, network: network),
OrderStore(dispatcher: dispatcher, storageManager: storageManager, network: network),
OrderStatusStore(dispatcher: dispatcher, storageManager: storageManager, network: network),
Expand Down
8 changes: 0 additions & 8 deletions WooCommerce/WooCommerce.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1013,9 +1013,7 @@
2DB8916B2E27F6D90001B175 /* OrderListCellViewModel+Localizations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB8916A2E27F6CE0001B175 /* OrderListCellViewModel+Localizations.swift */; };
2DB8916E2E27F7840001B175 /* OrderListCellViewModel+Localizations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB8916A2E27F6CE0001B175 /* OrderListCellViewModel+Localizations.swift */; };
2DCB54FA2E6AE8E100621F90 /* CIABEligibilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DCB54F92E6AE8D800621F90 /* CIABEligibilityChecker.swift */; };
2DCB54FC2E6AFE6A00621F90 /* CIABAffectedFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DCB54FB2E6AFE6900621F90 /* CIABAffectedFeature.swift */; };
2DE9DDFB2E6EF4A500155408 /* MockCIABEligibilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE9DDFA2E6EF4A300155408 /* MockCIABEligibilityChecker.swift */; };
2DE9DDFD2E6EF53C00155408 /* CIABEligibilityCheckerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE9DDFC2E6EF52E00155408 /* CIABEligibilityCheckerProtocol.swift */; };
2DF0D1BC2E2907C100F8995C /* MarkOrderAsReadUseCase+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB88DA32E27DD790001B175 /* MarkOrderAsReadUseCase+Woo.swift */; };
310D1B482734919E001D55B4 /* InPersonPaymentsLiveSiteInTestModeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 310D1B472734919E001D55B4 /* InPersonPaymentsLiveSiteInTestModeView.swift */; };
311237EE2714DA240033C44E /* CardPresentModalDisplayMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311237ED2714DA240033C44E /* CardPresentModalDisplayMessage.swift */; };
Expand Down Expand Up @@ -3897,9 +3895,7 @@
2DB891682E27F61C0001B175 /* OrderListCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderListCellViewModel.swift; sourceTree = "<group>"; };
2DB8916A2E27F6CE0001B175 /* OrderListCellViewModel+Localizations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrderListCellViewModel+Localizations.swift"; sourceTree = "<group>"; };
2DCB54F92E6AE8D800621F90 /* CIABEligibilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIABEligibilityChecker.swift; sourceTree = "<group>"; };
2DCB54FB2E6AFE6900621F90 /* CIABAffectedFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIABAffectedFeature.swift; sourceTree = "<group>"; };
2DE9DDFA2E6EF4A300155408 /* MockCIABEligibilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCIABEligibilityChecker.swift; sourceTree = "<group>"; };
2DE9DDFC2E6EF52E00155408 /* CIABEligibilityCheckerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIABEligibilityCheckerProtocol.swift; sourceTree = "<group>"; };
310D1B472734919E001D55B4 /* InPersonPaymentsLiveSiteInTestModeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsLiveSiteInTestModeView.swift; sourceTree = "<group>"; };
311237ED2714DA240033C44E /* CardPresentModalDisplayMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalDisplayMessage.swift; sourceTree = "<group>"; };
311D21E7264AEDB900102316 /* CardPresentModalScanningForReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalScanningForReader.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -7945,8 +7941,6 @@
2DCB54F82E6AE8C900621F90 /* CIAB */ = {
isa = PBXGroup;
children = (
2DE9DDFC2E6EF52E00155408 /* CIABEligibilityCheckerProtocol.swift */,
2DCB54FB2E6AFE6900621F90 /* CIABAffectedFeature.swift */,
2DCB54F92E6AE8D800621F90 /* CIABEligibilityChecker.swift */,
);
path = CIAB;
Expand Down Expand Up @@ -14166,7 +14160,6 @@
CE7F778B2C074D2500C89F4E /* EditableOrderShippingLineViewModel.swift in Sources */,
B95A45E92A77AE2C0073A91F /* CustomerSelectorViewModel.swift in Sources */,
024DF3072372C18D006658FE /* AztecUIConfigurator.swift in Sources */,
2DCB54FC2E6AFE6A00621F90 /* CIABAffectedFeature.swift in Sources */,
EE1B07F32C81CB4B006D9769 /* BlazeLocalNotificationScheduler.swift in Sources */,
DE02ABBE2B578D0E008E0AC4 /* CreditCardType.swift in Sources */,
020BE74823B05CF2007FE54C /* ProductInventoryEditableData.swift in Sources */,
Expand Down Expand Up @@ -14933,7 +14926,6 @@
B626C71B287659D60083820C /* CustomFieldsListView.swift in Sources */,
02ECD1E624FFB4E900735BE5 /* ProductFactory.swift in Sources */,
260520F42B87BA23005D5D59 /* WooAnalyticsEvent+ConnectivityTool.swift in Sources */,
2DE9DDFD2E6EF53C00155408 /* CIABEligibilityCheckerProtocol.swift in Sources */,
579CDEFF274D7E7900E8903D /* StoreStatsUsageTracksEventEmitter.swift in Sources */,
CEDBDA472B6BEF2E002047D4 /* AnalyticsWebReport.swift in Sources */,
314DC4BD268D158F00444C9E /* CardReaderSettingsKnownReadersProvider.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ import XCTest
@testable import Yosemite

class CIABEligibilityCheckerTests: XCTestCase {
private var storesManager: MockStoresManager!
private var sessionManager: SessionManager!
private var checker: CIABEligibilityChecker!

override func setUp() {
super.setUp()
sessionManager = .makeForTesting()
storesManager = MockStoresManager(sessionManager: sessionManager)
checker = CIABEligibilityChecker(stores: storesManager)
checker = CIABEligibilityChecker(
currentSite: {
return MockStoresManager(
sessionManager: self.sessionManager
).sessionManager.defaultSite
}
)
}

override func tearDown() {
checker = nil
storesManager = nil
sessionManager = nil
super.tearDown()
}
Expand Down