blob: de0b525e52db15a30638d308f49aacda7e18f51c [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill/core/browser/local_card_migration_manager.h"
#include <stddef.h>
#include <algorithm>
#include <vector>
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/form_data_importer.h"
#include "components/autofill/core/browser/payments/payments_client.h"
#include "components/autofill/core/browser/payments/payments_util.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "services/identity/public/cpp/identity_manager.h"
namespace autofill {
MigratableCreditCard::MigratableCreditCard(const CreditCard& credit_card)
: credit_card_(credit_card) {}
MigratableCreditCard::~MigratableCreditCard() {}
LocalCardMigrationManager::LocalCardMigrationManager(
AutofillClient* client,
payments::PaymentsClient* payments_client,
const std::string& app_locale,
PersonalDataManager* personal_data_manager)
: client_(client),
payments_client_(payments_client),
app_locale_(app_locale),
personal_data_manager_(personal_data_manager),
weak_ptr_factory_(this) {}
LocalCardMigrationManager::~LocalCardMigrationManager() {}
bool LocalCardMigrationManager::ShouldOfferLocalCardMigration(
int imported_credit_card_record_type) {
// Must be an existing card. New cards always get Upstream or local save.
switch (imported_credit_card_record_type) {
case FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD:
local_card_migration_origin_ =
AutofillMetrics::LocalCardMigrationOrigin::UseOfLocalCard;
break;
case FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD:
local_card_migration_origin_ =
AutofillMetrics::LocalCardMigrationOrigin::UseOfServerCard;
break;
default:
return false;
}
if (!IsCreditCardMigrationEnabled())
return false;
// Don't show the the prompt if user cancelled/rejected previously.
if (prefs::IsLocalCardMigrationPromptPreviouslyCancelled(client_->GetPrefs()))
return false;
// Fetch all migratable credit cards and store in |migratable_credit_cards_|.
GetMigratableCreditCards();
// If the form was submitted with a local card, only offer migration instead
// of Upstream if there are other local cards to migrate as well. If the form
// was submitted with a server card, offer migration if ANY local cards can be
// migrated.
return (imported_credit_card_record_type ==
FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD &&
migratable_credit_cards_.size() > 1) ||
(imported_credit_card_record_type ==
FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD &&
!migratable_credit_cards_.empty());
}
void LocalCardMigrationManager::AttemptToOfferLocalCardMigration(
bool is_from_settings_page) {
// Abort the migration if |payments_client_| is nullptr.
if (!payments_client_)
return;
migration_request_ = payments::PaymentsClient::MigrationRequestDetails();
payments_client_->GetUploadDetails(
std::vector<AutofillProfile>(), GetDetectedValues(),
/*active_experiments=*/std::vector<const char*>(), app_locale_,
base::BindOnce(&LocalCardMigrationManager::OnDidGetUploadDetails,
weak_ptr_factory_.GetWeakPtr(), is_from_settings_page),
payments::kMigrateCardsBillableServiceNumber,
is_from_settings_page ? payments::PaymentsClient::UploadCardSource::
LOCAL_CARD_MIGRATION_SETTINGS_PAGE
: payments::PaymentsClient::UploadCardSource::
LOCAL_CARD_MIGRATION_CHECKOUT_FLOW);
}
// Callback function when user agrees to migration on the intermediate dialog.
// Call ShowMainMigrationDialog() to pop up a larger, modal dialog showing the
// local cards to be uploaded.
void LocalCardMigrationManager::OnUserAcceptedIntermediateMigrationDialog() {
AutofillMetrics::LogLocalCardMigrationPromptMetric(
local_card_migration_origin_,
AutofillMetrics::INTERMEDIATE_BUBBLE_ACCEPTED);
ShowMainMigrationDialog();
}
// Send the migration request once risk data is available.
void LocalCardMigrationManager::OnUserAcceptedMainMigrationDialog(
const std::vector<std::string>& selected_card_guids) {
user_accepted_main_migration_dialog_ = true;
AutofillMetrics::LogLocalCardMigrationPromptMetric(
local_card_migration_origin_, AutofillMetrics::MAIN_DIALOG_ACCEPTED);
// Update the |migratable_credit_cards_| with the |selected_card_guids|. This
// will remove any card from |migratable_credit_cards_| of which the GUID is
// not in |selected_card_guids|.
auto card_is_selected = [&selected_card_guids](MigratableCreditCard& card) {
return !base::ContainsValue(selected_card_guids, card.credit_card().guid());
};
migratable_credit_cards_.erase(
std::remove_if(migratable_credit_cards_.begin(),
migratable_credit_cards_.end(), card_is_selected),
migratable_credit_cards_.end());
// Populating risk data and offering migration two-round pop-ups occur
// asynchronously. If |migration_risk_data_| has already been loaded, send the
// migrate local cards request. Otherwise, continue to wait and let
// OnDidGetUploadRiskData handle it.
if (!migration_request_.risk_data.empty())
SendMigrateLocalCardsRequest();
}
void LocalCardMigrationManager::OnUserDeletedLocalCardViaMigrationDialog(
const std::string& deleted_card_guid) {
personal_data_manager_->RemoveByGUID(deleted_card_guid);
}
bool LocalCardMigrationManager::IsCreditCardMigrationEnabled() {
// Confirm that the user is signed in, syncing, and the proper experiment
// flags are enabled.
bool migration_experiment_enabled =
features::GetLocalCardMigrationExperimentalFlag() !=
features::LocalCardMigrationExperimentalFlag::kMigrationDisabled;
bool credit_card_upload_enabled = ::autofill::IsCreditCardUploadEnabled(
client_->GetPrefs(), client_->GetSyncService(),
client_->GetIdentityManager()->GetPrimaryAccountInfo().email);
bool has_google_payments_account =
(payments::GetBillingCustomerId(personal_data_manager_,
payments_client_->GetPrefService()) != 0);
bool sync_feature_enabled =
(personal_data_manager_->GetSyncSigninState() ==
AutofillSyncSigninState::kSignedInAndSyncFeature);
return migration_experiment_enabled && credit_card_upload_enabled &&
has_google_payments_account && sync_feature_enabled;
}
void LocalCardMigrationManager::OnDidGetUploadDetails(
bool is_from_settings_page,
AutofillClient::PaymentsRpcResult result,
const base::string16& context_token,
std::unique_ptr<base::DictionaryValue> legal_message) {
if (result == AutofillClient::SUCCESS) {
migration_request_.context_token = context_token;
legal_message_ = std::move(legal_message);
migration_request_.risk_data.clear();
// If we successfully received the legal docs, trigger the offer-to-migrate
// dialog. If triggered from settings page, we pop-up the main prompt
// directly. If not, we pop up the intermediate bubble.
if (is_from_settings_page) {
// Set the origin to SettingsPage.
local_card_migration_origin_ =
AutofillMetrics::LocalCardMigrationOrigin::SettingsPage;
// Pops up a larger, modal dialog showing the local cards to be uploaded.
ShowMainMigrationDialog();
} else {
client_->ShowLocalCardMigrationDialog(base::BindOnce(
&LocalCardMigrationManager::OnUserAcceptedIntermediateMigrationDialog,
weak_ptr_factory_.GetWeakPtr()));
AutofillMetrics::LogLocalCardMigrationPromptMetric(
local_card_migration_origin_,
AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN);
}
// TODO(crbug.com/876895): Clean up the LoadRiskData Bind/BindRepeating
// usages
client_->LoadRiskData(base::BindRepeating(
&LocalCardMigrationManager::OnDidGetMigrationRiskData,
weak_ptr_factory_.GetWeakPtr()));
}
}
void LocalCardMigrationManager::OnDidMigrateLocalCards(
AutofillClient::PaymentsRpcResult result,
std::unique_ptr<std::unordered_map<std::string, std::string>> save_result,
const std::string& display_text) {
if (!save_result)
return;
if (result == AutofillClient::PaymentsRpcResult::SUCCESS) {
std::vector<CreditCard> migrated_cards;
// Traverse the migratable credit cards to update each migrated card status.
for (MigratableCreditCard& card : migratable_credit_cards_) {
// Not every card exists in the |save_result| since some cards are
// unchecked by the user and not migrated.
auto it = save_result->find(card.credit_card().guid());
// If current card exists in the |save_result|, update its migration
// status.
if (it != save_result->end()) {
// Server-side response can return SUCCESS, TEMPORARY_FAILURE, or
// PERMANENT_FAILURE (see SaveResult enum). Branch here depending on
// which is received.
if (it->second == kMigrationResultPermanentFailure ||
it->second == kMigrationResultTemporaryFailure) {
card.set_migration_status(autofill::MigratableCreditCard::
MigrationStatus::FAILURE_ON_UPLOAD);
} else if (it->second == kMigrationResultSuccess) {
card.set_migration_status(autofill::MigratableCreditCard::
MigrationStatus::SUCCESS_ON_UPLOAD);
migrated_cards.push_back(card.credit_card());
} else {
NOTREACHED();
}
}
}
// Remove cards that were successfully migrated from local storage.
personal_data_manager_->DeleteLocalCreditCards(migrated_cards);
}
if (base::FeatureList::IsEnabled(
features::kAutofillLocalCardMigrationShowFeedback)) {
client_->ShowLocalCardMigrationResults(
result != AutofillClient::PaymentsRpcResult::SUCCESS,
base::UTF8ToUTF16(display_text), migratable_credit_cards_,
base::BindRepeating(&LocalCardMigrationManager::
OnUserDeletedLocalCardViaMigrationDialog,
weak_ptr_factory_.GetWeakPtr()));
}
}
void LocalCardMigrationManager::OnDidGetMigrationRiskData(
const std::string& risk_data) {
migration_request_.risk_data = risk_data;
// Populating risk data and offering migration two-round pop-ups occur
// asynchronously. If the main migration dialog has already been accepted,
// send the migrate local cards request. Otherwise, continue to wait for the
// user to accept the two round dialog.
if (user_accepted_main_migration_dialog_)
SendMigrateLocalCardsRequest();
}
// Send the migration request. Will call payments_client to create a new
// PaymentsRequest. Also create a new callback function OnDidMigrateLocalCards.
void LocalCardMigrationManager::SendMigrateLocalCardsRequest() {
migration_request_.app_locale = app_locale_;
migration_request_.billing_customer_number = payments::GetBillingCustomerId(
personal_data_manager_, payments_client_->GetPrefService());
payments_client_->MigrateCards(
migration_request_, migratable_credit_cards_,
base::BindOnce(&LocalCardMigrationManager::OnDidMigrateLocalCards,
weak_ptr_factory_.GetWeakPtr()));
user_accepted_main_migration_dialog_ = false;
}
// Pops up a larger, modal dialog showing the local cards to be uploaded. Pass
// the reference of vector<MigratableCreditCard> and the callback function is
// OnUserAcceptedMainMigrationDialog(). Can be called when user agrees to
// migration on the intermediate dialog or directly from settings page.
void LocalCardMigrationManager::ShowMainMigrationDialog() {
AutofillMetrics::LogLocalCardMigrationPromptMetric(
local_card_migration_origin_, AutofillMetrics::MAIN_DIALOG_SHOWN);
// Pops up a larger, modal dialog showing the local cards to be uploaded.
client_->ConfirmMigrateLocalCardToCloud(
std::move(legal_message_), migratable_credit_cards_,
base::BindOnce(
&LocalCardMigrationManager::OnUserAcceptedMainMigrationDialog,
weak_ptr_factory_.GetWeakPtr()));
}
int LocalCardMigrationManager::GetDetectedValues() const {
int detected_values = 0;
// If all cards to be migrated have a cardholder name, include it in the
// detected values.
bool all_cards_have_cardholder_name = true;
for (MigratableCreditCard migratable_credit_card : migratable_credit_cards_) {
all_cards_have_cardholder_name &=
!migratable_credit_card.credit_card()
.GetInfo(AutofillType(CREDIT_CARD_NAME_FULL), app_locale_)
.empty();
}
if (all_cards_have_cardholder_name)
detected_values |= CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME;
// Local card migration should ONLY be offered when the user already has a
// Google Payments account.
DCHECK_NE(0, payments::GetBillingCustomerId(
personal_data_manager_, payments_client_->GetPrefService()));
detected_values |=
CreditCardSaveManager::DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT;
return detected_values;
}
void LocalCardMigrationManager::GetMigratableCreditCards() {
std::vector<CreditCard*> local_credit_cards =
personal_data_manager_->GetLocalCreditCards();
// Empty previous state.
migratable_credit_cards_.clear();
// Initialize the local credit card list and queue for showing and uploading.
for (CreditCard* credit_card : local_credit_cards) {
// If the card is valid (has a valid card number, expiration date, and is
// not expired) and is not a server card, add it to the list of migratable
// cards.
if (credit_card->IsValid() &&
!personal_data_manager_->IsServerCard(credit_card)) {
migratable_credit_cards_.push_back(MigratableCreditCard(*credit_card));
}
}
}
} // namespace autofill