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
2 changes: 1 addition & 1 deletion WooCommerce/Classes/Analytics/WooAnalyticsEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2414,7 +2414,7 @@ extension WooAnalyticsEvent {
}
}

private extension PaymentMethod {
extension PaymentMethod {
var analyticsValue: String {
switch self {
case .card, .cardPresent:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import protocol WooFoundation.Analytics
import Yosemite

final class POSCollectOrderPaymentAnalytics: POSCollectOrderPaymentAnalyticsTracking {
var connectedReaderModel: String?

private var customerInteractionStarted: Double = 0
private var orderSync: Double = 0
private var cardReaderReady: Double = 0
Expand All @@ -14,11 +12,35 @@ final class POSCollectOrderPaymentAnalytics: POSCollectOrderPaymentAnalyticsTrac

private let analytics: Analytics

init(analytics: Analytics = ServiceLocator.analytics) {
private let siteID: Int64
private var paymentGatewayAccount: PaymentGatewayAccount?
private let configuration: CardPresentPaymentsConfiguration
private var connectedReader: CardReader?
var connectedReaderModel: String? {
connectedReader?.readerType.model
}

init(siteID: Int64,
analytics: Analytics = ServiceLocator.analytics,
configuration: CardPresentPaymentsConfiguration = CardPresentConfigurationLoader().configuration) {
self.siteID = siteID
self.analytics = analytics
self.configuration = configuration
}

func preflightResultReceived(_ result: CardReaderPreflightResult?) {
switch result {
case .completed(let connectedReader, let paymentGatewayAccount):
self.connectedReader = connectedReader
self.paymentGatewayAccount = paymentGatewayAccount
case .canceled(_, let paymentGatewayAccount):
self.connectedReader = nil
self.paymentGatewayAccount = paymentGatewayAccount
case .none:
break
}
}

func preflightResultReceived(_ result: CardReaderPreflightResult?) { }
func trackProcessingCompletion(intent: Yosemite.PaymentIntent) { }

func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) {
Expand All @@ -35,6 +57,11 @@ final class POSCollectOrderPaymentAnalytics: POSCollectOrderPaymentAnalyticsTrac
let elapsedTimeSinceCardTapped = calculateElapsedTimeInMilliseconds(since: cardReaderTapped)

analytics.track(event: .PointOfSale.cardPresentCollectPaymentSuccess(
forGatewayID: paymentGatewayAccount?.gatewayID,
countryCode: configuration.countryCode,
paymentMethod: capturedPaymentData.paymentMethod,
cardReaderModel: connectedReaderModel,
siteID: siteID,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Do we need to track site_id explicitly? We already capture blog_id as part of the event.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging, I missed this detail. I used CollectOrderPaymentAnalytics as an example, but I didn't notice we track blog_id. Indeed, it doesn't look to be necessary

millisecondsSinceCustomerIteractionStarted: elapsedTimeSinceCustomerInteraction,
millisecondsSinceOrderSyncSuccess: elapsedTimeSinceOrderSync,
millisecondsSinceReaderReadyToCollect: elapsedTimeSinceCardReaderReady,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import enum Yosemite.POSItemType
import enum Yosemite.POSItem
import struct Yosemite.POSSimpleProduct
import struct Yosemite.POSVariation
import enum WooFoundation.CountryCode
import enum Yosemite.PaymentMethod

extension WooAnalyticsEvent {
enum PointOfSale {
Expand All @@ -25,6 +27,11 @@ extension WooAnalyticsEvent {
static let resultsCount = "results_count"
static let millisecondsSinceRequestSent = "milliseconds_since_request_sent"
static let totalItems = "total_items"
static let cardReaderModel = "card_reader_model"
static let countryCode = "country"
static let paymentMethodType = "payment_method_type"
static let siteID = "site_id"
static let gatewayID = "plugin_slug"
}

static func paymentsOnboardingShown() -> WooAnalyticsEvent {
Expand Down Expand Up @@ -100,12 +107,22 @@ extension WooAnalyticsEvent {
WooAnalyticsEvent(statName: .pointOfSaleReaderReadyForCardPayment, properties: [Key.waitingTime: "\(waitingTime)"])
}

static func cardPresentCollectPaymentSuccess(millisecondsSinceCustomerIteractionStarted: Double,
static func cardPresentCollectPaymentSuccess(forGatewayID: String?,
countryCode: CountryCode,
paymentMethod: PaymentMethod,
cardReaderModel: String?,
siteID: Int64,
millisecondsSinceCustomerIteractionStarted: Double,
millisecondsSinceOrderSyncSuccess: Double,
millisecondsSinceReaderReadyToCollect: Double,
millisecondsSinceCardTapped: Double,
checkoutTapCount: Int) -> WooAnalyticsEvent {
WooAnalyticsEvent(statName: .collectPaymentSuccess, properties: [
Key.cardReaderModel: readerModel(for: cardReaderModel),
Key.countryCode: countryCode.rawValue,
Key.gatewayID: safeGatewayID(for: forGatewayID),
Key.paymentMethodType: paymentMethod.analyticsValue,
Key.siteID: siteID,
Key.millisecondsSinceCustomerInteractionStarted: "\(millisecondsSinceCustomerIteractionStarted)",
Key.millisecondsSinceOrderSyncSuccess: "\(millisecondsSinceOrderSyncSuccess)",
Key.millisecondsSinceReaderReadyToCollect: "\(millisecondsSinceReaderReadyToCollect)",
Expand Down Expand Up @@ -183,6 +200,16 @@ extension WooAnalyticsEvent {
}
}

private extension WooAnalyticsEvent.PointOfSale {
static func readerModel(for connectedReaderModel: String?) -> String {
connectedReaderModel ?? "none_connected"
}

static func safeGatewayID(for gatewayID: String?) -> String {
gatewayID ?? "unknown"
}
}

extension WooAnalyticsEvent.PointOfSale {
/// Source of the event where the event is triggered
/// Views: Product, Variation, and Coupon Lists. Cart view and Checkout error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ struct PointOfSaleEntryPointView: View {
onPointOfSaleModeActiveStateChange: { _ in },
cardPresentPaymentService: CardPresentPaymentPreviewService(),
orderController: PointOfSalePreviewOrderController(),
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics(),
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentPreviewAnalytics(),
searchHistoryService: PointOfSalePreviewHistoryService(),
popularPurchasableItemsController: PointOfSalePreviewItemsController(),
barcodeScanService: PointOfSalePreviewBarcodeScanService(),
Expand Down
2 changes: 1 addition & 1 deletion WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private extension POSTabCoordinator {
func presentPOSView() {
Task { @MainActor [weak self] in
guard let self else { return }
let collectOrderPaymentAnalyticsTracker = POSCollectOrderPaymentAnalytics()
let collectOrderPaymentAnalyticsTracker = POSCollectOrderPaymentAnalytics(siteID: siteID)
let cardPresentPaymentService = await CardPresentPaymentService(siteID: siteID,
stores: storesManager,
collectOrderPaymentAnalyticsTracker: collectOrderPaymentAnalyticsTracker)
Expand Down
41 changes: 40 additions & 1 deletion WooCommerce/Classes/POS/Utils/PreviewHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import enum Yosemite.POSItemType
import protocol Yosemite.PointOfSaleBarcodeScanServiceProtocol
import enum Yosemite.PointOfSaleBarcodeScanError
import Combine
import struct Yosemite.PaymentIntent

// MARK: - PreviewProvider helpers
//
Expand Down Expand Up @@ -212,7 +213,7 @@ struct POSPreviewHelpers {
couponsSearchController: PointOfSaleCouponsControllerProtocol = PointOfSalePreviewCouponsController(),
cardPresentPaymentService: CardPresentPaymentFacade = CardPresentPaymentPreviewService(),
orderController: PointOfSaleOrderControllerProtocol = PointOfSalePreviewOrderController(),
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking = POSCollectOrderPaymentAnalytics(),
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking = POSCollectOrderPaymentPreviewAnalytics(),
searchHistoryService: POSSearchHistoryProviding = PointOfSalePreviewHistoryService(),
popularItemsController: PointOfSaleItemsControllerProtocol = PointOfSalePreviewItemsController(),
barcodeScanService: PointOfSaleBarcodeScanServiceProtocol = PointOfSalePreviewBarcodeScanService()
Expand All @@ -239,4 +240,42 @@ final class PointOfSalePreviewBarcodeScanService: PointOfSaleBarcodeScanServiceP
}
}

final class POSCollectOrderPaymentPreviewAnalytics: POSCollectOrderPaymentAnalyticsTracking {
func trackCustomerInteractionStarted() {}

func trackOrderSyncSuccess() {}

func trackCardReaderReady() {}

func trackCardReaderTapped() {}

func trackCheckoutTapped() {}

func resetCheckoutTapCountTracker() {}

func trackSuccessfulCashPayment() {}

var connectedReaderModel: String?

func preflightResultReceived(_ result: CardReaderPreflightResult?) {}

func trackProcessingCompletion(intent: PaymentIntent) {}

func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) {}

func trackPaymentFailure(with error: any Error) {}

func trackPaymentCancelation(cancelationSource: WooAnalyticsEvent.InPersonPayments.CancellationSource) {}

func trackEmailTapped() {}

func trackReceiptPrintTapped() {}

func trackReceiptPrintSuccess() {}

func trackReceiptPrintCanceled() {}

func trackReceiptPrintFailed(error: any Error) {}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ final class HubMenuViewModel: ObservableObject {
}()

private(set) var cardPresentPaymentService: CardPresentPaymentFacade?
private(set) var collectOrderPaymentAnalyticsTracker = POSCollectOrderPaymentAnalytics()
private(set) var collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics
private let analytics: Analytics

init(siteID: Int64,
Expand All @@ -181,6 +181,7 @@ final class HubMenuViewModel: ObservableObject {
currencySettings: ServiceLocator.currencySettings,
featureFlagService: featureFlagService)
self.analytics = analytics
self.collectOrderPaymentAnalyticsTracker = POSCollectOrderPaymentAnalytics(siteID: siteID)
observeSiteForUIUpdates()
observePlanName()
observeGoogleAdsEntryPointAvailability()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
@testable import WooCommerce
import protocol WooFoundation.Analytics
import struct Yosemite.CardPresentPaymentsConfiguration
import enum WooFoundation.CountryCode
import Testing

struct POSCollectOrderPaymentAnalyticsTests {
Expand All @@ -11,17 +13,24 @@ struct POSCollectOrderPaymentAnalyticsTests {
analytics = WooAnalytics(analyticsProvider: analyticsProvider)
}

@Test func POSCollectOrderPaymentAnalyticsTests_when_successful_payment_then_tracks_event_and_properties() {
@Test func analytics_when_successful_payment_then_tracks_event_and_properties() {
// Given
let sut = POSCollectOrderPaymentAnalytics(analytics: analytics)
let siteID: Int64 = 123
let configuration = CardPresentPaymentsConfiguration(country: .US)
let sut = POSCollectOrderPaymentAnalytics(siteID: siteID, analytics: analytics, configuration: configuration)
let capturedPaymentData = CardPresentCapturedPaymentData(paymentMethod: .cardPresent(details: .fake()), receiptParameters: nil)
let expectedEvent = "card_present_collect_payment_success"
let expectedProperties = [
"milliseconds_since_order_sync_success",
"milliseconds_since_reader_ready_to_collect_payment",
"milliseconds_since_card_tapped",
"milliseconds_since_customer_interaction_started",
"checkout_tap_count"
"checkout_tap_count",
"card_reader_model",
"country",
"payment_method_type",
"site_id",
"plugin_slug"
]

// When
Expand Down