Skip to content

Commit f5ccde0

Browse files
authored
[Woo POS] MVP analytics: Track remaining MVP events and properties (#15151)
2 parents 7923e8e + 1bafc53 commit f5ccde0

File tree

10 files changed

+100
-27
lines changed

10 files changed

+100
-27
lines changed

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,9 +1276,10 @@ enum WooAnalyticsStat: String {
12761276
case pointOfSaleItemRemovedFromCart = "item_removed_from_cart"
12771277
case pointOfSaleCheckoutTapped = "checkout_tapped"
12781278
case pointOfSaleBackToCartTapped = "back_to_cart_tapped"
1279+
case pointOfSaleBackToCheckoutFromCashTapped = "back_to_checkout_from_cash"
12791280
case pointOfSaleClearCartTapped = "clear_cart_tapped"
1280-
case pointOfSaleExitMenuItemTapped = "exit_pos_menu_item_tapped"
1281-
case pointOfSaleExitConfirmed = "exit_pos_confirmed"
1281+
case pointOfSaleExitMenuItemTapped = "exit_menu_item_tapped"
1282+
case pointOfSaleExitConfirmed = "exit_confirmed"
12821283
case pointOfSaleGetSupportTapped = "get_support_tapped"
12831284
case pointOfSaleSimpleProductsExplanationDialogShown = "simple_products_explanation_dialog_shown"
12841285
case pointOfSaleCreateNewOrderTapped = "create_new_order_tapped"
@@ -1287,6 +1288,10 @@ enum WooAnalyticsStat: String {
12871288
case pointOfSalePaymentsOnboardingShown = "payments_onboarding_shown"
12881289
case pointOfSalePaymentsOnboardingDismissed = "payments_onboarding_dismissed"
12891290
case pointOfSaleCardReaderConnectionTapped = "card_reader_connection_tapped"
1291+
case pointOfSaleInteractionWithCustomerStarted = "interaction_with_customer_started"
1292+
case pointOfSaleViewDocsTapped = "view_docs_tapped"
1293+
case pointOfSaleReaderReadyForCardPayment = "reader_ready_for_card_payment"
1294+
case pointOfSaleCashCollectPaymentSuccess = "cash_collect_payment_success"
12901295

12911296
// MARK: Custom Fields events
12921297
case productDetailCustomFieldsTapped = "product_detail_custom_fields_tapped"

WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin
55
var connectedReaderModel: String?
66

77
private var customerInteractionStarted: Double = 0
8-
private var orderCreated: Double = 0
8+
private var orderSync: Double = 0
99
private var cardReaderReady: Double = 0
1010
private var cardReaderTapped: Double = 0
1111
private var checkoutTapCount: Int = 0
@@ -20,12 +20,12 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin
2020
func preflightResultReceived(_ result: CardReaderPreflightResult?) { }
2121
func trackProcessingCompletion(intent: Yosemite.PaymentIntent) { }
2222

23-
func trackSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) {
23+
func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) {
2424
// Property: milliseconds_since_customer_interaction_started
2525
let elapsedTimeSinceCustomerInteraction = calculateElapsedTimeInMilliseconds(since: customerInteractionStarted)
2626

27-
// Property: milliseconds_since_order_creation_success
28-
let elapsedTimeSinceOrderCreation = calculateElapsedTimeInMilliseconds(since: orderCreated)
27+
// Property: milliseconds_since_order_sync_success
28+
let elapsedTimeSinceOrderSync = calculateElapsedTimeInMilliseconds(since: orderSync)
2929

3030
// Property: milliseconds_since_reader_ready_to_collect_payment
3131
let elapsedTimeSinceCardReaderReady = calculateElapsedTimeInMilliseconds(since: cardReaderReady)
@@ -35,7 +35,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin
3535

3636
analytics.track(event: .PointOfSale.cardPresentCollectPaymentSuccess(
3737
millisecondsSinceCustomerIteractionStarted: elapsedTimeSinceCustomerInteraction,
38-
millisecondsSinceOrderCreationSuccess: elapsedTimeSinceOrderCreation,
38+
millisecondsSinceOrderSyncSuccess: elapsedTimeSinceOrderSync,
3939
millisecondsSinceReaderReadyToCollect: elapsedTimeSinceCardReaderReady,
4040
millisecondsSinceCardTapped: elapsedTimeSinceCardTapped,
4141
checkoutTapCount: checkoutTapCount
@@ -45,6 +45,15 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin
4545
resetProcessingPaymentTracking()
4646
}
4747

48+
func trackSuccessfulCashPayment() {
49+
let elapsedTimeSinceCustomerInteraction = calculateElapsedTimeInMilliseconds(since: customerInteractionStarted)
50+
51+
analytics.track(event: .PointOfSale.cashCollectPaymentSuccess(
52+
millisecondsSinceCustomerIteractionStarted: elapsedTimeSinceCustomerInteraction
53+
))
54+
resetCheckoutTapCountTracker()
55+
}
56+
4857
func trackPaymentFailure(with error: any Error) { }
4958
func trackPaymentCancelation(cancelationSource: WooAnalyticsEvent.InPersonPayments.CancellationSource) { }
5059
func trackEmailTapped() { }
@@ -56,15 +65,19 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin
5665
func trackCustomerInteractionStarted() {
5766
// Any action that is considered as user starting an iteraction resets any ongoing counter
5867
resetAllCountersOnInteractionStarted()
68+
analytics.track(.pointOfSaleInteractionWithCustomerStarted)
5969
customerInteractionStarted = Date().timeIntervalSince1970
6070
}
6171

62-
func trackOrderCreationSuccess() {
63-
orderCreated = trackCurrentTime()
72+
func trackOrderSyncSuccess() {
73+
orderSync = trackCurrentTime()
6474
}
6575

6676
func trackCardReaderReady() {
6777
cardReaderReady = trackCurrentTime()
78+
79+
// As a side effect of knowing when the reader is ready, we track the elapsed from order sync (created or updated)
80+
trackElapsedTimeFromOrderSyncToCardReady()
6881
}
6982

7083
// The Stripe SDK returns multiple `.processing` events, but we want to capture the first one in the stream only.
@@ -83,6 +96,11 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin
8396
func resetCheckoutTapCountTracker() {
8497
checkoutTapCount = 0
8598
}
99+
100+
private func trackElapsedTimeFromOrderSyncToCardReady() {
101+
let elapsedTime = cardReaderReady - orderSync
102+
analytics.track(event: .PointOfSale.cardReaderReadyForCardPayment(waitingTime: elapsedTime))
103+
}
86104
}
87105

88106
// Helpers
@@ -101,7 +119,7 @@ private extension POSCollectOrderPaymentAnalytics {
101119
}
102120

103121
private func resetAllCountersOnInteractionStarted() {
104-
orderCreated = 0
122+
orderSync = 0
105123
cardReaderReady = 0
106124
cardReaderTapped = 0
107125
resetCheckoutTapCountTracker()
@@ -113,9 +131,10 @@ private extension POSCollectOrderPaymentAnalytics {
113131
// https://github.com/woocommerce/woocommerce-ios/issues/15149
114132
extension CollectOrderPaymentAnalytics {
115133
func trackCustomerInteractionStarted() { }
116-
func trackOrderCreationSuccess() { }
134+
func trackOrderSyncSuccess() { }
117135
func trackCardReaderReady() { }
118136
func trackCardReaderTapped() { }
119137
func trackCheckoutTapped() { }
120138
func resetCheckoutTapCountTracker() { }
139+
func trackSuccessfulCashPayment() { }
121140
}

WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ extension WooAnalyticsEvent {
1313
static let itemType = "product_type"
1414
static let itemsInCart = "items_in_cart"
1515
static let millisecondsSinceCustomerInteractionStarted = "milliseconds_since_customer_interaction_started"
16-
static let millisecondsSinceOrderCreationSuccess = "milliseconds_since_order_creation_success"
16+
static let millisecondsSinceOrderSyncSuccess = "milliseconds_since_order_sync_success"
1717
static let millisecondsSinceReaderReadyToCollect = "milliseconds_since_reader_ready_to_collect_payment"
1818
static let millisecondsSinceCardTapped = "milliseconds_since_card_tapped"
1919
static let checkoutTapCount = "checkout_tap_count"
20+
static let waitingTime = "waiting_time"
2021
}
2122

2223
static func paymentsOnboardingShown() -> WooAnalyticsEvent {
@@ -37,19 +38,32 @@ extension WooAnalyticsEvent {
3738
properties: [Key.itemsInCart: itemsInCart])
3839
}
3940

41+
/// Tracks the time elapsed preparing reader for payment, after successful order creation
42+
/// - Parameter waitingTime: Elapsed time from Order creation to card ready for payment
43+
///
44+
static func cardReaderReadyForCardPayment(waitingTime: Double) -> WooAnalyticsEvent {
45+
WooAnalyticsEvent(statName: .pointOfSaleReaderReadyForCardPayment, properties: [Key.waitingTime: "\(waitingTime)"])
46+
}
47+
4048
static func cardPresentCollectPaymentSuccess(millisecondsSinceCustomerIteractionStarted: Double,
41-
millisecondsSinceOrderCreationSuccess: Double,
49+
millisecondsSinceOrderSyncSuccess: Double,
4250
millisecondsSinceReaderReadyToCollect: Double,
4351
millisecondsSinceCardTapped: Double,
4452
checkoutTapCount: Int) -> WooAnalyticsEvent {
4553
WooAnalyticsEvent(statName: .collectPaymentSuccess, properties: [
4654
Key.millisecondsSinceCustomerInteractionStarted: "\(millisecondsSinceCustomerIteractionStarted)",
47-
Key.millisecondsSinceOrderCreationSuccess: "\(millisecondsSinceOrderCreationSuccess)",
55+
Key.millisecondsSinceOrderSyncSuccess: "\(millisecondsSinceOrderSyncSuccess)",
4856
Key.millisecondsSinceReaderReadyToCollect: "\(millisecondsSinceReaderReadyToCollect)",
4957
Key.millisecondsSinceCardTapped: "\(millisecondsSinceCardTapped)",
5058
Key.checkoutTapCount: "\(checkoutTapCount)"
5159
])
5260
}
61+
62+
static func cashCollectPaymentSuccess(millisecondsSinceCustomerIteractionStarted: Double) -> WooAnalyticsEvent {
63+
WooAnalyticsEvent(statName: .pointOfSaleCashCollectPaymentSuccess, properties: [
64+
Key.millisecondsSinceCustomerInteractionStarted: "\(millisecondsSinceCustomerIteractionStarted)",
65+
])
66+
}
5367
}
5468
}
5569

WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,22 @@ private extension PointOfSaleAggregateModel {
165165
collectOrderPaymentAnalyticsTracker.trackCustomerInteractionStarted()
166166
}
167167
}
168+
169+
// Tracks when the order is created or updated successfully
170+
// pdfdoF-6hn#comment-7625-p2
171+
func trackOrderSyncState(_ result: Result<SyncOrderState, Error>) {
172+
switch result {
173+
case .success(let syncState):
174+
switch syncState {
175+
case .newOrder, .orderUpdated:
176+
collectOrderPaymentAnalyticsTracker.trackOrderSyncSuccess()
177+
default:
178+
break
179+
}
180+
case .failure:
181+
break
182+
}
183+
}
168184
}
169185

170186
// MARK: - Card payments
@@ -242,6 +258,7 @@ extension PointOfSaleAggregateModel {
242258

243259
@MainActor
244260
func cancelCashPayment() async {
261+
analytics.track(.pointOfSaleBackToCheckoutFromCashTapped)
245262
paymentState = .card(.idle)
246263
if case .connected = cardReaderConnectionStatus {
247264
await collectCardPayment()
@@ -250,8 +267,7 @@ extension PointOfSaleAggregateModel {
250267

251268
private func cashPaymentSuccess() {
252269
paymentState = .cash(.paymentSuccess)
253-
// TODO: Move to trackSuccessfulCashPayment() on #15151
254-
collectOrderPaymentAnalyticsTracker.resetCheckoutTapCountTracker()
270+
collectOrderPaymentAnalyticsTracker.trackSuccessfulCashPayment()
255271
}
256272

257273
@MainActor
@@ -453,9 +469,7 @@ extension PointOfSaleAggregateModel {
453469
let syncOrderResult = await orderController.syncOrder(for: cart, retryHandler: { [weak self] in
454470
await self?.checkOut()
455471
})
456-
if case .success(.newOrder) = syncOrderResult {
457-
collectOrderPaymentAnalyticsTracker.trackOrderCreationSuccess()
458-
}
472+
trackOrderSyncState(syncOrderResult)
459473
await startPaymentWhenCardReaderConnected()
460474
}
461475
}

WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct POSFloatingControlView: View {
4040
}
4141
Button {
4242
showDocumentation = true
43+
ServiceLocator.analytics.track(.pointOfSaleViewDocsTapped)
4344
} label: {
4445
Label(
4546
title: { Text(Localization.viewDocumentation) },

WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ protocol CollectOrderPaymentAnalyticsTracking {
99

1010
func trackProcessingCompletion(intent: PaymentIntent)
1111

12-
func trackSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData)
12+
func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData)
1313

1414
func trackPaymentFailure(with error: Error)
1515

@@ -26,11 +26,12 @@ protocol CollectOrderPaymentAnalyticsTracking {
2626
func trackReceiptPrintFailed(error: Error)
2727

2828
func trackCustomerInteractionStarted()
29-
func trackOrderCreationSuccess()
29+
func trackOrderSyncSuccess()
3030
func trackCardReaderReady()
3131
func trackCardReaderTapped()
3232
func trackCheckoutTapped()
3333
func resetCheckoutTapCountTracker()
34+
func trackSuccessfulCashPayment()
3435
}
3536

3637
final class CollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTracking {
@@ -89,7 +90,7 @@ final class CollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTracking {
8990
}
9091
}
9192

92-
func trackSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) {
93+
func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) {
9394
analytics.track(event: WooAnalyticsEvent.InPersonPayments
9495
.collectPaymentSuccess(forGatewayID: paymentGatewayAccount?.gatewayID,
9596
countryCode: configuration.countryCode,

WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ private extension CollectOrderPaymentUseCase {
377377
/// Tracks the successful payments
378378
///
379379
func handleSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) {
380-
analyticsTracker.trackSuccessfulPayment(capturedPaymentData: capturedPaymentData)
380+
analyticsTracker.trackSuccessfulCardPayment(capturedPaymentData: capturedPaymentData)
381381
}
382382

383383
func handlePaymentCancellation(from cancellationSource: WooAnalyticsEvent.InPersonPayments.CancellationSource) {

WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ final class MockCollectOrderPaymentAnalyticsTracker: CollectOrderPaymentAnalytic
1515

1616
var didCallTrackSuccessfulPayment = false
1717
var spyTrackSuccessfulPaymentCapturedPaymentData: CardPresentCapturedPaymentData? = nil
18-
func trackSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) {
18+
func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) {
1919
didCallTrackSuccessfulPayment = true
2020
spyTrackSuccessfulPaymentCapturedPaymentData = capturedPaymentData
2121
}
@@ -58,7 +58,7 @@ final class MockCollectOrderPaymentAnalyticsTracker: CollectOrderPaymentAnalytic
5858
// no-op
5959
}
6060

61-
func trackOrderCreationSuccess() {
61+
func trackOrderSyncSuccess() {
6262
// no-op
6363
}
6464

@@ -78,4 +78,8 @@ final class MockCollectOrderPaymentAnalyticsTracker: CollectOrderPaymentAnalytic
7878
func resetCheckoutTapCountTracker() {
7979
// no-op
8080
}
81+
82+
func trackSuccessfulCashPayment() {
83+
// no-op
84+
}
8185
}

WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ struct POSCollectOrderPaymentAnalyticsTests {
1717
let capturedPaymentData = CardPresentCapturedPaymentData(paymentMethod: .cardPresent(details: .fake()), receiptParameters: nil)
1818
let expectedEvent = "card_present_collect_payment_success"
1919
let expectedProperties = [
20-
"milliseconds_since_order_creation_success",
20+
"milliseconds_since_order_sync_success",
2121
"milliseconds_since_reader_ready_to_collect_payment",
2222
"milliseconds_since_card_tapped",
2323
"milliseconds_since_customer_interaction_started",
2424
"checkout_tap_count"
2525
]
2626

2727
// When
28-
sut.trackSuccessfulPayment(capturedPaymentData: capturedPaymentData)
28+
sut.trackSuccessfulCardPayment(capturedPaymentData: capturedPaymentData)
2929

3030
// Then
3131
#expect(analyticsProvider.receivedEvents.first(where: { $0 == expectedEvent }) != nil)

WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,21 @@ struct PointOfSaleAggregateModelTests {
810810
// Then
811811
#expect(analyticsTracker.didCallTrackCheckoutTapped == true)
812812
}
813+
814+
@available(iOS 17.0, *)
815+
@Test func cancelCashPayment_when_invoked_then_tracks_expected_event() async throws {
816+
// Given
817+
let analyticsTracker = MockCollectOrderPaymentAnalyticsTracker()
818+
let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(),
819+
cardPresentPaymentService: MockCardPresentPaymentService(),
820+
orderController: MockPointOfSaleOrderController(),
821+
collectOrderPaymentAnalyticsTracker: analyticsTracker)
822+
// When
823+
await sut.cancelCashPayment()
824+
825+
// Then
826+
#expect(analyticsProvider.receivedEvents.first(where: { $0 == "back_to_checkout_from_cash" }) != nil)
827+
}
813828
}
814829
}
815830

0 commit comments

Comments
 (0)