Skip to content

Commit 7ca6531

Browse files
authored
Merge release/23.7 into trunk (#16416)
2 parents 2cb584a + 56e1a54 commit 7ca6531

File tree

24 files changed

+19459
-33
lines changed

24 files changed

+19459
-33
lines changed

Modules/Sources/Experiments/DefaultFeatureFlagService.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
9898
return true
9999
case .ciabBookings:
100100
return buildConfig == .localDeveloper || buildConfig == .alpha
101+
case .ciab:
102+
return buildConfig == .localDeveloper || buildConfig == .alpha
101103
case .pointOfSaleSurveys:
102104
return true
103105
case .pointOfSaleCatalogAPI:

Modules/Sources/Experiments/FeatureFlag.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ public enum FeatureFlag: Int {
204204
///
205205
case ciabBookings
206206

207+
/// Represents CIAB environment availability overall
208+
/// Has same underlying logic as `ciabBookings` flag.
209+
///
210+
case ciab
211+
207212
/// Enables surveys for potential and current POS merchants
208213
///
209214
case pointOfSaleSurveys

Modules/Sources/Yosemite/Stores/OrderCardPresentPaymentEligibilityStore.swift

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import Foundation
22
import protocol Storage.StorageManagerType
33
import protocol NetworkingCore.Network
4+
import protocol WooFoundation.CrashLogger
5+
import enum WooFoundation.SeverityLevel
46

57
/// Determines whether an order is eligible for card present payment or not
68
///
79
public final class OrderCardPresentPaymentEligibilityStore: Store {
810
private let currentSite: () -> Site?
11+
private let isCIABEnvironmentSupported: () -> Bool
12+
private let crashLogger: CrashLogger
913
private lazy var siteCIABEligibilityChecker: CIABEligibilityCheckerProtocol = CIABEligibilityChecker(
1014
currentSite: currentSite
1115
)
@@ -14,9 +18,13 @@ public final class OrderCardPresentPaymentEligibilityStore: Store {
1418
dispatcher: Dispatcher,
1519
storageManager: StorageManagerType,
1620
network: Network,
21+
crashLogger: CrashLogger,
22+
isCIABEnvironmentSupported: @escaping () -> Bool,
1723
currentSite: @escaping () -> Site?
1824
) {
1925
self.currentSite = currentSite
26+
self.isCIABEnvironmentSupported = isCIABEnvironmentSupported
27+
self.crashLogger = crashLogger
2028
super.init(
2129
dispatcher: dispatcher,
2230
storageManager: storageManager,
@@ -56,20 +64,38 @@ private extension OrderCardPresentPaymentEligibilityStore {
5664
onCompletion: (Result<Bool, Error>) -> Void) {
5765
let storage = storageManager.viewStorage
5866

59-
guard let site = storage.loadSite(siteID: siteID)?.toReadOnly() else {
60-
return onCompletion(
61-
.failure(
62-
OrderIsEligibleForCardPresentPaymentError.siteNotFoundInStorage
63-
)
64-
)
65-
}
67+
/// The following checks are only relevant if CIAB is rolled out.
68+
if isCIABEnvironmentSupported() {
69+
let storageSite = storage.loadSite(siteID: siteID)?.toReadOnly()
6670

67-
guard siteCIABEligibilityChecker.isFeatureSupported(.cardReader, for: site) else {
68-
return onCompletion(
69-
.failure(
70-
OrderIsEligibleForCardPresentPaymentError.cardReaderPaymentOptionIsNotSupportedForCIABSites
71+
let site: Site?
72+
if let storageSite {
73+
site = storageSite
74+
} else {
75+
/// Non - fatal fallback to `currentSite` when a storage site is missing
76+
site = currentSite()
77+
78+
logFailedStorageSiteRead(
79+
siteID: siteID,
80+
currentSiteFallbackValue: site
7181
)
72-
)
82+
}
83+
84+
if let site {
85+
guard siteCIABEligibilityChecker.isFeatureSupported(.cardReader, for: site) else {
86+
return onCompletion(
87+
.failure(
88+
OrderIsEligibleForCardPresentPaymentError.cardReaderPaymentOptionIsNotSupportedForCIABSites
89+
)
90+
)
91+
}
92+
} else {
93+
/// Don't interrupt the flow if the `site` is not found
94+
/// Making the assumption that it's not a CIAB site and skipping those checks
95+
///
96+
/// Log an error
97+
logFailedDefaultSiteRead(siteID: siteID)
98+
}
7399
}
74100

75101
guard let order = storage.loadOrder(siteID: siteID, orderID: orderID)?.toReadOnly() else {
@@ -83,10 +109,41 @@ private extension OrderCardPresentPaymentEligibilityStore {
83109
}
84110
}
85111

112+
/// Error logging
113+
private extension OrderCardPresentPaymentEligibilityStore {
114+
func logFailedStorageSiteRead(siteID: Int64, currentSiteFallbackValue: Site?) {
115+
let message = "OrderCardPresentPaymentEligibilityStore: Storage site missing, falling back to currentSite."
116+
117+
DDLogError(message)
118+
119+
crashLogger.logMessage(
120+
message,
121+
properties: [
122+
"siteID": siteID,
123+
"currentSiteID": currentSiteFallbackValue?.siteID ?? "empty",
124+
],
125+
level: .error
126+
)
127+
}
128+
129+
func logFailedDefaultSiteRead(siteID: Int64) {
130+
let message = "OrderCardPresentPaymentEligibilityStore: Current default site missing."
131+
132+
DDLogError(message)
133+
134+
crashLogger.logMessage(
135+
"OrderCardPresentPaymentEligibilityStore: Current default site missing.",
136+
properties: [
137+
"requestedSiteID": siteID
138+
],
139+
level: .error
140+
)
141+
}
142+
}
143+
86144
extension OrderCardPresentPaymentEligibilityStore {
87145
enum OrderIsEligibleForCardPresentPaymentError: Error {
88146
case orderNotFoundInStorage
89-
case siteNotFoundInStorage
90147
case cardReaderPaymentOptionIsNotSupportedForCIABSites
91148
}
92149
}

Modules/Tests/PointOfSaleTests/Mocks/MockFeatureFlagService.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ final class MockFeatureFlagService: POSFeatureFlagProviding {
2525
var isProductImageOptimizedHandlingEnabled: Bool
2626
var isFeatureFlagEnabledReturnValue: [FeatureFlag: Bool] = [:]
2727
var isCIABBookingsEnabled: Bool
28+
var isCIABEnabled: Bool
2829

2930
init(isInboxOn: Bool = false,
3031
isShowInboxCTAEnabled: Bool = false,
@@ -47,7 +48,8 @@ final class MockFeatureFlagService: POSFeatureFlagProviding {
4748
notificationSettings: Bool = false,
4849
allowMerchantAIAPIKey: Bool = false,
4950
isProductImageOptimizedHandlingEnabled: Bool = false,
50-
isCIABBookingsEnabled: Bool = false) {
51+
isCIABBookingsEnabled: Bool = false,
52+
isCIABEnabled: Bool = false) {
5153
self.isInboxOn = isInboxOn
5254
self.isShowInboxCTAEnabled = isShowInboxCTAEnabled
5355
self.isUpdateOrderOptimisticallyOn = isUpdateOrderOptimisticallyOn
@@ -70,6 +72,7 @@ final class MockFeatureFlagService: POSFeatureFlagProviding {
7072
self.allowMerchantAIAPIKey = allowMerchantAIAPIKey
7173
self.isProductImageOptimizedHandlingEnabled = isProductImageOptimizedHandlingEnabled
7274
self.isCIABBookingsEnabled = isCIABBookingsEnabled
75+
self.isCIABEnabled = isCIABEnabled
7376
}
7477

7578
func isFeatureFlagEnabled(_ featureFlag: FeatureFlag) -> Bool {
@@ -124,6 +127,8 @@ final class MockFeatureFlagService: POSFeatureFlagProviding {
124127
return isProductImageOptimizedHandlingEnabled
125128
case .ciabBookings:
126129
return isCIABBookingsEnabled
130+
case .ciab:
131+
return isCIABEnabled
127132
default:
128133
return false
129134
}

Modules/Tests/YosemiteTests/Stores/OrderCardPresentPaymentEligibilityStoreTests.swift

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import XCTest
44

55
@testable import Yosemite
66
@testable import Networking
7+
@testable import WooFoundation
78

89
final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
910

@@ -28,6 +29,7 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
2829
private var store: OrderCardPresentPaymentEligibilityStore!
2930

3031
private var currentSite: Site?
32+
private var isCIABSupported = true
3133

3234
override func setUp() {
3335
super.setUp()
@@ -38,6 +40,10 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
3840
dispatcher: dispatcher,
3941
storageManager: storageManager,
4042
network: network,
43+
crashLogger: MockCrashLogger(),
44+
isCIABEnvironmentSupported: { [weak self] in
45+
return self?.isCIABSupported ?? false
46+
},
4147
currentSite: { [weak self] in
4248
return self?.currentSite
4349
}
@@ -46,6 +52,7 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
4652

4753
override func tearDown() {
4854
currentSite = nil
55+
isCIABSupported = true
4956
super.tearDown()
5057
}
5158

@@ -98,6 +105,53 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
98105
XCTAssertTrue(eligibility)
99106
}
100107

108+
func test_orderIsEligibleForCardPresentPayment_returns_true_for_eligible_order_and_none_stored_site() throws {
109+
// Given
110+
let orderItem = OrderItem.fake().copy(itemID: 1234,
111+
name: "Chocolate cake",
112+
productID: 678,
113+
quantity: 1.0)
114+
let cppEligibleOrder = Order.fake().copy(siteID: sampleSiteID,
115+
orderID: 111,
116+
status: .pending,
117+
currency: "USD",
118+
datePaid: nil,
119+
total: "5.00",
120+
paymentMethodID: "woocommerce_payments",
121+
items: [orderItem])
122+
let nonSubscriptionProduct = Product.fake().copy(siteID: sampleSiteID,
123+
productID: 678,
124+
name: "Chocolate cake",
125+
productTypeKey: "simple")
126+
127+
let regularSite = Site.fake().copy(
128+
siteID: sampleSiteID,
129+
isGarden: false,
130+
gardenName: nil
131+
)
132+
self.currentSite = regularSite
133+
134+
storageManager.insertSampleProduct(readOnlyProduct: nonSubscriptionProduct)
135+
storageManager.insertSampleOrder(readOnlyOrder: cppEligibleOrder)
136+
137+
let configuration = CardPresentPaymentsConfiguration(country: .US)
138+
139+
// When
140+
let result = waitFor { promise in
141+
let action = OrderCardPresentPaymentEligibilityAction
142+
.orderIsEligibleForCardPresentPayment(orderID: 111,
143+
siteID: self.sampleSiteID,
144+
cardPresentPaymentsConfiguration: configuration) { result in
145+
promise(result)
146+
}
147+
self.store.onAction(action)
148+
}
149+
150+
// Then
151+
let eligibility = try XCTUnwrap(result.get())
152+
XCTAssertTrue(eligibility)
153+
}
154+
101155
func test_orderIsEligibleForCardPresentPayment_returns_failure_for_CIAB_sites() throws {
102156
// Given
103157
let orderItem = OrderItem.fake().copy(itemID: 1234,
@@ -147,4 +201,96 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase {
147201
.cardReaderPaymentOptionIsNotSupportedForCIABSites)
148202
}
149203
}
204+
205+
func test_orderIsEligibleForCardPresentPayment_returns_success_when_site_is_CIAB_and_CIAB_not_supported() throws {
206+
// Given
207+
208+
/// Simulate that the CIAB environment support is not yet rolled out
209+
isCIABSupported = false
210+
211+
let orderItem = OrderItem.fake().copy(itemID: 1234,
212+
name: "Chocolate cake",
213+
productID: 678,
214+
quantity: 1.0)
215+
let cppEligibleOrder = Order.fake().copy(siteID: sampleSiteID,
216+
orderID: 111,
217+
status: .pending,
218+
currency: "USD",
219+
datePaid: nil,
220+
total: "5.00",
221+
paymentMethodID: "woocommerce_payments",
222+
items: [orderItem])
223+
let nonSubscriptionProduct = Product.fake().copy(siteID: sampleSiteID,
224+
productID: 678,
225+
name: "Chocolate cake",
226+
productTypeKey: "simple")
227+
228+
let ciabSite = Site.fake().copy(
229+
siteID: sampleSiteID,
230+
isGarden: true,
231+
gardenName: "commerce"
232+
)
233+
self.currentSite = ciabSite
234+
235+
storageManager.insertSampleSite(readOnlySite: ciabSite)
236+
storageManager.insertSampleProduct(readOnlyProduct: nonSubscriptionProduct)
237+
storageManager.insertSampleOrder(readOnlyOrder: cppEligibleOrder)
238+
239+
let configuration = CardPresentPaymentsConfiguration(country: .US)
240+
241+
// When
242+
let result = waitFor { promise in
243+
let action = OrderCardPresentPaymentEligibilityAction
244+
.orderIsEligibleForCardPresentPayment(orderID: 111,
245+
siteID: self.sampleSiteID,
246+
cardPresentPaymentsConfiguration: configuration) { result in
247+
promise(result)
248+
}
249+
self.store.onAction(action)
250+
}
251+
252+
// Then
253+
let eligibility = try XCTUnwrap(result.get())
254+
XCTAssertTrue(eligibility)
255+
}
256+
257+
func test_orderIsEligibleForCardPresentPayment_returns_success_when_site_is_not_obtained_and_CIAB_supported() throws {
258+
// Given
259+
let orderItem = OrderItem.fake().copy(itemID: 1234,
260+
name: "Chocolate cake",
261+
productID: 678,
262+
quantity: 1.0)
263+
let cppEligibleOrder = Order.fake().copy(siteID: sampleSiteID,
264+
orderID: 111,
265+
status: .pending,
266+
currency: "USD",
267+
datePaid: nil,
268+
total: "5.00",
269+
paymentMethodID: "woocommerce_payments",
270+
items: [orderItem])
271+
let nonSubscriptionProduct = Product.fake().copy(siteID: sampleSiteID,
272+
productID: 678,
273+
name: "Chocolate cake",
274+
productTypeKey: "simple")
275+
276+
storageManager.insertSampleProduct(readOnlyProduct: nonSubscriptionProduct)
277+
storageManager.insertSampleOrder(readOnlyOrder: cppEligibleOrder)
278+
279+
let configuration = CardPresentPaymentsConfiguration(country: .US)
280+
281+
// When
282+
let result = waitFor { promise in
283+
let action = OrderCardPresentPaymentEligibilityAction
284+
.orderIsEligibleForCardPresentPayment(orderID: 111,
285+
siteID: self.sampleSiteID,
286+
cardPresentPaymentsConfiguration: configuration) { result in
287+
promise(result)
288+
}
289+
self.store.onAction(action)
290+
}
291+
292+
// Then
293+
let eligibility = try XCTUnwrap(result.get())
294+
XCTAssertTrue(eligibility)
295+
}
150296
}

WooCommerce/Classes/Yosemite/AuthenticatedState.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,12 @@ class AuthenticatedState: StoresManagerState {
8686
dispatcher: dispatcher,
8787
storageManager: storageManager,
8888
network: network,
89+
crashLogger: ServiceLocator.crashLogging,
90+
isCIABEnvironmentSupported: {
91+
ServiceLocator.featureFlagService.isFeatureFlagEnabled(.ciab)
92+
},
8993
currentSite: {
90-
ServiceLocator.stores.sessionManager.defaultSite
94+
sessionManager.defaultSite
9195
}
9296
),
9397
OrderNoteStore(dispatcher: dispatcher, storageManager: storageManager, network: network),

0 commit comments

Comments
 (0)