Skip to content

Commit e7e1677

Browse files
authored
Merge pull request #8681 from woocommerce/issue/8680-change-merchant-eligibility-condition
[IPPInAppFeedback] Change merchant eligibility condition
2 parents dbf8ff1 + 0c0b17b commit e7e1677

File tree

4 files changed

+153
-49
lines changed

4 files changed

+153
-49
lines changed

WooCommerce/Classes/ViewRelated/Orders/OrderListViewController.swift

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,11 @@ final class OrderListViewController: UIViewController, GhostableViewController {
162162
configureSyncingCoordinator()
163163

164164
if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.IPPInAppFeedbackBanner) {
165-
inPersonPaymentsSurveyVariation = viewModel.feedbackBannerSurveySource()
166-
167-
if let inPersonPaymentsSurveyVariation {
168-
viewModel.trackInPersonPaymentsFeedbackBannerShown(for: inPersonPaymentsSurveyVariation)
169-
} else {
170-
DDLogError("Couldn't assign an In-Person Payments survey variation")
171-
}
165+
viewModel.feedbackBannerSurveySource(onCompletion: { survey in
166+
// Only assign the survey once we're sure the data is fetched from storage
167+
inPersonPaymentsSurveyVariation = survey
168+
viewModel.trackInPersonPaymentsFeedbackBannerShown(for: survey)
169+
})
172170
}
173171
}
174172

