blob: 0785136125f766401cc49c2ea4a44b62df59d846 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PAYMENTS_CORE_JOURNEY_LOGGER_H_
#define COMPONENTS_PAYMENTS_CORE_JOURNEY_LOGGER_H_
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
namespace payments {
// A class to keep track of different stats during a Payment Request journey. It
// collects different metrics during the course of the checkout flow, like the
// number of credit cards that the user added or edited. The metrics will be
// logged when RecordJourneyStatsHistograms is called with the completion status
// of the Payment Request.
class JourneyLogger {
public:
// Note: Java counterparts will be generated for these enums.
// The different sections of a Payment Request. Used to record journey
// stats.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.payments
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: Section
enum Section {
SECTION_CONTACT_INFO = 0,
SECTION_PAYMENT_METHOD = 1,
SECTION_SHIPPING_ADDRESS = 2,
SECTION_MAX,
};
// Used to log different parameters' effect on whether the transaction was
// completed.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.payments
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: CompletionStatus
enum CompletionStatus {
COMPLETION_STATUS_COMPLETED = 0,
COMPLETION_STATUS_USER_ABORTED = 1,
COMPLETION_STATUS_OTHER_ABORTED = 2,
COMPLETION_STATUS_COULD_NOT_SHOW = 3,
COMPLETION_STATUS_USER_OPTED_OUT = 4,
COMPLETION_STATUS_MAX,
};
// Used to record the different events that happened during the Payment
// Request.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.payments
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: Event
enum Event {
// Initiated means the PaymentRequest object was constructed.
EVENT_INITIATED = 0,
// PaymentRequest was triggered via .show() and a native UI was shown.
EVENT_SHOWN = 1 << 0,
// A payment app was invoked, regardless of whether the UI was skipped or
// the pay button was actually clicked.
EVENT_PAY_CLICKED = 1 << 1,
EVENT_RECEIVED_INSTRUMENT_DETAILS = 1 << 2,
// PaymentRequest was triggered via .show() and no UI was shown because we
// skipped directly to the payment app.
EVENT_SKIPPED_SHOW = 1 << 3,
// .complete() was called by the merchant, completing the flow.
EVENT_COMPLETED = 1 << 4,
// The user aborted the flow by either dismissing it explicitly, or
// navigating away (if possible).
EVENT_USER_ABORTED = 1 << 5,
// Other reasons for aborting include the merchant calling .abort(), the
// merchant triggering a navigation, the tab closing, the browser closing,
// etc. See implementation for details.
EVENT_OTHER_ABORTED = 1 << 6,
EVENT_HAD_INITIAL_FORM_OF_PAYMENT = 1 << 7,
EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS = 1 << 8,
// canMakePayment was called with a result of "true" or "false",
// respectively. An absence of both events means canMakePayment was not
// called, or the user was in incognito mode.
EVENT_CAN_MAKE_PAYMENT_TRUE = 1 << 9,
EVENT_CAN_MAKE_PAYMENT_FALSE = 1 << 10,
// Correspond to the merchant specifying requestShipping, requestPayerName,
// requestPayerEmail, requestPayerPhone.
EVENT_REQUEST_SHIPPING = 1 << 11,
EVENT_REQUEST_PAYER_NAME = 1 << 12,
EVENT_REQUEST_PAYER_EMAIL = 1 << 13,
EVENT_REQUEST_PAYER_PHONE = 1 << 14,
// The merchant requested at least one basic-card method.
EVENT_REQUEST_METHOD_BASIC_CARD = 1 << 15,
// The merchant requested a Google payment method.
EVENT_REQUEST_METHOD_GOOGLE = 1 << 16,
// The merchant requested a non-Google, non-basic-card payment method.
EVENT_REQUEST_METHOD_OTHER = 1 << 17,
// The user initiated the transaction using a saved credit card, a Google
// payment app (e.g., Android Pay), or another payment instrument,
// respectively.
EVENT_SELECTED_CREDIT_CARD = 1 << 18,
EVENT_SELECTED_GOOGLE = 1 << 19,
EVENT_SELECTED_OTHER = 1 << 20,
// hasEnrolledInstrument was called with a result of "true" or "false",
// respectively. An absence of both events means hasEnrolledInstrument was
// not called, or the user was in incognito mode.
EVENT_HAS_ENROLLED_INSTRUMENT_TRUE = 1 << 21,
EVENT_HAS_ENROLLED_INSTRUMENT_FALSE = 1 << 22,
// True when a NotShownReason is set.
EVENT_COULD_NOT_SHOW = 1 << 23,
EVENT_NEEDS_COMPLETION_CONTACT_INFO = 1 << 24,
EVENT_NEEDS_COMPLETION_PAYMENT = 1 << 25,
EVENT_NEEDS_COMPLETION_SHIPPING = 1 << 26,
// Payment apps available (after JIT crawling) at the time show() is called.
EVENT_AVAILABLE_METHOD_BASIC_CARD = 1 << 27,
EVENT_AVAILABLE_METHOD_GOOGLE = 1 << 28,
EVENT_AVAILABLE_METHOD_OTHER = 1 << 29,
// Bits for secure-payment-confirmation method.
EVENT_REQUEST_METHOD_SECURE_PAYMENT_CONFIRMATION = 1 << 30,
EVENT_SELECTED_SECURE_PAYMENT_CONFIRMATION = 1 << 31,
EVENT_ENUM_MAX = EVENT_SELECTED_SECURE_PAYMENT_CONFIRMATION,
};
// A new version of Event. Some basic-card/autofill related bits are
// removed to free up more bits for new future payment methods.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.payments
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: Event2
enum class Event2 {
// Initiated means the PaymentRequest object was constructed.
kInitiated = 0,
// PaymentRequest was triggered via .show() and a native UI was shown.
kShown = 1 << 0,
// A payment app was invoked.
kPayClicked = 1 << 1,
// Whether any payer data (i.e., name, email, phone)
// was requested.
kRequestPayerData = 1 << 2,
// PaymentRequest was triggered via .show() and no UI was shown because we
// skipped directly to the payment app.
kSkippedShow = 1 << 3,
// .complete() was called by the merchant, completing the flow.
kCompleted = 1 << 4,
// The user aborted the flow by either dismissing it explicitly, or
// navigating away (if possible).
kUserAborted = 1 << 5,
// Other reasons for aborting include the merchant calling .abort(), the
// merchant triggering a navigation, the tab closing, the browser closing,
// etc. See implementation for details.
kOtherAborted = 1 << 6,
// Whether or not any requested method is available.
kHadInitialFormOfPayment = 1 << 7,
// An opt-out experience was offered to the user as part of the flow.
kOptOutOffered = 1 << 8,
// The user elected to opt-out of the flow (and future flows).
kUserOptedOut = 1 << 9,
// .show() was allowed without a user activaiton.
kActivationlessShow = 1 << 10,
// Correspond to the merchant specifying requestShipping,
// requestPayerName,
// requestPayerEmail, requestPayerPhone.
kRequestShipping = 1 << 11,
// The merchent requested a Google Pay Authentication method.
kRequestMethodGooglePayAuthentication = 1 << 13,
// The merchant requested a Play Billing payment method.
kRequestMethodPlayBilling = 1 << 14,
// The merchant requested at least one basic-card method.
kRequestMethodBasicCard = 1 << 15,
// The merchant requested a Google payment method.
kRequestMethodGoogle = 1 << 16,
// The merchant requested a non-Google, non-basic-card payment method.
kRequestMethodOther = 1 << 17,
// The user initiated the transaction using a saved credit card, a Google
// payment app (e.g., Android Pay), or another payment instrument,
// respectively.
kSelectedCreditCard = 1 << 18,
kSelectedGoogle = 1 << 19,
kSelectedOther = 1 << 20,
kSelectedPlayBilling = 1 << 21,
// True when a NotShownReason is set.
kCouldNotShow = 1 << 23,
// Bits for secure-payment-confirmation method.
kNoMatchingCredentials = 1 << 29,
kRequestMethodSecurePaymentConfirmation = 1 << 30,
kSelectedSecurePaymentConfirmation = 1 << 31,
kEnumMax = kSelectedSecurePaymentConfirmation,
};
// The reason why the Payment Request was aborted.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.payments
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: AbortReason
enum AbortReason {
ABORT_REASON_ABORTED_BY_USER = 0,
ABORT_REASON_ABORTED_BY_MERCHANT = 1,
ABORT_REASON_INVALID_DATA_FROM_RENDERER = 2,
ABORT_REASON_MOJO_CONNECTION_ERROR = 3,
ABORT_REASON_MOJO_RENDERER_CLOSING = 4,
ABORT_REASON_INSTRUMENT_DETAILS_ERROR = 5,
ABORT_REASON_NO_MATCHING_PAYMENT_METHOD = 6, // Deprecated.
ABORT_REASON_NO_SUPPORTED_PAYMENT_METHOD = 7, // Deprecated.
ABORT_REASON_OTHER = 8,
ABORT_REASON_USER_NAVIGATION = 9,
ABORT_REASON_MERCHANT_NAVIGATION = 10,
ABORT_REASON_USER_OPTED_OUT = 11,
ABORT_REASON_MAX,
};
// The categories of the payment methods.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.payments
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: PaymentMethodCategory
enum class PaymentMethodCategory {
kBasicCard = 0,
kGoogle = 1,
kPlayBilling = 2,
kSecurePaymentConfirmation = 3,
kOther = 4,
kGooglePayAuthentication = 5,
kMaxValue = kGooglePayAuthentication,
};
// Records different checkout steps for payment requests. The difference
// between number of requests recorded for each step and its successor shows
// the drop-off that happened during that step.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.payments
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: CheckoutFunnelStep
enum class CheckoutFunnelStep {
// Payment request has been initiated.
kInitiated = 0,
// .show() has been called. (a.k.a. the user has clicked on the checkout/buy
// button on merchant's site.)
kShowCalled = 1,
// Payment request UI has been shown or skipped in favor of the payment
// handler UI. Drop-off before this step means that the browser could not
// proceed with the payment request. (e.g. because of no payment app being
// available for requested payment method(s) or an unsecured origin.)
kPaymentRequestTriggered = 2,
// Payment handler UI has been invoked either by skipping to it directly or
// the user clicking on the "Continue" button in payment sheet UI.
kPaymentHandlerInvoked = 3,
// Payment request has been completed with 'success' status.
kCompleted = 4,
kMaxValue = kCompleted,
};
JourneyLogger(bool is_incognito, ukm::SourceId payment_request_source_id);
JourneyLogger(const JourneyLogger&) = delete;
JourneyLogger& operator=(const JourneyLogger&) = delete;
~JourneyLogger();
// Sets the number of suggestions shown for the specified section.
void SetNumberOfSuggestionsShown(Section section,
int number,
bool has_complete_suggestion);
// Records the fact that the merchant called CanMakePayment and records its
// return value.
void SetCanMakePaymentValue(bool value);
// Records the fact that the merchant called HasEnrolledInstrument and records
// its return value.
void SetHasEnrolledInstrumentValue(bool value);
// Records that an Opt Out experience is being offered to the user in the
// current UI flow.
void SetOptOutOffered();
// Records that a show() was allowed without a user activation.
void SetActivationlessShow();
// Records that a payment app has been shown without payment UIs being shown
// before that.
void SetSkippedShow();
// Records that a payment UI has been shown.
void SetShown();
// Records that the instrument details have been received.
void SetReceivedInstrumentDetails();
// Records that a payment app was invoked.
void SetPayClicked();
// Records the category of the selected app.
void SetSelectedMethod(PaymentMethodCategory category);
// Records the method that is supported by the available payment apps.
void SetAvailableMethod(PaymentMethodCategory category);
// Records the user information requested by the merchant.
void SetRequestedInformation(bool requested_shipping,
bool requested_email,
bool requested_phone,
bool requested_name);
// Records the requested payment method types. A value should be true if at
// least one payment method in the category (basic-card, google payment
// method, secure payment confirmation method or other url-based payment
// method, respectively) is requested.
// TODO(crbug.com/40534824): Add support for non-basic-card, non-URL methods.
void SetRequestedPaymentMethods(
const std::vector<PaymentMethodCategory>& methods);
// Records that the Payment Request was completed successfully, and starts the
// logging of all the journey metrics.
void SetCompleted();
// Records that the Payment Request was aborted. This counts as a completion,
// starting the logging of all the journey metrics.
void SetAborted(AbortReason reason);
// Records that the Payment Request was not shown to the user.
void SetNotShown();
// Records that the SPC No Matching Credentials UX was shown to the user.
void SetNoMatchingCredentialsShown();
// Increments the bucket count for the given checkout step.
void RecordCheckoutStep(CheckoutFunnelStep step);
// Sets the UKM source id of the selected app when it gets invoked.
void SetPaymentAppUkmSourceId(ukm::SourceId payment_app_source_id);
base::WeakPtr<JourneyLogger> GetWeakPtr();
private:
// Records that an event occurred.
void SetEventOccurred(Event event);
// Records that an event occurred.
void SetEvent2Occurred(Event2 event);
// Whether the given event was occurred.
bool WasOccurred(Event2 event) const;
static const int NUMBER_OF_SECTIONS = 3;
// Note: These constants should always be in sync with their counterpart in
// components/payments/content/android/java/src/org/chromium/components/
// payments/JourneyLogger.java.
// The minimum expected value of CustomCountHistograms is always set to 1. It
// is still possible to log the value 0 to that type of histogram.
const int MIN_EXPECTED_SAMPLE = 1;
const int MAX_EXPECTED_SAMPLE = 49;
const int NUMBER_BUCKETS = 50;
struct SectionStats {
SectionStats()
: number_suggestions_shown_(0),
is_requested_(false),
has_complete_suggestion_(false) {}
int number_suggestions_shown_;
bool is_requested_;
bool has_complete_suggestion_;
};
// Records the histograms for all the sections that were requested by the
// merchant and for the usage of the CanMakePayment method and its effect on
// the transaction. This method should be called when the Payment Request has
// either been completed or aborted.
void RecordJourneyStatsHistograms(CompletionStatus completion_status);
// Records the histograms for all the sections that were requested by the
// merchant.
void RecordSectionSpecificStats(CompletionStatus completion_status);
// Records the metric about the different events that happened during the
// Payment Request.
void RecordEventsMetric(CompletionStatus completion_status);
// Validates the recorded event sequence during the Payment Request.
void ValidateEventBits() const;
// Asserts that the given bits in events_ and events2_ are equal.
void AssertOccurredTogether(Event event, Event2 event2) const;
// Returns whether this Payment Request was triggered (shown or skipped show).
bool WasPaymentRequestTriggered();
// Sets needs completion bit in events_ bit field for the given section.
void SetSectionNeedsCompletion(Section section);
SectionStats sections_[NUMBER_OF_SECTIONS];
bool has_recorded_ = false;
bool is_incognito_;
// Accumulates the many events that have happened during the Payment Request.
int events_;
// The 2.0 version of event_.
int events2_;
ukm::SourceId payment_request_source_id_;
ukm::SourceId payment_app_source_id_ = ukm::kInvalidSourceId;
base::WeakPtrFactory<JourneyLogger> weak_ptr_factory_{this};
};
} // namespace payments
#endif // COMPONENTS_PAYMENTS_CORE_JOURNEY_LOGGER_H_