blob: dcc082a039a284c9de8ec4499cdce9d33283dbb3 [file] [log] [blame]
// Copyright 2015 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/payments/payments_client.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/account_info_getter.h"
#include "components/autofill/core/browser/autofill_data_model.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/local_card_migration_manager.h"
#include "components/autofill/core/browser/payments/payments_request.h"
#include "components/autofill/core/browser/payments/payments_service_url.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/variations/net/variations_http_headers.h"
#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace autofill {
namespace payments {
namespace {
const char kUnmaskCardRequestPath[] =
"payments/apis-secure/creditcardservice/getrealpan?s7e_suffix=chromewallet";
const char kUnmaskCardRequestFormat[] =
"requestContentType=application/json; charset=utf-8&request=%s"
"&s7e_13_cvc=%s";
const char kGetUploadDetailsRequestPath[] =
"payments/apis/chromepaymentsservice/getdetailsforsavecard";
const char kUploadCardRequestPath[] =
"payments/apis-secure/chromepaymentsservice/savecard"
"?s7e_suffix=chromewallet";
const char kUploadCardRequestFormat[] =
"requestContentType=application/json; charset=utf-8&request=%s"
"&s7e_1_pan=%s&s7e_13_cvc=%s";
const char kUploadCardRequestFormatWithoutCvc[] =
"requestContentType=application/json; charset=utf-8&request=%s"
"&s7e_1_pan=%s";
const char kMigrateCardsRequestPath[] =
"payments/apis-secure/chromepaymentsservice/migratecards"
"?s7e_suffix=chromewallet";
const char kMigrateCardsRequestFormat[] =
"requestContentType=application/json; charset=utf-8&request=%s";
const char kTokenFetchId[] = "wallet_client";
const char kPaymentsOAuth2Scope[] =
"https://www.googleapis.com/auth/wallet.chrome";
GURL GetRequestUrl(const std::string& path) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch("sync-url")) {
if (IsPaymentsProductionEnabled()) {
LOG(ERROR) << "You are using production Payments but you specified a "
"--sync-url. You likely want to disable the sync sandbox "
"or switch to sandbox Payments. Both are controlled in "
"about:flags.";
}
} else if (!IsPaymentsProductionEnabled()) {
LOG(ERROR) << "You are using sandbox Payments but you didn't specify a "
"--sync-url. You likely want to enable the sync sandbox "
"or switch to production Payments. Both are controlled in "
"about:flags.";
}
return GetBaseSecureUrl().Resolve(path);
}
base::DictionaryValue BuildCustomerContextDictionary(
int64_t external_customer_id) {
base::DictionaryValue customer_context;
customer_context.SetString("external_customer_id",
std::to_string(external_customer_id));
return customer_context;
}
base::DictionaryValue BuildRiskDictionary(
const std::string& encoded_risk_data) {
base::DictionaryValue risk_data;
#if defined(OS_IOS)
// Browser fingerprinting is not available on iOS. Instead, we generate
// RiskAdvisoryData.
risk_data.SetString("message_type", "RISK_ADVISORY_DATA");
risk_data.SetString("encoding_type", "BASE_64_URL");
#else
risk_data.SetString("message_type", "BROWSER_NATIVE_FINGERPRINTING");
risk_data.SetString("encoding_type", "BASE_64");
#endif
risk_data.SetString("value", encoded_risk_data);
return risk_data;
}
void SetStringIfNotEmpty(const AutofillDataModel& profile,
const ServerFieldType& type,
const std::string& app_locale,
const std::string& path,
base::DictionaryValue* dictionary) {
const base::string16 value = profile.GetInfo(AutofillType(type), app_locale);
if (!value.empty())
dictionary->SetString(path, value);
}
void AppendStringIfNotEmpty(const AutofillProfile& profile,
const ServerFieldType& type,
const std::string& app_locale,
base::ListValue* list) {
const base::string16 value = profile.GetInfo(type, app_locale);
if (!value.empty())
list->AppendString(value);
}
// Returns a dictionary with the structure expected by Payments RPCs, containing
// each of the fields in |profile|, formatted according to |app_locale|. If
// |include_non_location_data| is false, the name and phone number in |profile|
// are not included.
std::unique_ptr<base::DictionaryValue> BuildAddressDictionary(
const AutofillProfile& profile,
const std::string& app_locale,
bool include_non_location_data) {
std::unique_ptr<base::DictionaryValue> postal_address(
new base::DictionaryValue());
if (include_non_location_data) {
SetStringIfNotEmpty(profile, NAME_FULL, app_locale,
PaymentsClient::kRecipientName, postal_address.get());
}
std::unique_ptr<base::ListValue> address_lines(new base::ListValue());
AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE1, app_locale,
address_lines.get());
AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE2, app_locale,
address_lines.get());
AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE3, app_locale,
address_lines.get());
if (!address_lines->empty())
postal_address->Set("address_line", std::move(address_lines));
SetStringIfNotEmpty(profile, ADDRESS_HOME_CITY, app_locale, "locality_name",
postal_address.get());
SetStringIfNotEmpty(profile, ADDRESS_HOME_STATE, app_locale,
"administrative_area_name", postal_address.get());
SetStringIfNotEmpty(profile, ADDRESS_HOME_ZIP, app_locale,
"postal_code_number", postal_address.get());
// Use GetRawInfo to get a country code instead of the country name:
const base::string16 country_code = profile.GetRawInfo(ADDRESS_HOME_COUNTRY);
if (!country_code.empty())
postal_address->SetString("country_name_code", country_code);
std::unique_ptr<base::DictionaryValue> address(new base::DictionaryValue());
address->Set("postal_address", std::move(postal_address));
if (include_non_location_data) {
SetStringIfNotEmpty(profile, PHONE_HOME_WHOLE_NUMBER, app_locale,
PaymentsClient::kPhoneNumber, address.get());
}
return address;
}
// Returns a dictionary of the credit card with the structure expected by
// Payments RPCs, containing expiration month, expiration year and cardholder
// name (if any) fields in |credit_card|, formatted according to |app_locale|.
// |pan_field_name| is the field name for the encrypted pan. We use each credit
// card's guid as the unique id.
std::unique_ptr<base::DictionaryValue> BuildCreditCardDictionary(
const CreditCard& credit_card,
const std::string& app_locale,
const std::string& pan_field_name) {
std::unique_ptr<base::DictionaryValue> card(new base::DictionaryValue());
card->SetString("unique_id", credit_card.guid());
const base::string16 exp_month =
credit_card.GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), app_locale);
const base::string16 exp_year = credit_card.GetInfo(
AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
int value = 0;
if (base::StringToInt(exp_month, &value))
card->SetInteger("expiration_month", value);
if (base::StringToInt(exp_year, &value))
card->SetInteger("expiration_year", value);
SetStringIfNotEmpty(credit_card, CREDIT_CARD_NAME_FULL, app_locale,
"cardholder_name", card.get());
card->SetString("encrypted_pan", "__param:" + pan_field_name);
return card;
}
// Populates the list of active experiments that affect either the data sent in
// payments RPCs or whether the RPCs are sent or not.
void SetActiveExperiments(const std::vector<const char*>& active_experiments,
base::DictionaryValue* request_dict) {
if (active_experiments.empty())
return;
std::unique_ptr<base::ListValue> active_chrome_experiments(
std::make_unique<base::ListValue>());
for (const char* it : active_experiments)
active_chrome_experiments->AppendString(it);
request_dict->Set("active_chrome_experiments",
std::move(active_chrome_experiments));
}
class UnmaskCardRequest : public PaymentsRequest {
public:
UnmaskCardRequest(const PaymentsClient::UnmaskRequestDetails& request_details,
const bool full_sync_enabled,
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const std::string&)> callback)
: request_details_(request_details),
full_sync_enabled_(full_sync_enabled),
callback_(std::move(callback)) {
DCHECK(
CreditCard::MASKED_SERVER_CARD == request_details.card.record_type() ||
CreditCard::FULL_SERVER_CARD == request_details.card.record_type());
}
~UnmaskCardRequest() override {}
std::string GetRequestUrlPath() override { return kUnmaskCardRequestPath; }
std::string GetRequestContentType() override {
return "application/x-www-form-urlencoded";
}
std::string GetRequestContent() override {
base::DictionaryValue request_dict;
request_dict.SetString("encrypted_cvc", "__param:s7e_13_cvc");
request_dict.SetString("credit_card_id", request_details_.card.server_id());
request_dict.SetKey("risk_data_encoded",
BuildRiskDictionary(request_details_.risk_data));
std::unique_ptr<base::DictionaryValue> context(new base::DictionaryValue());
context->SetInteger("billable_service", kUnmaskCardBillableServiceNumber);
if (request_details_.billing_customer_number != 0) {
context->SetKey("customer_context",
BuildCustomerContextDictionary(
request_details_.billing_customer_number));
}
request_dict.Set("context", std::move(context));
if (ShouldUseActiveSignedInAccount()) {
std::unique_ptr<base::DictionaryValue> chrome_user_context(
new base::DictionaryValue());
chrome_user_context->SetBoolean("full_sync_enabled", full_sync_enabled_);
request_dict.Set("chrome_user_context", std::move(chrome_user_context));
}
int value = 0;
if (base::StringToInt(request_details_.user_response.exp_month, &value))
request_dict.SetInteger("expiration_month", value);
if (base::StringToInt(request_details_.user_response.exp_year, &value))
request_dict.SetInteger("expiration_year", value);
std::string json_request;
base::JSONWriter::Write(request_dict, &json_request);
std::string request_content = base::StringPrintf(
kUnmaskCardRequestFormat,
net::EscapeUrlEncodedData(json_request, true).c_str(),
net::EscapeUrlEncodedData(
base::UTF16ToASCII(request_details_.user_response.cvc), true)
.c_str());
VLOG(3) << "getrealpan request body: " << request_content;
return request_content;
}
void ParseResponse(std::unique_ptr<base::DictionaryValue> response) override {
response->GetString("pan", &real_pan_);
}
bool IsResponseComplete() override { return !real_pan_.empty(); }
void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
std::move(callback_).Run(result, real_pan_);
}
private:
PaymentsClient::UnmaskRequestDetails request_details_;
const bool full_sync_enabled_;
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const std::string&)>
callback_;
std::string real_pan_;
};
class GetUploadDetailsRequest : public PaymentsRequest {
public:
GetUploadDetailsRequest(
const std::vector<AutofillProfile>& addresses,
const int detected_values,
const std::vector<const char*>& active_experiments,
const bool full_sync_enabled,
const std::string& app_locale,
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const base::string16&,
std::unique_ptr<base::DictionaryValue>)> callback,
const int billable_service_number,
PaymentsClient::UploadCardSource upload_card_source)
: addresses_(addresses),
detected_values_(detected_values),
active_experiments_(active_experiments),
full_sync_enabled_(full_sync_enabled),
app_locale_(app_locale),
callback_(std::move(callback)),
billable_service_number_(billable_service_number),
upload_card_source_(upload_card_source) {}
~GetUploadDetailsRequest() override {}
std::string GetRequestUrlPath() override {
return kGetUploadDetailsRequestPath;
}
std::string GetRequestContentType() override { return "application/json"; }
std::string GetRequestContent() override {
base::DictionaryValue request_dict;
std::unique_ptr<base::DictionaryValue> context(new base::DictionaryValue());
context->SetString("language_code", app_locale_);
context->SetInteger("billable_service", billable_service_number_);
request_dict.Set("context", std::move(context));
if (ShouldUseActiveSignedInAccount()) {
std::unique_ptr<base::DictionaryValue> chrome_user_context(
new base::DictionaryValue());
chrome_user_context->SetBoolean("full_sync_enabled", full_sync_enabled_);
request_dict.Set("chrome_user_context", std::move(chrome_user_context));
}
std::unique_ptr<base::ListValue> addresses(new base::ListValue());
for (const AutofillProfile& profile : addresses_) {
// These addresses are used by Payments to (1) accurately determine the
// user's country in order to show the correct legal documents and (2) to
// verify that the addresses are valid for their purposes so that we don't
// offer save in a case where it would definitely fail (e.g. P.O. boxes if
// min address is not possible). The final parameter directs
// BuildAddressDictionary to omit names and phone numbers, which aren't
// useful for these purposes.
addresses->Append(BuildAddressDictionary(profile, app_locale_, false));
}
request_dict.Set("address", std::move(addresses));
// It's possible we may not have found name/address/CVC in the checkout
// flow. The detected_values_ bitmask tells Payments what *was* found, and
// Payments will decide if the provided data is enough to offer upload save.
request_dict.SetInteger("detected_values", detected_values_);
SetActiveExperiments(active_experiments_, &request_dict);
switch (upload_card_source_) {
case PaymentsClient::UploadCardSource::UNKNOWN_UPLOAD_CARD_SOURCE:
request_dict.SetString("upload_card_source",
"UNKNOWN_UPLOAD_CARD_SOURCE");
break;
case PaymentsClient::UploadCardSource::UPSTREAM_CHECKOUT_FLOW:
request_dict.SetString("upload_card_source", "UPSTREAM_CHECKOUT_FLOW");
break;
case PaymentsClient::UploadCardSource::UPSTREAM_SETTINGS_PAGE:
request_dict.SetString("upload_card_source", "UPSTREAM_SETTINGS_PAGE");
break;
case PaymentsClient::UploadCardSource::UPSTREAM_CARD_OCR:
request_dict.SetString("upload_card_source", "UPSTREAM_CARD_OCR");
break;
case PaymentsClient::UploadCardSource::LOCAL_CARD_MIGRATION_CHECKOUT_FLOW:
request_dict.SetString("upload_card_source",
"LOCAL_CARD_MIGRATION_CHECKOUT_FLOW");
break;
case PaymentsClient::UploadCardSource::LOCAL_CARD_MIGRATION_SETTINGS_PAGE:
request_dict.SetString("upload_card_source",
"LOCAL_CARD_MIGRATION_SETTINGS_PAGE");
break;
default:
NOTREACHED();
}
std::string request_content;
base::JSONWriter::Write(request_dict, &request_content);
VLOG(3) << "getdetailsforsavecard request body: " << request_content;
return request_content;
}
void ParseResponse(std::unique_ptr<base::DictionaryValue> response) override {
response->GetString("context_token", &context_token_);
base::DictionaryValue* unowned_legal_message;
if (response->GetDictionary("legal_message", &unowned_legal_message))
legal_message_ = unowned_legal_message->CreateDeepCopy();
}
bool IsResponseComplete() override {
return !context_token_.empty() && legal_message_;
}
void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
std::move(callback_).Run(result, context_token_, std::move(legal_message_));
}
private:
const std::vector<AutofillProfile> addresses_;
const int detected_values_;
const std::vector<const char*> active_experiments_;
const bool full_sync_enabled_;
std::string app_locale_;
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const base::string16&,
std::unique_ptr<base::DictionaryValue>)>
callback_;
base::string16 context_token_;
std::unique_ptr<base::DictionaryValue> legal_message_;
const int billable_service_number_;
PaymentsClient::UploadCardSource upload_card_source_;
};
class UploadCardRequest : public PaymentsRequest {
public:
UploadCardRequest(const PaymentsClient::UploadRequestDetails& request_details,
const bool full_sync_enabled,
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const std::string&)> callback)
: request_details_(request_details),
full_sync_enabled_(full_sync_enabled),
callback_(std::move(callback)) {}
~UploadCardRequest() override {}
std::string GetRequestUrlPath() override { return kUploadCardRequestPath; }
std::string GetRequestContentType() override {
return "application/x-www-form-urlencoded";
}
std::string GetRequestContent() override {
base::DictionaryValue request_dict;
request_dict.SetString("encrypted_pan", "__param:s7e_1_pan");
if (!request_details_.cvc.empty())
request_dict.SetString("encrypted_cvc", "__param:s7e_13_cvc");
request_dict.SetKey("risk_data_encoded",
BuildRiskDictionary(request_details_.risk_data));
const std::string& app_locale = request_details_.app_locale;
std::unique_ptr<base::DictionaryValue> context(new base::DictionaryValue());
context->SetString("language_code", app_locale);
context->SetInteger("billable_service", kUploadCardBillableServiceNumber);
if (request_details_.billing_customer_number != 0) {
context->SetKey("customer_context",
BuildCustomerContextDictionary(
request_details_.billing_customer_number));
}
request_dict.Set("context", std::move(context));
if (ShouldUseActiveSignedInAccount()) {
std::unique_ptr<base::DictionaryValue> chrome_user_context(
new base::DictionaryValue());
chrome_user_context->SetBoolean("full_sync_enabled", full_sync_enabled_);
request_dict.Set("chrome_user_context", std::move(chrome_user_context));
}
SetStringIfNotEmpty(request_details_.card, CREDIT_CARD_NAME_FULL,
app_locale, "cardholder_name", &request_dict);
std::unique_ptr<base::ListValue> addresses(new base::ListValue());
for (const AutofillProfile& profile : request_details_.profiles) {
addresses->Append(BuildAddressDictionary(profile, app_locale, true));
}
request_dict.Set("address", std::move(addresses));
request_dict.SetString("context_token", request_details_.context_token);
int value = 0;
const base::string16 exp_month = request_details_.card.GetInfo(
AutofillType(CREDIT_CARD_EXP_MONTH), app_locale);
const base::string16 exp_year = request_details_.card.GetInfo(
AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
if (base::StringToInt(exp_month, &value))
request_dict.SetInteger("expiration_month", value);
if (base::StringToInt(exp_year, &value))
request_dict.SetInteger("expiration_year", value);
SetActiveExperiments(request_details_.active_experiments, &request_dict);
const base::string16 pan = request_details_.card.GetInfo(
AutofillType(CREDIT_CARD_NUMBER), app_locale);
std::string json_request;
base::JSONWriter::Write(request_dict, &json_request);
std::string request_content;
if (request_details_.cvc.empty()) {
request_content = base::StringPrintf(
kUploadCardRequestFormatWithoutCvc,
net::EscapeUrlEncodedData(json_request, true).c_str(),
net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str());
} else {
request_content = base::StringPrintf(
kUploadCardRequestFormat,
net::EscapeUrlEncodedData(json_request, true).c_str(),
net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str(),
net::EscapeUrlEncodedData(base::UTF16ToASCII(request_details_.cvc),
true)
.c_str());
}
VLOG(3) << "savecard request body: " << request_content;
return request_content;
}
void ParseResponse(std::unique_ptr<base::DictionaryValue> response) override {
response->GetString("credit_card_id", &server_id_);
}
bool IsResponseComplete() override { return true; }
void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
std::move(callback_).Run(result, server_id_);
}
private:
const PaymentsClient::UploadRequestDetails request_details_;
const bool full_sync_enabled_;
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const std::string&)>
callback_;
std::string server_id_;
};
class MigrateCardsRequest : public PaymentsRequest {
public:
MigrateCardsRequest(
const PaymentsClient::MigrationRequestDetails& request_details,
const std::vector<MigratableCreditCard>& migratable_credit_cards,
const bool full_sync_enabled,
MigrateCardsCallback callback)
: request_details_(request_details),
migratable_credit_cards_(migratable_credit_cards),
full_sync_enabled_(full_sync_enabled),
callback_(std::move(callback)) {}
~MigrateCardsRequest() override {}
std::string GetRequestUrlPath() override { return kMigrateCardsRequestPath; }
std::string GetRequestContentType() override {
return "application/x-www-form-urlencoded";
}
// TODO(crbug.com/877281):Refactor DictionaryValue to base::Value
std::string GetRequestContent() override {
base::DictionaryValue request_dict;
request_dict.SetKey("risk_data_encoded",
BuildRiskDictionary(request_details_.risk_data));
const std::string& app_locale = request_details_.app_locale;
std::unique_ptr<base::DictionaryValue> context(new base::DictionaryValue());
context->SetString("language_code", app_locale);
context->SetInteger("billable_service", kMigrateCardsBillableServiceNumber);
if (request_details_.billing_customer_number != 0) {
context->SetKey("customer_context",
BuildCustomerContextDictionary(
request_details_.billing_customer_number));
}
request_dict.Set("context", std::move(context));
if (ShouldUseActiveSignedInAccount()) {
std::unique_ptr<base::DictionaryValue> chrome_user_context(
new base::DictionaryValue());
chrome_user_context->SetBoolean("full_sync_enabled", full_sync_enabled_);
request_dict.Set("chrome_user_context", std::move(chrome_user_context));
}
request_dict.SetString("context_token", request_details_.context_token);
std::string all_pans_data = std::string();
std::unique_ptr<base::ListValue> migrate_cards(new base::ListValue());
for (size_t index = 0; index < migratable_credit_cards_.size(); ++index) {
std::string pan_field_name = GetPanFieldName(index);
// Generate credit card dictionary.
migrate_cards->Append(BuildCreditCardDictionary(
migratable_credit_cards_[index].credit_card(), app_locale,
pan_field_name));
// Append pan data to the |all_pans_data|.
all_pans_data +=
GetAppendPan(migratable_credit_cards_[index].credit_card(),
app_locale, pan_field_name);
}
request_dict.Set("local_card", std::move(migrate_cards));
std::string json_request;
base::JSONWriter::Write(request_dict, &json_request);
std::string request_content = base::StringPrintf(
kMigrateCardsRequestFormat,
net::EscapeUrlEncodedData(json_request, true).c_str());
request_content += all_pans_data;
return request_content;
}
void ParseResponse(std::unique_ptr<base::DictionaryValue> response) override {
const base::ListValue* save_result_list = nullptr;
if (!response->GetList("save_result", &save_result_list))
return;
save_result_ =
std::make_unique<std::unordered_map<std::string, std::string>>();
for (size_t i = 0; i < save_result_list->GetSize(); ++i) {
const base::DictionaryValue* single_card_save_result;
if (save_result_list->GetDictionary(i, &single_card_save_result)) {
std::string unique_id;
single_card_save_result->GetString("unique_id", &unique_id);
std::string save_result;
single_card_save_result->GetString("status", &save_result);
save_result_->insert(std::make_pair(unique_id, save_result));
}
}
response->GetString("value_prop_display_text", &display_text_);
}
bool IsResponseComplete() override {
return !display_text_.empty() && save_result_;
}
void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
std::move(callback_).Run(result, std::move(save_result_), display_text_);
}
private:
// Return the pan field name for the encrypted pan based on the |index|.
std::string GetPanFieldName(const size_t& index) {
return "s7e_1_pan" + std::to_string(index);
}
// Return the formatted pan to append to the end of the request.
std::string GetAppendPan(const CreditCard& credit_card,
const std::string& app_locale,
const std::string& pan_field_name) {
const base::string16 pan =
credit_card.GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale);
std::string pan_str =
net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str();
std::string append_pan = "&" + pan_field_name + "=" + pan_str;
return append_pan;
}
const PaymentsClient::MigrationRequestDetails request_details_;
const std::vector<MigratableCreditCard>& migratable_credit_cards_;
const bool full_sync_enabled_;
MigrateCardsCallback callback_;
std::unique_ptr<std::unordered_map<std::string, std::string>> save_result_;
std::string display_text_;
};
} // namespace
const char PaymentsClient::kRecipientName[] = "recipient_name";
const char PaymentsClient::kPhoneNumber[] = "phone_number";
PaymentsClient::UnmaskRequestDetails::UnmaskRequestDetails() {}
PaymentsClient::UnmaskRequestDetails::UnmaskRequestDetails(
const UnmaskRequestDetails& other) = default;
PaymentsClient::UnmaskRequestDetails::~UnmaskRequestDetails() {}
PaymentsClient::UploadRequestDetails::UploadRequestDetails() {}
PaymentsClient::UploadRequestDetails::UploadRequestDetails(
const UploadRequestDetails& other) = default;
PaymentsClient::UploadRequestDetails::~UploadRequestDetails() {}
PaymentsClient::MigrationRequestDetails::MigrationRequestDetails() {}
PaymentsClient::MigrationRequestDetails::MigrationRequestDetails(
const MigrationRequestDetails& other) = default;
PaymentsClient::MigrationRequestDetails::~MigrationRequestDetails() {}
PaymentsClient::PaymentsClient(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* pref_service,
identity::IdentityManager* identity_manager,
AccountInfoGetter* account_info_getter,
bool is_off_the_record)
: url_loader_factory_(url_loader_factory),
pref_service_(pref_service),
identity_manager_(identity_manager),
account_info_getter_(account_info_getter),
is_off_the_record_(is_off_the_record),
has_retried_authorization_(false),
weak_ptr_factory_(this) {}
PaymentsClient::~PaymentsClient() {}
void PaymentsClient::Prepare() {
if (access_token_.empty())
StartTokenFetch(false);
}
PrefService* PaymentsClient::GetPrefService() const {
return pref_service_;
}
void PaymentsClient::UnmaskCard(
const PaymentsClient::UnmaskRequestDetails& request_details,
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const std::string&)> callback) {
IssueRequest(
std::make_unique<UnmaskCardRequest>(
request_details, account_info_getter_->IsSyncFeatureEnabled(),
std::move(callback)),
true);
}
void PaymentsClient::GetUploadDetails(
const std::vector<AutofillProfile>& addresses,
const int detected_values,
const std::vector<const char*>& active_experiments,
const std::string& app_locale,
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const base::string16&,
std::unique_ptr<base::DictionaryValue>)> callback,
const int billable_service_number,
UploadCardSource upload_card_source) {
IssueRequest(
std::make_unique<GetUploadDetailsRequest>(
addresses, detected_values, active_experiments,
account_info_getter_->IsSyncFeatureEnabled(), app_locale,
std::move(callback), billable_service_number, upload_card_source),
false);
}
void PaymentsClient::UploadCard(
const PaymentsClient::UploadRequestDetails& request_details,
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const std::string&)> callback) {
IssueRequest(
std::make_unique<UploadCardRequest>(
request_details, account_info_getter_->IsSyncFeatureEnabled(),
std::move(callback)),
true);
}
void PaymentsClient::MigrateCards(
const MigrationRequestDetails& request_details,
const std::vector<MigratableCreditCard>& migratable_credit_cards,
MigrateCardsCallback callback) {
IssueRequest(
std::make_unique<MigrateCardsRequest>(
request_details, migratable_credit_cards,
account_info_getter_->IsSyncFeatureEnabled(), std::move(callback)),
/*authenticate=*/true);
}
void PaymentsClient::CancelRequest() {
request_.reset();
resource_request_.reset();
simple_url_loader_.reset();
token_fetcher_.reset();
access_token_.clear();
has_retried_authorization_ = false;
}
void PaymentsClient::set_url_loader_factory_for_testing(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
url_loader_factory_ = std::move(url_loader_factory);
}
void PaymentsClient::IssueRequest(std::unique_ptr<PaymentsRequest> request,
bool authenticate) {
request_ = std::move(request);
has_retried_authorization_ = false;
InitializeResourceRequest();
if (!authenticate) {
StartRequest();
} else if (access_token_.empty()) {
StartTokenFetch(false);
} else {
SetOAuth2TokenAndStartRequest();
}
}
void PaymentsClient::InitializeResourceRequest() {
resource_request_ = std::make_unique<network::ResourceRequest>();
resource_request_->url = GetRequestUrl(request_->GetRequestUrlPath());
resource_request_->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DISABLE_CACHE;
resource_request_->method = "POST";
if (base::FeatureList::IsEnabled(
features::kAutofillSendExperimentIdsInPaymentsRPCs)) {
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
// User is always signed-in to be able to upload card to Google Payments.
variations::AppendVariationHeaders(
resource_request_->url,
is_off_the_record_ ? variations::InIncognito::kYes
: variations::InIncognito::kNo,
variations::SignedIn::kYes, &resource_request_->headers);
}
}
void PaymentsClient::OnSimpleLoaderComplete(
std::unique_ptr<std::string> response_body) {
int response_code = -1;
if (simple_url_loader_->ResponseInfo() &&
simple_url_loader_->ResponseInfo()->headers) {
response_code =
simple_url_loader_->ResponseInfo()->headers->response_code();
}
std::string data;
if (response_body)
data = std::move(*response_body);
OnSimpleLoaderCompleteInternal(response_code, data);
}
void PaymentsClient::OnSimpleLoaderCompleteInternal(int response_code,
const std::string& data) {
std::unique_ptr<base::DictionaryValue> response_dict;
VLOG(2) << "Got data: " << data;
AutofillClient::PaymentsRpcResult result = AutofillClient::SUCCESS;
switch (response_code) {
// Valid response.
case net::HTTP_OK: {
std::string error_code;
std::unique_ptr<base::Value> message_value = base::JSONReader::Read(data);
if (message_value.get() && message_value->is_dict()) {
response_dict.reset(
static_cast<base::DictionaryValue*>(message_value.release()));
response_dict->GetString("error.code", &error_code);
request_->ParseResponse(std::move(response_dict));
}
if (base::LowerCaseEqualsASCII(error_code, "internal"))
result = AutofillClient::TRY_AGAIN_FAILURE;
else if (!error_code.empty() || !request_->IsResponseComplete())
result = AutofillClient::PERMANENT_FAILURE;
break;
}
case net::HTTP_UNAUTHORIZED: {
if (has_retried_authorization_) {
result = AutofillClient::PERMANENT_FAILURE;
break;
}
has_retried_authorization_ = true;
InitializeResourceRequest();
StartTokenFetch(true);
return;
}
// TODO(estade): is this actually how network connectivity issues are
// reported?
case net::HTTP_REQUEST_TIMEOUT: {
result = AutofillClient::NETWORK_ERROR;
break;
}
// Handle anything else as a generic (permanent) failure.
default: {
result = AutofillClient::PERMANENT_FAILURE;
break;
}
}
if (result != AutofillClient::SUCCESS) {
VLOG(1) << "Payments returned error: " << response_code
<< " with data: " << data;
}
request_->RespondToDelegate(result);
}
void PaymentsClient::AccessTokenFetchFinished(
GoogleServiceAuthError error,
identity::AccessTokenInfo access_token_info) {
DCHECK(token_fetcher_);
token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
AccessTokenError(error);
return;
}
access_token_ = access_token_info.token;
if (resource_request_)
SetOAuth2TokenAndStartRequest();
}
void PaymentsClient::AccessTokenError(const GoogleServiceAuthError& error) {
VLOG(1) << "Unhandled OAuth2 error: " << error.ToString();
if (simple_url_loader_)
simple_url_loader_.reset();
if (request_)
request_->RespondToDelegate(AutofillClient::PERMANENT_FAILURE);
}
void PaymentsClient::StartTokenFetch(bool invalidate_old) {
// We're still waiting for the last request to come back.
if (!invalidate_old && token_fetcher_)
return;
DCHECK(account_info_getter_);
identity::ScopeSet payments_scopes;
payments_scopes.insert(kPaymentsOAuth2Scope);
std::string account_id =
account_info_getter_->GetAccountInfoForPaymentsServer().account_id;
if (invalidate_old) {
DCHECK(!access_token_.empty());
identity_manager_->RemoveAccessTokenFromCache(account_id, payments_scopes,
access_token_);
}
access_token_.clear();
token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForAccount(
account_id, kTokenFetchId, payments_scopes,
base::BindOnce(&PaymentsClient::AccessTokenFetchFinished,
base::Unretained(this)),
identity::AccessTokenFetcher::Mode::kImmediate);
}
void PaymentsClient::SetOAuth2TokenAndStartRequest() {
DCHECK(resource_request_);
resource_request_->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
std::string("Bearer ") + access_token_);
StartRequest();
}
void PaymentsClient::StartRequest() {
DCHECK(resource_request_);
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("payments_sync_cards", R"(
semantics {
sender: "Payments"
description:
"This service communicates with Google Payments servers to upload "
"(save) or receive the user's credit card info."
trigger:
"Requests are triggered by a user action, such as selecting a "
"masked server card from Chromium's credit card autofill dropdown, "
"submitting a form which has credit card information, or accepting "
"the prompt to save a credit card to Payments servers."
data:
"In case of save, a protocol buffer containing relevant address "
"and credit card information which should be saved in Google "
"Payments servers, along with user credentials. In case of load, a "
"protocol buffer containing the id of the credit card to unmask, "
"an encrypted cvc value, an optional updated card expiration date, "
"and user credentials."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"Users can enable or disable this feature in Chromium settings by "
"toggling 'Credit cards and addresses using Google Payments', "
"under 'Advanced sync settings...'. This feature is enabled by "
"default."
chrome_policy {
AutoFillEnabled {
policy_options {mode: MANDATORY}
AutoFillEnabled: false
}
}
})");
// TODO(https://crbug.com/808498): Re-add data use measurement once
// SimpleURLLoader supports it.
// ID=data_use_measurement::DataUseUserData::AUTOFILL
simple_url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request_), traffic_annotation);
simple_url_loader_->AttachStringForUpload(request_->GetRequestContent(),
request_->GetRequestContentType());
simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_.get(),
base::BindOnce(&PaymentsClient::OnSimpleLoaderComplete,
base::Unretained(this)));
}
} // namespace payments
} // namespace autofill