@@ -813,15 +811,15 @@ private extension OrderListViewController {
813811
var campaign: FeatureAnnouncementCampaign = .inPersonPaymentsCashOnDelivery
814812

815813
switch survey {
816-
case .IPP_COD :
814+
case .inPersonPaymentsCashOnDelivery :
817815
bannerTitle = Localization.inPersonPaymentsCashOnDeliveryBannerTitle
818816
bannerText = Localization.inPersonPaymentsCashOnDeliveryBannerContent
819817
campaign = .inPersonPaymentsCashOnDelivery
820-
case .IPP_firstTransaction:
818+
case .inPersonPaymentsFirstTransaction:
821819
bannerTitle = Localization.inPersonPaymentsFirstTransactionBannerTitle
822820
bannerTitle = Localization.inPersonPaymentsFirstTransactionBannerContent
823821
campaign = .inPersonPaymentsFirstTransaction
824-
case .IPP_powerUsers:
822+
case .inPersonPaymentsPowerUsers:
825823
bannerTitle = Localization.inPersonPaymentsPowerUsersBannerTitle
826824
bannerTitle = Localization.inPersonPaymentsPowerUsersBannerContent
827825
campaign = .inPersonPaymentsPowerUsers

WooCommerce/Classes/ViewRelated/Orders/OrderListViewModel.swift

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,22 @@ final class OrderListViewModel {
6565
CardPresentConfigurationLoader().configuration.isSupportedCountry
6666
}
6767

68-
/// Results controller that fetches any IPP transactions via WooCommerce Payments
68+
/// Results controller that fetches any WooCommerce Payments In-Person Payments transactions
6969
///
70-
private lazy var IPPOrdersResultsController: ResultsController<StorageOrder> = {
71-
let paymentGateway = Constants.paymentMethodID
70+
private lazy var WCPayOrdersResultsController: ResultsController<StorageOrder> = {
71+
let wcpay = Constants.wcpayPaymentMethodID
7272
let predicate = NSPredicate(
7373
format: "siteID == %lld AND paymentMethodID == %@",
74-
argumentArray: [siteID, paymentGateway]
74+
argumentArray: [siteID, wcpay]
7575
)
7676
return ResultsController<StorageOrder>(storageManager: storageManager, matching: predicate, sortedBy: [])
7777
}()
7878

79-
/// Results controller that fetches IPP transactions via WooCommerce Payments, within the last 30 days
79+
/// Results controller that fetches WooCommerce Payments In-Person Payments within the last 30 days
8080
///
81-
private lazy var recentIPPOrdersResultsController: ResultsController<StorageOrder> = {
81+
private lazy var recentWCPayIPPResultsController: ResultsController<StorageOrder> = {
8282
let today = Date()
83-
let paymentGateway = Constants.paymentMethodID
83+
let wcpay = Constants.wcpayPaymentMethodID
8484
let thirtyDaysBeforeToday = Calendar.current.date(
8585
byAdding: .day,
8686
value: -30,
@@ -89,7 +89,7 @@ final class OrderListViewModel {
8989

9090
let predicate = NSPredicate(
9191
format: "siteID == %lld AND paymentMethodID == %@ AND datePaid >= %@",
92-
argumentArray: [siteID, paymentGateway, thirtyDaysBeforeToday]
92+
argumentArray: [siteID, wcpay, thirtyDaysBeforeToday]
9393
)
9494

9595
return ResultsController<StorageOrder>(storageManager: storageManager, matching: predicate, sortedBy: [])
@@ -271,8 +271,8 @@ final class OrderListViewModel {
271271

272272
private func fetchIPPTransactions() {
273273
do {
274-
try IPPOrdersResultsController.performFetch()
275-
try recentIPPOrdersResultsController.performFetch()
274+
try WCPayOrdersResultsController.performFetch()
275+
try recentWCPayIPPResultsController.performFetch()
276276
} catch {
277277
DDLogError("Error fetching IPP transactions: \(error)")
278278
}
@@ -282,11 +282,11 @@ final class OrderListViewModel {
282282
var campaign: FeatureAnnouncementCampaign? = nil
283283

284284
switch surveySource {
285-
case .IPP_COD:
285+
case .inPersonPaymentsCashOnDelivery:
286286
campaign = .inPersonPaymentsCashOnDelivery
287-
case .IPP_firstTransaction:
287+
case .inPersonPaymentsFirstTransaction:
288288
campaign = .inPersonPaymentsFirstTransaction
289-
case .IPP_powerUsers:
289+
case .inPersonPaymentsPowerUsers:
290290
campaign = .inPersonPaymentsPowerUsers
291291
default:
292292
break
@@ -303,29 +303,34 @@ final class OrderListViewModel {
303303
)
304304
}
305305

306-
func feedbackBannerSurveySource() -> SurveyViewController.Source? {
306+
func feedbackBannerSurveySource(onCompletion: (SurveyViewController.Source) -> Void) {
307307
if isCODEnabled && isIPPSupportedCountry {
308+
fetchIPPTransactions()
308309

309-
let hasResults = IPPOrdersResultsController.fetchedObjects.isEmpty ? false : true
310+
let hasWCPayResults = WCPayOrdersResultsController.fetchedObjects.isEmpty ? false : true
311+
let WCPayResultsCount = WCPayOrdersResultsController.fetchedObjects.count
312+
let hasOneOrMoreWCPayTransactions = (WCPayResultsCount >= 1) ? true : false
310313

311314
/// In order to filter WCPay transactions processed through IPP within the last 30 days,
312315
/// we check if these contain `receipt_url` in their metadata, unlike those processed through a website,
313316
/// which doesn't
314317
///
315-
let IPPTransactionsFound = recentIPPOrdersResultsController.fetchedObjects.filter({
318+
let recentIPPWCPayTransactionsFound = recentWCPayIPPResultsController.fetchedObjects.filter({
316319
$0.customFields.contains(where: {$0.key == Constants.receiptURLKey }) &&
317320
$0.paymentMethodTitle == Constants.paymentMethodTitle})
318-
let IPPresultsCount = IPPTransactionsFound.count
319-
320-
if !hasResults {
321-
return .IPP_COD
322-
} else if IPPresultsCount < Constants.numberOfTransactions {
323-
return .IPP_firstTransaction
324-
} else if IPPresultsCount >= Constants.numberOfTransactions {
325-
return .IPP_powerUsers
321+
let recentWCPayResultsCount = recentIPPWCPayTransactionsFound.count
322+
323+
if !hasWCPayResults {
324+
// Case 1: No WCPay transactions
325+
onCompletion(.inPersonPaymentsCashOnDelivery)
326+
} else if hasOneOrMoreWCPayTransactions && (recentWCPayResultsCount < Constants.numberOfTransactions) {
327+
// Case 2: One or more WCPay transactions, but less than 10 within latest 30 days
328+
onCompletion(.inPersonPaymentsFirstTransaction)
329+
} else if WCPayResultsCount >= Constants.numberOfTransactions {
330+
// Case 3: More than 10 WCPay transactions
331+
onCompletion(.inPersonPaymentsPowerUsers)
326332
}
327333
}
328-
return nil
329334
}
330335

331336
private func createQuery() -> FetchResultSnapshotsProvider<StorageOrder>.Query {
@@ -546,7 +551,7 @@ extension OrderListViewModel {
546551
// MARK: IPP feedback constants
547552
private extension OrderListViewModel {
548553
enum Constants {
549-
static let paymentMethodID = "woocommerce_payments"
554+
static let wcpayPaymentMethodID = "woocommerce_payments"
550555
static let paymentMethodTitle = "WooCommerce In-Person Payments"
551556
static let receiptURLKey = "receipt_url"
552557
static let numberOfTransactions = 10

WooCommerce/Classes/ViewRelated/Survey/SurveyViewController.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ extension SurveyViewController {
6868
case addOnsI1
6969
case orderCreation
7070
case couponManagement
71-
case IPP_COD
72-
case IPP_firstTransaction
73-
case IPP_powerUsers
71+
case inPersonPaymentsCashOnDelivery
72+
case inPersonPaymentsFirstTransaction
73+
case inPersonPaymentsPowerUsers
7474

7575
fileprivate var url: URL {
7676
switch self {
@@ -105,17 +105,17 @@ extension SurveyViewController {
105105
.asURL()
106106
.tagPlatform("ios")
107107
.tagAppVersion(Bundle.main.bundleVersion())
108-
case .IPP_COD:
108+
case .inPersonPaymentsCashOnDelivery:
109109
return WooConstants.URLs.inPersonPaymentsCashOnDeliveryFeedback
110110
.asURL()
111111
.tagPlatform("ios")
112112
.tagAppVersion(Bundle.main.bundleVersion())
113-
case .IPP_firstTransaction:
113+
case .inPersonPaymentsFirstTransaction:
114114
return WooConstants.URLs.inPersonPaymentsFirstTransactionFeedback
115115
.asURL()
116116
.tagPlatform("ios")
117117
.tagAppVersion(Bundle.main.bundleVersion())
118-
case .IPP_powerUsers:
118+
case .inPersonPaymentsPowerUsers:
119119
return WooConstants.URLs.inPersonPaymentsPowerUsersFeedback
120120
.asURL()
121121
.tagPlatform("ios")
@@ -125,7 +125,7 @@ extension SurveyViewController {
125125

126126
fileprivate var title: String {
127127
switch self {
128-
case .inAppFeedback, .IPP_COD, .IPP_firstTransaction, .IPP_powerUsers:
128+
case .inAppFeedback, .inPersonPaymentsCashOnDelivery, .inPersonPaymentsFirstTransaction, .inPersonPaymentsPowerUsers:
129129
return Localization.title
130130
case .productsFeedback, .shippingLabelsRelease3Feedback, .addOnsI1, .orderCreation, .couponManagement:
131131
return Localization.giveFeedback
@@ -147,11 +147,11 @@ extension SurveyViewController {
147147
return .orderCreation
148148
case .couponManagement:
149149
return .couponManagement
150-
case .IPP_COD:
150+
case .inPersonPaymentsCashOnDelivery:
151151
return .inPersonPaymentsCashOnDeliveryBanner
152-
case .IPP_firstTransaction:
152+
case .inPersonPaymentsFirstTransaction:
153153
return .inPersonPaymentsFirstTransactionBanner
154-
case .IPP_powerUsers:
154+
case .inPersonPaymentsPowerUsers:
155155
return .inPersonPaymentsPowerUsersBanner
156156
}
157157
}

WooCommerce/WooCommerceTests/ViewRelated/Orders/OrderListViewModelTests.swift

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,8 @@ final class OrderListViewModelTests: XCTestCase {
459459
XCTAssertFalse(resynchronizeRequested)
460460
}
461461

462+
// MARK: - In-Person Payments feedback banner tracks
463+
462464
func test_trackInPersonPaymentsFeedbackBannerShown_tracks_event_and_properties_correctly() {
463465
// Given
464466
let expectedEvent = WooAnalyticsStat.inPersonPaymentsBannerShown
@@ -467,7 +469,7 @@ final class OrderListViewModelTests: XCTestCase {
467469

468470
// When
469471
let viewModel = OrderListViewModel(siteID: siteID, analytics: analytics, filters: nil)
470-
viewModel.trackInPersonPaymentsFeedbackBannerShown(for: .IPP_COD)
472+
viewModel.trackInPersonPaymentsFeedbackBannerShown(for: .inPersonPaymentsCashOnDelivery)
471473

472474
// Then
473475
XCTAssertEqual(analyticsProvider.receivedEvents.first, expectedEvent.rawValue)
@@ -543,6 +545,103 @@ final class OrderListViewModelTests: XCTestCase {
543545
assertEqual(expectedRemindLater, actualProperties["remind_later"] as? Bool)
544546
}
545547

548+
// MARK: - In-Person Payments feedback banner survey
549+
550+
func test_feedbackBannerSurveySource_when_there_are_no_wcpay_orders_then_assigns_inPersonPaymentsCashOnDelivery_survey() {
551+
// Given
552+
let viewModel = OrderListViewModel(siteID: siteID, analytics: analytics, filters: nil)
553+
var expectedSurvey: SurveyViewController.Source?
554+
555+
// When
556+
let _ = insertOrder(
557+
id: 123,
558+
status: .completed,
559+
dateCreated: Date()
560+
)
561+
562+
// Confidence check
563+
XCTAssertEqual(storage.countObjects(ofType: StorageOrder.self), 1)
564+
565+
// Then
566+
viewModel.feedbackBannerSurveySource(onCompletion: { survey in
567+
expectedSurvey = survey
568+
XCTAssertEqual(expectedSurvey, .inPersonPaymentsCashOnDelivery)
569+
})
570+
}
571+
572+
func test_feedbackBannerSurveySource_when_there_is_one_wcpay_order_then_assigns_inPersonPaymentsCashOnDelivery_survey() {
573+
// Given
574+
let viewModel = OrderListViewModel(siteID: siteID, analytics: analytics, filters: nil)
575+
var expectedSurvey: SurveyViewController.Source?
576+
577+
// When
578+
let _ = insertOrder(
579+
id: 123,
580+
status: .completed,
581+
dateCreated: Date(),
582+
paymentMethodID: "woocommerce_payments"
583+
)
584+
585+
// Confidence check
586+
XCTAssertEqual(storage.countObjects(ofType: StorageOrder.self), 1)
587+
588+
// Then
589+
viewModel.feedbackBannerSurveySource(onCompletion: { survey in
590+
expectedSurvey = survey
591+
XCTAssertEqual(expectedSurvey, .inPersonPaymentsCashOnDelivery)
592+
})
593+
}
594+
595+
func test_feedbackBannerSurveySource_when_there_are_less_than_ten_wcpay_orders_then_assigns_inPersonPaymentsFirstTransaction_survey() {
596+
// Given
597+
let viewModel = OrderListViewModel(siteID: siteID, analytics: analytics, filters: nil)
598+
var expectedSurvey: SurveyViewController.Source?
599+
600+
// When
601+
let _ = (0..<9).map { orderID in
602+
insertOrder(
603+
id: orderID ,
604+
status: .completed,
605+
dateCreated: Date(),
606+
paymentMethodID: "woocommerce_payments"
607+
)
608+
}
609+
610+
// Confidence check
611+
XCTAssertEqual(storage.countObjects(ofType: StorageOrder.self), 9)
612+
613+
// Then
614+
viewModel.feedbackBannerSurveySource(onCompletion: { survey in
615+
expectedSurvey = survey
616+
XCTAssertEqual(expectedSurvey, .inPersonPaymentsFirstTransaction)
617+
})
618+
}
619+
620+
func test_feedbackBannerSurveySource_when_there_more_than_ten_wcpay_orders_then_assigns_inPersonPaymentsPowerUsers_survey() {
621+
// Given
622+
let viewModel = OrderListViewModel(siteID: siteID, analytics: analytics, filters: nil)
623+
var expectedSurvey: SurveyViewController.Source?
624+
625+
// When
626+
let _ = (0..<15).map { orderID in
627+
insertOrder(
628+
id: orderID ,
629+
status: .completed,
630+
dateCreated: Date(),
631+
paymentMethodID: "woocommerce_payments"
632+
)
633+
}
634+
635+
// Confidence check
636+
XCTAssertEqual(storage.countObjects(ofType: StorageOrder.self), 15)
637+
638+
// Then
639+
viewModel.feedbackBannerSurveySource(onCompletion: { survey in
640+
expectedSurvey = survey
641+
XCTAssertEqual(expectedSurvey, .inPersonPaymentsPowerUsers)
642+
})
643+
}
644+
546645
func test_IPPFeedbackBannerWasSubmitted_hides_banner_after_being_called() {
547646
// Given
548647
let viewModel = OrderListViewModel(siteID: siteID, filters: nil)
@@ -702,11 +801,13 @@ private extension OrderListViewModelTests {
702801

703802
func insertOrder(id orderID: Int64,
704803
status: OrderStatusEnum,
705-
dateCreated: Date = Date()) -> Yosemite.Order {
804+
dateCreated: Date = Date(),
805+
paymentMethodID: String? = nil) -> Yosemite.Order {
706806
let readonlyOrder = MockOrders().empty().copy(siteID: siteID,
707807
orderID: orderID,
708808
status: status,
709-
dateCreated: dateCreated)
809+
dateCreated: dateCreated,
810+
paymentMethodID: paymentMethodID)
710811
let storageOrder = storage.insertNewObject(ofType: StorageOrder.self)
711812
storageOrder.update(with: readonlyOrder)
712813

0 commit comments

Comments
 (0)