From cf75a9554709ddedca8a0543b33b492f0f790afe Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Fri, 14 Feb 2025 14:40:12 +0100 Subject: [PATCH 01/14] update exit event name --- WooCommerce/Classes/Analytics/WooAnalyticsStat.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index 843ce229a5b..68e88dfc5a4 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -1276,8 +1276,8 @@ enum WooAnalyticsStat: String { case pointOfSaleCheckoutTapped = "checkout_tapped" case pointOfSaleBackToCartTapped = "back_to_cart_tapped" case pointOfSaleClearCartTapped = "clear_cart_tapped" - case pointOfSaleExitMenuItemTapped = "exit_pos_menu_item_tapped" - case pointOfSaleExitConfirmed = "exit_pos_confirmed" + case pointOfSaleExitMenuItemTapped = "exit_menu_item_tapped" + case pointOfSaleExitConfirmed = "exit_confirmed" case pointOfSaleGetSupportTapped = "get_support_tapped" case pointOfSaleSimpleProductsExplanationDialogShown = "simple_products_explanation_dialog_shown" case pointOfSaleCreateNewOrderTapped = "create_new_order_tapped" From 3de830dde00de4980d7d14c1ece35b4ae76d1002 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Fri, 14 Feb 2025 14:50:36 +0100 Subject: [PATCH 02/14] Add back_to_checkout_from_cash event --- WooCommerce/Classes/Analytics/WooAnalyticsStat.swift | 1 + .../Classes/POS/Presentation/PointOfSaleCollectCashView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index 68e88dfc5a4..13b65331c8a 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -1275,6 +1275,7 @@ enum WooAnalyticsStat: String { case pointOfSaleItemRemovedFromCart = "item_removed_from_cart" case pointOfSaleCheckoutTapped = "checkout_tapped" case pointOfSaleBackToCartTapped = "back_to_cart_tapped" + case pointOfSaleBackToCheckoutFromCashTapped = "back_to_checkout_from_cash" case pointOfSaleClearCartTapped = "clear_cart_tapped" case pointOfSaleExitMenuItemTapped = "exit_menu_item_tapped" case pointOfSaleExitConfirmed = "exit_confirmed" diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift index ca80f5050d5..ba191a89a01 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift @@ -32,6 +32,7 @@ struct PointOfSaleCollectCashView: View { Button(action: { Task { @MainActor in await posModel.cancelCashPayment() + ServiceLocator.analytics.track(.pointOfSaleBackToCheckoutFromCashTapped) isTextFieldFocused = false } }, label: { From ee9c9d6d1e0fe2cb7c0c981b7febf237bfe1e9de Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Fri, 14 Feb 2025 14:55:58 +0100 Subject: [PATCH 03/14] add interaction_with_customer_started event --- WooCommerce/Classes/Analytics/WooAnalyticsStat.swift | 1 + .../Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index 13b65331c8a..1018e0a4140 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -1287,6 +1287,7 @@ enum WooAnalyticsStat: String { case pointOfSalePaymentsOnboardingShown = "payments_onboarding_shown" case pointOfSalePaymentsOnboardingDismissed = "payments_onboarding_dismissed" case pointOfSaleCardReaderConnectionTapped = "card_reader_connection_tapped" + case pointOfSaleInteractionWithCustomerStarted = "interaction_with_customer_started" // MARK: Custom Fields events case productDetailCustomFieldsTapped = "product_detail_custom_fields_tapped" diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index 23bb24fa391..a7b16724dbd 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -43,6 +43,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin func trackReceiptPrintFailed(error: any Error) { } func trackCustomerInteractionStarted() { + ServiceLocator.analytics.track(.pointOfSaleInteractionWithCustomerStarted) customerInteractionStarted = Date().timeIntervalSince1970 } From ae94644c903d3b547a36f0213774663597bd9db9 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Mon, 17 Feb 2025 09:07:31 +0100 Subject: [PATCH 04/14] Add view_docs_tapped event --- WooCommerce/Classes/Analytics/WooAnalyticsStat.swift | 1 + .../Classes/POS/Presentation/POSFloatingControlView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index 1018e0a4140..68fa9e7023a 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -1288,6 +1288,7 @@ enum WooAnalyticsStat: String { case pointOfSalePaymentsOnboardingDismissed = "payments_onboarding_dismissed" case pointOfSaleCardReaderConnectionTapped = "card_reader_connection_tapped" case pointOfSaleInteractionWithCustomerStarted = "interaction_with_customer_started" + case pointOfSaleViewDocsTapped = "view_docs_tapped" // MARK: Custom Fields events case productDetailCustomFieldsTapped = "product_detail_custom_fields_tapped" diff --git a/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift b/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift index 385a6a30326..132d20d56d6 100644 --- a/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift +++ b/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift @@ -40,6 +40,7 @@ struct POSFloatingControlView: View { } Button { showDocumentation = true + ServiceLocator.analytics.track(.pointOfSaleViewDocsTapped) } label: { Label( title: { Text(Localization.viewDocumentation) }, From ac0163da812f11119256c2f99963f6af4b147921 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Mon, 17 Feb 2025 09:56:44 +0100 Subject: [PATCH 05/14] Track reader_ready_for_card_payment --- WooCommerce/Classes/Analytics/WooAnalyticsStat.swift | 1 + .../POS/Analytics/POSCollectOrderPaymentAnalytics.swift | 8 ++++++++ .../POS/Analytics/WooAnalyticsEvent+PointOfSale.swift | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index 68fa9e7023a..8a5a1415fef 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -1289,6 +1289,7 @@ enum WooAnalyticsStat: String { case pointOfSaleCardReaderConnectionTapped = "card_reader_connection_tapped" case pointOfSaleInteractionWithCustomerStarted = "interaction_with_customer_started" case pointOfSaleViewDocsTapped = "view_docs_tapped" + case pointOfSaleReaderReadyForCardPayment = "reader_ready_for_card_payment" // MARK: Custom Fields events case productDetailCustomFieldsTapped = "product_detail_custom_fields_tapped" diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index a7b16724dbd..46e381a8eb9 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -53,6 +53,9 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin func trackCardReaderReady() { cardReaderReady = trackCurrentTime() + + // As a side effect of knowing when the reader is ready, we track the elapsed from order creation + trackElapsedTimeFromOrderCreationToCardReady() } func trackCardReaderTapped() { @@ -66,6 +69,11 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin func resetCheckoutTapCountTracker() { checkoutTapCount = 0 } + + private func trackElapsedTimeFromOrderCreationToCardReady() { + let elapsedTime = cardReaderReady - orderCreated + ServiceLocator.analytics.track(event: .PointOfSale.cardReaderReadyForCardPayment(waitingTime: elapsedTime)) + } } // Helpers diff --git a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift index 218e273db8f..536be06f7dd 100644 --- a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift +++ b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift @@ -17,6 +17,7 @@ extension WooAnalyticsEvent { static let millisecondsSinceReaderReadyToCollect = "milliseconds_since_reader_ready_to_collect_payment" static let millisecondsSinceCardTapped = "milliseconds_since_card_tapped" static let checkoutTapCount = "checkout_tap_count" + static let waitingTime = "waiting_time" } static func paymentsOnboardingShown() -> WooAnalyticsEvent { @@ -36,6 +37,13 @@ extension WooAnalyticsEvent { WooAnalyticsEvent(statName: .pointOfSaleCheckoutTapped, properties: [Key.itemsInCart: itemsInCart]) } + + /// Tracks the time elapsed preparing reader for payment, after successful order creation + /// - Parameter waitingTime: Elapsed time from Order creation to card ready for payment + /// + static func cardReaderReadyForCardPayment(waitingTime: Double) -> WooAnalyticsEvent { + WooAnalyticsEvent(statName: .pointOfSaleReaderReadyForCardPayment, properties: [Key.waitingTime: "\(waitingTime)"]) + } static func cardPresentCollectPaymentSuccess(millisecondsSinceCustomerIteractionStarted: Double, millisecondsSinceOrderCreationSuccess: Double, From be267726b6b3a965b807cf1bc44928934a78e48c Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Mon, 17 Feb 2025 10:06:37 +0100 Subject: [PATCH 06/14] Add cash_collect_payment_success --- WooCommerce/Classes/Analytics/WooAnalyticsStat.swift | 1 + .../POS/Analytics/POSCollectOrderPaymentAnalytics.swift | 9 +++++++++ .../POS/Analytics/WooAnalyticsEvent+PointOfSale.swift | 6 ++++++ .../Classes/POS/Models/PointOfSaleAggregateModel.swift | 1 + .../Collect Payments/CollectOrderPaymentAnalytics.swift | 1 + 5 files changed, 18 insertions(+) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index 8a5a1415fef..dfd323fd751 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -1290,6 +1290,7 @@ enum WooAnalyticsStat: String { case pointOfSaleInteractionWithCustomerStarted = "interaction_with_customer_started" case pointOfSaleViewDocsTapped = "view_docs_tapped" case pointOfSaleReaderReadyForCardPayment = "reader_ready_for_card_payment" + case pointOfSaleCashCollectPaymentSuccess = "cash_collect_payment_success" // MARK: Custom Fields events case productDetailCustomFieldsTapped = "product_detail_custom_fields_tapped" diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index 46e381a8eb9..f3d3d8d5742 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -33,6 +33,14 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin checkoutTapCount: checkoutTapCount )) } + + func trackSuccessfulCashPayment() { + let elapsedTimeSinceCustomerInteraction = calculateElapsedTimeInMilliseconds(since: customerInteractionStarted) + + ServiceLocator.analytics.track(event: .PointOfSale.cashCollectPaymentSuccess( + millisecondsSinceCustomerIteractionStarted: elapsedTimeSinceCustomerInteraction + )) + } func trackPaymentFailure(with error: any Error) { } func trackPaymentCancelation(cancelationSource: WooAnalyticsEvent.InPersonPayments.CancellationSource) { } @@ -97,4 +105,5 @@ extension CollectOrderPaymentAnalytics { func trackCardReaderTapped() { } func trackCheckoutTapped() { } func resetCheckoutTapCountTracker() { } + func trackSuccessfulCashPayment() { } } diff --git a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift index 536be06f7dd..9adc33a8a31 100644 --- a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift +++ b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift @@ -58,6 +58,12 @@ extension WooAnalyticsEvent { Key.checkoutTapCount: "\(checkoutTapCount)" ]) } + + static func cashCollectPaymentSuccess(millisecondsSinceCustomerIteractionStarted: Double) -> WooAnalyticsEvent { + WooAnalyticsEvent(statName: .pointOfSaleCashCollectPaymentSuccess, properties: [ + Key.millisecondsSinceCustomerInteractionStarted: "\(millisecondsSinceCustomerIteractionStarted)", + ]) + } } } diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index 93a260a770c..2ca8556d067 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -251,6 +251,7 @@ extension PointOfSaleAggregateModel { private func cashPaymentSuccess() { paymentState = .cash(.paymentSuccess) + collectOrderPaymentAnalyticsTracker.trackSuccessfulCashPayment() collectOrderPaymentAnalyticsTracker.resetCheckoutTapCountTracker() } diff --git a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift index 70d39ecdcaf..ab562e7ae71 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift @@ -31,6 +31,7 @@ protocol CollectOrderPaymentAnalyticsTracking { func trackCardReaderTapped() func trackCheckoutTapped() func resetCheckoutTapCountTracker() + func trackSuccessfulCashPayment() } final class CollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTracking { From 5ea0e84209229df62a35276d0818eea21f204ca2 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Mon, 17 Feb 2025 10:07:21 +0100 Subject: [PATCH 07/14] lint --- .../POS/Analytics/POSCollectOrderPaymentAnalytics.swift | 4 ++-- .../Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index f3d3d8d5742..6540e75a034 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -33,10 +33,10 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin checkoutTapCount: checkoutTapCount )) } - + func trackSuccessfulCashPayment() { let elapsedTimeSinceCustomerInteraction = calculateElapsedTimeInMilliseconds(since: customerInteractionStarted) - + ServiceLocator.analytics.track(event: .PointOfSale.cashCollectPaymentSuccess( millisecondsSinceCustomerIteractionStarted: elapsedTimeSinceCustomerInteraction )) diff --git a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift index 9adc33a8a31..c60e2590027 100644 --- a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift +++ b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift @@ -37,7 +37,7 @@ extension WooAnalyticsEvent { WooAnalyticsEvent(statName: .pointOfSaleCheckoutTapped, properties: [Key.itemsInCart: itemsInCart]) } - + /// Tracks the time elapsed preparing reader for payment, after successful order creation /// - Parameter waitingTime: Elapsed time from Order creation to card ready for payment /// From 9ab83fe9c1eded744ad00db12ac1314cedc7a4fd Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Mon, 17 Feb 2025 10:13:51 +0100 Subject: [PATCH 08/14] make test target compile --- .../Mocks/MockCollectOrderPaymentAnalyticsTracker.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift b/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift index 9debf5dd850..d2d0c8904aa 100644 --- a/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift +++ b/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift @@ -77,4 +77,8 @@ final class MockCollectOrderPaymentAnalyticsTracker: CollectOrderPaymentAnalytic func resetCheckoutTapCountTracker() { // no-op } + + func trackSuccessfulCashPayment() { + // no-op + } } From 11823d69e9919bffe097209023eac1de0e1bed26 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Mon, 17 Feb 2025 14:04:25 +0100 Subject: [PATCH 09/14] move event to posmodel & unit test --- .../POS/Models/PointOfSaleAggregateModel.swift | 1 + .../Presentation/PointOfSaleCollectCashView.swift | 1 - .../Models/PointOfSaleAggregateModelTests.swift | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index 2ca8556d067..451e47e5083 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -243,6 +243,7 @@ extension PointOfSaleAggregateModel { @MainActor func cancelCashPayment() async { + analytics.track(.pointOfSaleBackToCheckoutFromCashTapped) paymentState = .card(.idle) if case .connected = cardReaderConnectionStatus { await collectCardPayment() diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift index ba191a89a01..ca80f5050d5 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift @@ -32,7 +32,6 @@ struct PointOfSaleCollectCashView: View { Button(action: { Task { @MainActor in await posModel.cancelCashPayment() - ServiceLocator.analytics.track(.pointOfSaleBackToCheckoutFromCashTapped) isTextFieldFocused = false } }, label: { diff --git a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift index c61799796d0..bee9b2e27da 100644 --- a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift @@ -810,6 +810,21 @@ struct PointOfSaleAggregateModelTests { // Then #expect(analyticsTracker.didCallTrackCheckoutTapped == true) } + + @available(iOS 17.0, *) + @Test func cancelCashPayment_when_invoked_then_tracks_expected_event() async throws { + // Given + let analyticsTracker = MockCollectOrderPaymentAnalyticsTracker() + let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + cardPresentPaymentService: MockCardPresentPaymentService(), + orderController: MockPointOfSaleOrderController(), + collectOrderPaymentAnalyticsTracker: analyticsTracker) + // When + await sut.cancelCashPayment() + + // Then + #expect(analyticsProvider.receivedEvents.first(where: { $0 == "back_to_checkout_from_cash" }) != nil) + } } } From ceee9b2f0f51b447052c4e5240ec1fa7500b1fd0 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Mon, 17 Feb 2025 14:05:21 +0100 Subject: [PATCH 10/14] remove unnecessary reference --- .../Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index e46e86602dd..5a3c791c440 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -87,7 +87,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin private func trackElapsedTimeFromOrderCreationToCardReady() { let elapsedTime = cardReaderReady - orderCreated - ServiceLocator.analytics.track(event: .PointOfSale.cardReaderReadyForCardPayment(waitingTime: elapsedTime)) + analytics.track(event: .PointOfSale.cardReaderReadyForCardPayment(waitingTime: elapsedTime)) } } From 28cac57bab73bf9066b506cad27cdd02d2685e0c Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Mon, 17 Feb 2025 14:16:26 +0100 Subject: [PATCH 11/14] remove additional unnecessary references to servicelocator --- .../POS/Analytics/POSCollectOrderPaymentAnalytics.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index 5a3c791c440..83b5c49a779 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -44,7 +44,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin func trackSuccessfulCashPayment() { let elapsedTimeSinceCustomerInteraction = calculateElapsedTimeInMilliseconds(since: customerInteractionStarted) - ServiceLocator.analytics.track(event: .PointOfSale.cashCollectPaymentSuccess( + analytics.track(event: .PointOfSale.cashCollectPaymentSuccess( millisecondsSinceCustomerIteractionStarted: elapsedTimeSinceCustomerInteraction )) } @@ -58,7 +58,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin func trackReceiptPrintFailed(error: any Error) { } func trackCustomerInteractionStarted() { - ServiceLocator.analytics.track(.pointOfSaleInteractionWithCustomerStarted) + analytics.track(.pointOfSaleInteractionWithCustomerStarted) customerInteractionStarted = Date().timeIntervalSince1970 } From 84c1df29697f74846535ba7d25d4a5b0402bd6d7 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Tue, 18 Feb 2025 14:22:44 +0100 Subject: [PATCH 12/14] Move reset tap counter to analytics tracker --- .../Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift | 1 + WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index 18b44983b5b..a416fc8d1f2 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -51,6 +51,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin analytics.track(event: .PointOfSale.cashCollectPaymentSuccess( millisecondsSinceCustomerIteractionStarted: elapsedTimeSinceCustomerInteraction )) + resetCheckoutTapCountTracker() } func trackPaymentFailure(with error: any Error) { } diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index e46ac27e0c9..173d8cdd115 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -252,8 +252,6 @@ extension PointOfSaleAggregateModel { private func cashPaymentSuccess() { paymentState = .cash(.paymentSuccess) collectOrderPaymentAnalyticsTracker.trackSuccessfulCashPayment() - // TODO: Move to trackSuccessfulCashPayment() on #15151 - collectOrderPaymentAnalyticsTracker.resetCheckoutTapCountTracker() } @MainActor From 7a7492badeb6b452e3d59baffdf28e782f29cbf8 Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Tue, 18 Feb 2025 14:23:00 +0100 Subject: [PATCH 13/14] rename --- .../POS/Analytics/POSCollectOrderPaymentAnalytics.swift | 2 +- .../Collect Payments/CollectOrderPaymentAnalytics.swift | 4 ++-- .../Orders/Collect Payments/CollectOrderPaymentUseCase.swift | 2 +- .../Mocks/MockCollectOrderPaymentAnalyticsTracker.swift | 2 +- .../POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index a416fc8d1f2..b6e0c8ff6aa 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -20,7 +20,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin func preflightResultReceived(_ result: CardReaderPreflightResult?) { } func trackProcessingCompletion(intent: Yosemite.PaymentIntent) { } - func trackSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) { + func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) { // Property: milliseconds_since_customer_interaction_started let elapsedTimeSinceCustomerInteraction = calculateElapsedTimeInMilliseconds(since: customerInteractionStarted) diff --git a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift index ab562e7ae71..be9d61425d2 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift @@ -9,7 +9,7 @@ protocol CollectOrderPaymentAnalyticsTracking { func trackProcessingCompletion(intent: PaymentIntent) - func trackSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) + func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) func trackPaymentFailure(with error: Error) @@ -90,7 +90,7 @@ final class CollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTracking { } } - func trackSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) { + func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) { analytics.track(event: WooAnalyticsEvent.InPersonPayments .collectPaymentSuccess(forGatewayID: paymentGatewayAccount?.gatewayID, countryCode: configuration.countryCode, diff --git a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift index 714382b25c5..076a9bf9c37 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentUseCase.swift @@ -377,7 +377,7 @@ private extension CollectOrderPaymentUseCase { /// Tracks the successful payments /// func handleSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) { - analyticsTracker.trackSuccessfulPayment(capturedPaymentData: capturedPaymentData) + analyticsTracker.trackSuccessfulCardPayment(capturedPaymentData: capturedPaymentData) } func handlePaymentCancellation(from cancellationSource: WooAnalyticsEvent.InPersonPayments.CancellationSource) { diff --git a/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift b/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift index 3312580071c..8032171ffa7 100644 --- a/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift +++ b/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift @@ -15,7 +15,7 @@ final class MockCollectOrderPaymentAnalyticsTracker: CollectOrderPaymentAnalytic var didCallTrackSuccessfulPayment = false var spyTrackSuccessfulPaymentCapturedPaymentData: CardPresentCapturedPaymentData? = nil - func trackSuccessfulPayment(capturedPaymentData: CardPresentCapturedPaymentData) { + func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) { didCallTrackSuccessfulPayment = true spyTrackSuccessfulPaymentCapturedPaymentData = capturedPaymentData } diff --git a/WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift b/WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift index 7003cf71f75..a2e64fda77c 100644 --- a/WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift @@ -25,7 +25,7 @@ struct POSCollectOrderPaymentAnalyticsTests { ] // When - sut.trackSuccessfulPayment(capturedPaymentData: capturedPaymentData) + sut.trackSuccessfulCardPayment(capturedPaymentData: capturedPaymentData) // Then #expect(analyticsProvider.receivedEvents.first(where: { $0 == expectedEvent }) != nil) From 1bafc533e9328480aef9d1c16dfb08c28f0e5ecc Mon Sep 17 00:00:00 2001 From: iamgabrielma Date: Wed, 19 Feb 2025 09:26:16 +0100 Subject: [PATCH 14/14] Updated milliseconds_since_order_sync_success We track this property when the order is created or updated --- .../POSCollectOrderPaymentAnalytics.swift | 24 +++++++++---------- .../WooAnalyticsEvent+PointOfSale.swift | 6 ++--- .../Models/PointOfSaleAggregateModel.swift | 20 +++++++++++++--- .../CollectOrderPaymentAnalytics.swift | 2 +- ...kCollectOrderPaymentAnalyticsTracker.swift | 2 +- ...POSCollectOrderPaymentAnalyticsTests.swift | 2 +- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift index b6e0c8ff6aa..fdfc51a0a62 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalytics.swift @@ -5,7 +5,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin var connectedReaderModel: String? private var customerInteractionStarted: Double = 0 - private var orderCreated: Double = 0 + private var orderSync: Double = 0 private var cardReaderReady: Double = 0 private var cardReaderTapped: Double = 0 private var checkoutTapCount: Int = 0 @@ -24,8 +24,8 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin // Property: milliseconds_since_customer_interaction_started let elapsedTimeSinceCustomerInteraction = calculateElapsedTimeInMilliseconds(since: customerInteractionStarted) - // Property: milliseconds_since_order_creation_success - let elapsedTimeSinceOrderCreation = calculateElapsedTimeInMilliseconds(since: orderCreated) + // Property: milliseconds_since_order_sync_success + let elapsedTimeSinceOrderSync = calculateElapsedTimeInMilliseconds(since: orderSync) // Property: milliseconds_since_reader_ready_to_collect_payment let elapsedTimeSinceCardReaderReady = calculateElapsedTimeInMilliseconds(since: cardReaderReady) @@ -35,7 +35,7 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin analytics.track(event: .PointOfSale.cardPresentCollectPaymentSuccess( millisecondsSinceCustomerIteractionStarted: elapsedTimeSinceCustomerInteraction, - millisecondsSinceOrderCreationSuccess: elapsedTimeSinceOrderCreation, + millisecondsSinceOrderSyncSuccess: elapsedTimeSinceOrderSync, millisecondsSinceReaderReadyToCollect: elapsedTimeSinceCardReaderReady, millisecondsSinceCardTapped: elapsedTimeSinceCardTapped, checkoutTapCount: checkoutTapCount @@ -69,15 +69,15 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin customerInteractionStarted = Date().timeIntervalSince1970 } - func trackOrderCreationSuccess() { - orderCreated = trackCurrentTime() + func trackOrderSyncSuccess() { + orderSync = trackCurrentTime() } func trackCardReaderReady() { cardReaderReady = trackCurrentTime() - // As a side effect of knowing when the reader is ready, we track the elapsed from order creation - trackElapsedTimeFromOrderCreationToCardReady() + // As a side effect of knowing when the reader is ready, we track the elapsed from order sync (created or updated) + trackElapsedTimeFromOrderSyncToCardReady() } // The Stripe SDK returns multiple `.processing` events, but we want to capture the first one in the stream only. @@ -97,8 +97,8 @@ final class POSCollectOrderPaymentAnalytics: CollectOrderPaymentAnalyticsTrackin checkoutTapCount = 0 } - private func trackElapsedTimeFromOrderCreationToCardReady() { - let elapsedTime = cardReaderReady - orderCreated + private func trackElapsedTimeFromOrderSyncToCardReady() { + let elapsedTime = cardReaderReady - orderSync analytics.track(event: .PointOfSale.cardReaderReadyForCardPayment(waitingTime: elapsedTime)) } } @@ -119,7 +119,7 @@ private extension POSCollectOrderPaymentAnalytics { } private func resetAllCountersOnInteractionStarted() { - orderCreated = 0 + orderSync = 0 cardReaderReady = 0 cardReaderTapped = 0 resetCheckoutTapCountTracker() @@ -131,7 +131,7 @@ private extension POSCollectOrderPaymentAnalytics { // https://github.com/woocommerce/woocommerce-ios/issues/15149 extension CollectOrderPaymentAnalytics { func trackCustomerInteractionStarted() { } - func trackOrderCreationSuccess() { } + func trackOrderSyncSuccess() { } func trackCardReaderReady() { } func trackCardReaderTapped() { } func trackCheckoutTapped() { } diff --git a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift index c60e2590027..f6db030e25a 100644 --- a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift +++ b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift @@ -13,7 +13,7 @@ extension WooAnalyticsEvent { static let itemType = "product_type" static let itemsInCart = "items_in_cart" static let millisecondsSinceCustomerInteractionStarted = "milliseconds_since_customer_interaction_started" - static let millisecondsSinceOrderCreationSuccess = "milliseconds_since_order_creation_success" + static let millisecondsSinceOrderSyncSuccess = "milliseconds_since_order_sync_success" static let millisecondsSinceReaderReadyToCollect = "milliseconds_since_reader_ready_to_collect_payment" static let millisecondsSinceCardTapped = "milliseconds_since_card_tapped" static let checkoutTapCount = "checkout_tap_count" @@ -46,13 +46,13 @@ extension WooAnalyticsEvent { } static func cardPresentCollectPaymentSuccess(millisecondsSinceCustomerIteractionStarted: Double, - millisecondsSinceOrderCreationSuccess: Double, + millisecondsSinceOrderSyncSuccess: Double, millisecondsSinceReaderReadyToCollect: Double, millisecondsSinceCardTapped: Double, checkoutTapCount: Int) -> WooAnalyticsEvent { WooAnalyticsEvent(statName: .collectPaymentSuccess, properties: [ Key.millisecondsSinceCustomerInteractionStarted: "\(millisecondsSinceCustomerIteractionStarted)", - Key.millisecondsSinceOrderCreationSuccess: "\(millisecondsSinceOrderCreationSuccess)", + Key.millisecondsSinceOrderSyncSuccess: "\(millisecondsSinceOrderSyncSuccess)", Key.millisecondsSinceReaderReadyToCollect: "\(millisecondsSinceReaderReadyToCollect)", Key.millisecondsSinceCardTapped: "\(millisecondsSinceCardTapped)", Key.checkoutTapCount: "\(checkoutTapCount)" diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index 173d8cdd115..08600840dc5 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -165,6 +165,22 @@ private extension PointOfSaleAggregateModel { collectOrderPaymentAnalyticsTracker.trackCustomerInteractionStarted() } } + + // Tracks when the order is created or updated successfully + // pdfdoF-6hn#comment-7625-p2 + func trackOrderSyncState(_ result: Result) { + switch result { + case .success(let syncState): + switch syncState { + case .newOrder, .orderUpdated: + collectOrderPaymentAnalyticsTracker.trackOrderSyncSuccess() + default: + break + } + case .failure: + break + } + } } // MARK: - Card payments @@ -453,9 +469,7 @@ extension PointOfSaleAggregateModel { let syncOrderResult = await orderController.syncOrder(for: cart, retryHandler: { [weak self] in await self?.checkOut() }) - if case .success(.newOrder) = syncOrderResult { - collectOrderPaymentAnalyticsTracker.trackOrderCreationSuccess() - } + trackOrderSyncState(syncOrderResult) await startPaymentWhenCardReaderConnected() } } diff --git a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift index be9d61425d2..12b60825fe1 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Collect Payments/CollectOrderPaymentAnalytics.swift @@ -26,7 +26,7 @@ protocol CollectOrderPaymentAnalyticsTracking { func trackReceiptPrintFailed(error: Error) func trackCustomerInteractionStarted() - func trackOrderCreationSuccess() + func trackOrderSyncSuccess() func trackCardReaderReady() func trackCardReaderTapped() func trackCheckoutTapped() diff --git a/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift b/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift index 8032171ffa7..5c6e4aba671 100644 --- a/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift +++ b/WooCommerce/WooCommerceTests/Mocks/MockCollectOrderPaymentAnalyticsTracker.swift @@ -58,7 +58,7 @@ final class MockCollectOrderPaymentAnalyticsTracker: CollectOrderPaymentAnalytic // no-op } - func trackOrderCreationSuccess() { + func trackOrderSyncSuccess() { // no-op } diff --git a/WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift b/WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift index a2e64fda77c..5ca22be7b87 100644 --- a/WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Analytics/POSCollectOrderPaymentAnalyticsTests.swift @@ -17,7 +17,7 @@ struct POSCollectOrderPaymentAnalyticsTests { let capturedPaymentData = CardPresentCapturedPaymentData(paymentMethod: .cardPresent(details: .fake()), receiptParameters: nil) let expectedEvent = "card_present_collect_payment_success" let expectedProperties = [ - "milliseconds_since_order_creation_success", + "milliseconds_since_order_sync_success", "milliseconds_since_reader_ready_to_collect_payment", "milliseconds_since_card_tapped", "milliseconds_since_customer_interaction_started",