blob: 59e10c1ad083e92c66161cc62be59b55071b8f9b [file] [log] [blame]
// Copyright 2013 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/content/browser/wallet/wallet_client.h"
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "components/autofill/browser/autofill_metrics.h"
#include "components/autofill/content/browser/wallet/instrument.h"
#include "components/autofill/content/browser/wallet/wallet_address.h"
#include "components/autofill/content/browser/wallet/wallet_client_delegate.h"
#include "components/autofill/content/browser/wallet/wallet_items.h"
#include "components/autofill/content/browser/wallet/wallet_service_url.h"
#include "crypto/random.h"
#include "google_apis/google_api_keys.h"
#include "net/http/http_status_code.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request_context_getter.h"
namespace autofill {
namespace wallet {
namespace {
const char kJsonMimeType[] = "application/json";
const size_t kOneTimePadLength = 6;
std::string AutocheckoutStatusToString(AutocheckoutStatus status) {
switch (status) {
case MISSING_FIELDMAPPING:
return "MISSING_FIELDMAPPING";
case MISSING_ADVANCE:
return "MISSING_ADVANCE";
case CANNOT_PROCEED:
return "CANNOT_PROCEED";
case SUCCESS:
// SUCCESS cannot be sent to the server as it will result in a failure.
NOTREACHED();
return "ERROR";
}
NOTREACHED();
return "NOT_POSSIBLE";
}
std::string DialogTypeToFeatureString(autofill::DialogType dialog_type) {
switch (dialog_type) {
case DIALOG_TYPE_REQUEST_AUTOCOMPLETE:
return "REQUEST_AUTOCOMPLETE";
case DIALOG_TYPE_AUTOCHECKOUT:
return "AUTOCHECKOUT";
}
NOTREACHED();
return "NOT_POSSIBLE";
}
std::string RiskCapabilityToString(
WalletClient::RiskCapability risk_capability) {
switch (risk_capability) {
case WalletClient::RELOGIN:
return "RELOGIN";
case WalletClient::VERIFY_CVC:
return "VERIFY_CVC";
}
NOTREACHED();
return "NOT_POSSIBLE";
}
WalletClient::ErrorType StringToErrorType(const std::string& error_type) {
std::string trimmed;
TrimWhitespaceASCII(error_type,
TRIM_ALL,
&trimmed);
if (LowerCaseEqualsASCII(trimmed, "buyer_account_error"))
return WalletClient::BUYER_ACCOUNT_ERROR;
if (LowerCaseEqualsASCII(trimmed, "internal_error"))
return WalletClient::INTERNAL_ERROR;
if (LowerCaseEqualsASCII(trimmed, "invalid_params"))
return WalletClient::INVALID_PARAMS;
if (LowerCaseEqualsASCII(trimmed, "service_unavailable"))
return WalletClient::SERVICE_UNAVAILABLE;
if (LowerCaseEqualsASCII(trimmed, "spending_limit_exceeded"))
return WalletClient::SPENDING_LIMIT_EXCEEDED;
if (LowerCaseEqualsASCII(trimmed, "unsupported_api_version"))
return WalletClient::UNSUPPORTED_API_VERSION;
return WalletClient::UNKNOWN_ERROR;
}
// Gets and parses required actions from a SaveToWallet response. Returns
// false if any unknown required actions are seen and true otherwise.
void GetRequiredActionsForSaveToWallet(
const base::DictionaryValue& dict,
std::vector<RequiredAction>* required_actions) {
const base::ListValue* required_action_list;
if (!dict.GetList("required_action", &required_action_list))
return;
for (size_t i = 0; i < required_action_list->GetSize(); ++i) {
std::string action_string;
if (required_action_list->GetString(i, &action_string)) {
RequiredAction action = ParseRequiredActionFromString(action_string);
if (!ActionAppliesToSaveToWallet(action)) {
DLOG(ERROR) << "Response from Google wallet with bad required action:"
" \"" << action_string << "\"";
required_actions->clear();
return;
}
required_actions->push_back(action);
}
}
}
// Converts the |error_type| to the corresponding value from the stable UMA
// metric enumeration.
AutofillMetrics::WalletErrorMetric ErrorTypeToUmaMetric(
WalletClient::ErrorType error_type) {
switch (error_type) {
case WalletClient::BAD_REQUEST:
return AutofillMetrics::WALLET_BAD_REQUEST;
case WalletClient::BUYER_ACCOUNT_ERROR:
return AutofillMetrics::WALLET_BUYER_ACCOUNT_ERROR;
case WalletClient::INTERNAL_ERROR:
return AutofillMetrics::WALLET_INTERNAL_ERROR;
case WalletClient::INVALID_PARAMS:
return AutofillMetrics::WALLET_INVALID_PARAMS;
case WalletClient::SERVICE_UNAVAILABLE:
return AutofillMetrics::WALLET_SERVICE_UNAVAILABLE;
case WalletClient::SPENDING_LIMIT_EXCEEDED:
return AutofillMetrics::WALLET_SPENDING_LIMIT_EXCEEDED;
case WalletClient::UNSUPPORTED_API_VERSION:
return AutofillMetrics::WALLET_UNSUPPORTED_API_VERSION;
case WalletClient::UNKNOWN_ERROR:
return AutofillMetrics::WALLET_UNKNOWN_ERROR;
}
NOTREACHED();
return AutofillMetrics::WALLET_UNKNOWN_ERROR;
}
// Converts the |required_action| to the corresponding value from the stable UMA
// metric enumeration.
AutofillMetrics::WalletRequiredActionMetric RequiredActionToUmaMetric(
RequiredAction required_action) {
switch (required_action) {
case UNKNOWN_TYPE:
return AutofillMetrics::UNKNOWN_REQUIRED_ACTION;
case CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS:
return AutofillMetrics::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS;
case SETUP_WALLET:
return AutofillMetrics::SETUP_WALLET;
case ACCEPT_TOS:
return AutofillMetrics::ACCEPT_TOS;
case GAIA_AUTH:
return AutofillMetrics::GAIA_AUTH;
case UPDATE_EXPIRATION_DATE:
return AutofillMetrics::UPDATE_EXPIRATION_DATE;
case UPGRADE_MIN_ADDRESS:
return AutofillMetrics::UPGRADE_MIN_ADDRESS;
case INVALID_FORM_FIELD:
return AutofillMetrics::INVALID_FORM_FIELD;
case VERIFY_CVV:
return AutofillMetrics::VERIFY_CVV;
case PASSIVE_GAIA_AUTH:
return AutofillMetrics::PASSIVE_GAIA_AUTH;
case REQUIRE_PHONE_NUMBER:
return AutofillMetrics::REQUIRE_PHONE_NUMBER;
}
NOTREACHED();
return AutofillMetrics::UNKNOWN_REQUIRED_ACTION;
}
// Keys for JSON communication with the Online Wallet server.
const char kAcceptedLegalDocumentKey[] = "accepted_legal_document";
const char kApiKeyKey[] = "api_key";
const char kAuthResultKey[] = "auth_result";
const char kEncryptedOtpKey[] = "encrypted_otp";
const char kErrorTypeKey[] = "wallet_error.error_type";
const char kFeatureKey[] = "feature";
const char kGoogleTransactionIdKey[] = "google_transaction_id";
const char kInstrumentIdKey[] = "instrument_id";
const char kInstrumentKey[] = "instrument";
const char kInstrumentEscrowHandleKey[] = "instrument_escrow_handle";
const char kInstrumentExpMonthKey[] = "instrument.credit_card.exp_month";
const char kInstrumentExpYearKey[] = "instrument.credit_card.exp_year";
const char kInstrumentType[] = "instrument.type";
const char kInstrumentPhoneNumberKey[] = "instrument_phone_number";
const char kMerchantDomainKey[] = "merchant_domain";
const char kReasonKey[] = "reason";
const char kRiskCapabilitiesKey[] = "supported_risk_challenge";
const char kRiskParamsKey[] = "risk_params";
const char kSelectedAddressIdKey[] = "selected_address_id";
const char kSelectedInstrumentIdKey[] = "selected_instrument_id";
const char kSessionMaterialKey[] = "session_material";
const char kShippingAddressIdKey[] = "shipping_address_id";
const char kShippingAddressKey[] = "shipping_address";
const char kSuccessKey[] = "success";
const char kUpgradedBillingAddressKey[] = "upgraded_billing_address";
const char kUpgradedInstrumentIdKey[] = "upgraded_instrument_id";
} // namespace
WalletClient::FullWalletRequest::FullWalletRequest(
const std::string& instrument_id,
const std::string& address_id,
const GURL& source_url,
const std::string& google_transaction_id,
const std::vector<RiskCapability> risk_capabilities)
: instrument_id(instrument_id),
address_id(address_id),
source_url(source_url),
google_transaction_id(google_transaction_id),
risk_capabilities(risk_capabilities) {}
WalletClient::FullWalletRequest::~FullWalletRequest() {}
WalletClient::UpdateInstrumentRequest::UpdateInstrumentRequest(
const std::string& instrument_id,
const GURL& source_url)
: instrument_id(instrument_id),
expiration_month(0),
expiration_year(0),
source_url(source_url) {}
WalletClient::UpdateInstrumentRequest::~UpdateInstrumentRequest() {}
WalletClient::WalletClient(net::URLRequestContextGetter* context_getter,
WalletClientDelegate* delegate)
: context_getter_(context_getter),
delegate_(delegate),
request_type_(NO_PENDING_REQUEST),
one_time_pad_(kOneTimePadLength),
encryption_escrow_client_(context_getter, this) {
DCHECK(context_getter_.get());
DCHECK(delegate_);
}
WalletClient::~WalletClient() {}
void WalletClient::AcceptLegalDocuments(
const std::vector<WalletItems::LegalDocument*>& documents,
const std::string& google_transaction_id,
const GURL& source_url) {
if (documents.empty())
return;
std::vector<std::string> document_ids;
for (size_t i = 0; i < documents.size(); ++i) {
document_ids.push_back(documents[i]->id());
}
DoAcceptLegalDocuments(document_ids, google_transaction_id, source_url);
}
void WalletClient::AuthenticateInstrument(
const std::string& instrument_id,
const std::string& card_verification_number,
const std::string& obfuscated_gaia_id) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::AuthenticateInstrument,
base::Unretained(this),
instrument_id,
card_verification_number,
obfuscated_gaia_id));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
DCHECK(pending_request_body_.empty());
request_type_ = AUTHENTICATE_INSTRUMENT;
pending_request_body_.SetString(kApiKeyKey, google_apis::GetAPIKey());
pending_request_body_.SetString(kRiskParamsKey, delegate_->GetRiskData());
pending_request_body_.SetString(kInstrumentIdKey, instrument_id);
encryption_escrow_client_.EscrowCardVerificationNumber(
card_verification_number, obfuscated_gaia_id);
}
void WalletClient::GetFullWallet(const FullWalletRequest& full_wallet_request) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::GetFullWallet,
base::Unretained(this),
full_wallet_request));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
DCHECK(pending_request_body_.empty());
request_type_ = GET_FULL_WALLET;
pending_request_body_.SetString(kApiKeyKey, google_apis::GetAPIKey());
pending_request_body_.SetString(kRiskParamsKey, delegate_->GetRiskData());
pending_request_body_.SetString(kSelectedInstrumentIdKey,
full_wallet_request.instrument_id);
pending_request_body_.SetString(kSelectedAddressIdKey,
full_wallet_request.address_id);
pending_request_body_.SetString(
kMerchantDomainKey,
full_wallet_request.source_url.GetWithEmptyPath().spec());
pending_request_body_.SetString(kGoogleTransactionIdKey,
full_wallet_request.google_transaction_id);
pending_request_body_.SetString(
kFeatureKey,
DialogTypeToFeatureString(delegate_->GetDialogType()));
scoped_ptr<base::ListValue> risk_capabilities_list(new base::ListValue());
for (std::vector<RiskCapability>::const_iterator it =
full_wallet_request.risk_capabilities.begin();
it != full_wallet_request.risk_capabilities.end();
++it) {
risk_capabilities_list->AppendString(RiskCapabilityToString(*it));
}
pending_request_body_.Set(kRiskCapabilitiesKey,
risk_capabilities_list.release());
crypto::RandBytes(&(one_time_pad_[0]), one_time_pad_.size());
encryption_escrow_client_.EncryptOneTimePad(one_time_pad_);
}
void WalletClient::GetWalletItems(const GURL& source_url) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::GetWalletItems,
base::Unretained(this),
source_url));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
request_type_ = GET_WALLET_ITEMS;
base::DictionaryValue request_dict;
request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
request_dict.SetString(kMerchantDomainKey,
source_url.GetWithEmptyPath().spec());
std::string post_body;
base::JSONWriter::Write(&request_dict, &post_body);
MakeWalletRequest(GetGetWalletItemsUrl(), post_body);
}
void WalletClient::SaveAddress(const Address& shipping_address,
const GURL& source_url) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::SaveAddress,
base::Unretained(this),
shipping_address,
source_url));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
request_type_ = SAVE_ADDRESS;
base::DictionaryValue request_dict;
request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData());
request_dict.SetString(kMerchantDomainKey,
source_url.GetWithEmptyPath().spec());
request_dict.Set(kShippingAddressKey,
shipping_address.ToDictionaryWithID().release());
std::string post_body;
base::JSONWriter::Write(&request_dict, &post_body);
MakeWalletRequest(GetSaveToWalletUrl(), post_body);
}
void WalletClient::SaveInstrument(
const Instrument& instrument,
const std::string& obfuscated_gaia_id,
const GURL& source_url) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::SaveInstrument,
base::Unretained(this),
instrument,
obfuscated_gaia_id,
source_url));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
DCHECK(pending_request_body_.empty());
request_type_ = SAVE_INSTRUMENT;
pending_request_body_.SetString(kApiKeyKey, google_apis::GetAPIKey());
pending_request_body_.SetString(kRiskParamsKey, delegate_->GetRiskData());
pending_request_body_.SetString(kMerchantDomainKey,
source_url.GetWithEmptyPath().spec());
pending_request_body_.Set(kInstrumentKey,
instrument.ToDictionary().release());
pending_request_body_.SetString(kInstrumentPhoneNumberKey,
instrument.address().phone_number());
encryption_escrow_client_.EscrowInstrumentInformation(instrument,
obfuscated_gaia_id);
}
void WalletClient::SaveInstrumentAndAddress(
const Instrument& instrument,
const Address& address,
const std::string& obfuscated_gaia_id,
const GURL& source_url) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::SaveInstrumentAndAddress,
base::Unretained(this),
instrument,
address,
obfuscated_gaia_id,
source_url));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
DCHECK(pending_request_body_.empty());
request_type_ = SAVE_INSTRUMENT_AND_ADDRESS;
pending_request_body_.SetString(kApiKeyKey, google_apis::GetAPIKey());
pending_request_body_.SetString(kRiskParamsKey, delegate_->GetRiskData());
pending_request_body_.SetString(kMerchantDomainKey,
source_url.GetWithEmptyPath().spec());
pending_request_body_.Set(kInstrumentKey,
instrument.ToDictionary().release());
pending_request_body_.SetString(kInstrumentPhoneNumberKey,
instrument.address().phone_number());
pending_request_body_.Set(kShippingAddressKey,
address.ToDictionaryWithID().release());
encryption_escrow_client_.EscrowInstrumentInformation(instrument,
obfuscated_gaia_id);
}
void WalletClient::SendAutocheckoutStatus(
AutocheckoutStatus status,
const GURL& source_url,
const std::string& google_transaction_id) {
DVLOG(1) << "Sending Autocheckout Status: " << status
<< " for: " << source_url;
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::SendAutocheckoutStatus,
base::Unretained(this),
status,
source_url,
google_transaction_id));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
request_type_ = SEND_STATUS;
base::DictionaryValue request_dict;
request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
bool success = status == SUCCESS;
request_dict.SetBoolean(kSuccessKey, success);
request_dict.SetString(kMerchantDomainKey,
source_url.GetWithEmptyPath().spec());
if (!success)
request_dict.SetString(kReasonKey, AutocheckoutStatusToString(status));
request_dict.SetString(kGoogleTransactionIdKey, google_transaction_id);
std::string post_body;
base::JSONWriter::Write(&request_dict, &post_body);
MakeWalletRequest(GetSendStatusUrl(), post_body);
}
void WalletClient::UpdateAddress(const Address& address,
const GURL& source_url) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::UpdateAddress,
base::Unretained(this),
address,
source_url));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
request_type_ = UPDATE_ADDRESS;
base::DictionaryValue request_dict;
request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData());
request_dict.SetString(kMerchantDomainKey,
source_url.GetWithEmptyPath().spec());
request_dict.Set(kShippingAddressKey,
address.ToDictionaryWithID().release());
std::string post_body;
base::JSONWriter::Write(&request_dict, &post_body);
MakeWalletRequest(GetSaveToWalletUrl(), post_body);
}
void WalletClient::UpdateInstrument(
const UpdateInstrumentRequest& update_instrument_request,
scoped_ptr<Address> billing_address) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::UpdateInstrument,
base::Unretained(this),
update_instrument_request,
base::Passed(&billing_address)));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
DCHECK(pending_request_body_.empty());
DCHECK(update_instrument_request.card_verification_number.empty() ==
update_instrument_request.obfuscated_gaia_id.empty());
DCHECK(billing_address ||
(update_instrument_request.expiration_month > 0 &&
update_instrument_request.expiration_year > 0));
request_type_ = UPDATE_INSTRUMENT;
base::DictionaryValue* active_request_body;
base::DictionaryValue request_dict;
if (update_instrument_request.card_verification_number.empty())
active_request_body = &request_dict;
else
active_request_body = &pending_request_body_;
active_request_body->SetString(kApiKeyKey, google_apis::GetAPIKey());
active_request_body->SetString(kRiskParamsKey, delegate_->GetRiskData());
active_request_body->SetString(
kMerchantDomainKey,
update_instrument_request.source_url.GetWithEmptyPath().spec());
active_request_body->SetString(kUpgradedInstrumentIdKey,
update_instrument_request.instrument_id);
if (billing_address) {
active_request_body->SetString(kInstrumentPhoneNumberKey,
billing_address->phone_number());
active_request_body->Set(
kUpgradedBillingAddressKey,
billing_address->ToDictionaryWithoutID().release());
}
if (update_instrument_request.expiration_month > 0 &&
update_instrument_request.expiration_year > 0) {
DCHECK(!update_instrument_request.card_verification_number.empty());
active_request_body->SetInteger(
kInstrumentExpMonthKey,
update_instrument_request.expiration_month);
active_request_body->SetInteger(kInstrumentExpYearKey,
update_instrument_request.expiration_year);
}
if (active_request_body->HasKey(kInstrumentKey))
active_request_body->SetString(kInstrumentType, "CREDIT_CARD");
if (update_instrument_request.card_verification_number.empty()) {
std::string post_body;
base::JSONWriter::Write(active_request_body, &post_body);
MakeWalletRequest(GetSaveToWalletUrl(), post_body);
} else {
encryption_escrow_client_.EscrowCardVerificationNumber(
update_instrument_request.card_verification_number,
update_instrument_request.obfuscated_gaia_id);
}
}
bool WalletClient::HasRequestInProgress() const {
// |SaveInstrument*()| and |UpdateInstrument()| methods don't set |request_|
// until sensitive info has been escrowed, so this class is considered to have
// a request in progress if |encryption_escrow_client_| is working as well.
return request_ || encryption_escrow_client_.HasRequestInProgress();
}
void WalletClient::CancelRequests() {
encryption_escrow_client_.CancelRequest();
pending_request_body_.Clear();
request_.reset();
request_type_ = NO_PENDING_REQUEST;
while (!pending_requests_.empty()) {
pending_requests_.pop();
}
}
void WalletClient::DoAcceptLegalDocuments(
const std::vector<std::string>& document_ids,
const std::string& google_transaction_id,
const GURL& source_url) {
if (HasRequestInProgress()) {
pending_requests_.push(base::Bind(&WalletClient::DoAcceptLegalDocuments,
base::Unretained(this),
document_ids,
google_transaction_id,
source_url));
return;
}
DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
request_type_ = ACCEPT_LEGAL_DOCUMENTS;
base::DictionaryValue request_dict;
request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
request_dict.SetString(kGoogleTransactionIdKey, google_transaction_id);
request_dict.SetString(kMerchantDomainKey,
source_url.GetWithEmptyPath().spec());
scoped_ptr<base::ListValue> docs_list(new base::ListValue());
for (std::vector<std::string>::const_iterator it = document_ids.begin();
it != document_ids.end(); ++it) {
if (!it->empty())
docs_list->AppendString(*it);
}
request_dict.Set(kAcceptedLegalDocumentKey, docs_list.release());
std::string post_body;
base::JSONWriter::Write(&request_dict, &post_body);
MakeWalletRequest(GetAcceptLegalDocumentsUrl(), post_body);
}
void WalletClient::MakeWalletRequest(const GURL& url,
const std::string& post_body) {
DCHECK(!HasRequestInProgress());
request_.reset(net::URLFetcher::Create(
0, url, net::URLFetcher::POST, this));
request_->SetRequestContext(context_getter_.get());
DVLOG(1) << "Making request to " << url << " with post_body=" << post_body;
request_->SetUploadData(kJsonMimeType, post_body);
request_started_timestamp_ = base::Time::Now();
request_->Start();
delegate_->GetMetricLogger().LogWalletErrorMetric(
delegate_->GetDialogType(),
AutofillMetrics::WALLET_ERROR_BASELINE_ISSUED_REQUEST);
delegate_->GetMetricLogger().LogWalletRequiredActionMetric(
delegate_->GetDialogType(),
AutofillMetrics::WALLET_REQUIRED_ACTION_BASELINE_ISSUED_REQUEST);
}
// TODO(ahutter): Add manual retry logic if it's necessary.
void WalletClient::OnURLFetchComplete(
const net::URLFetcher* source) {
delegate_->GetMetricLogger().LogWalletApiCallDuration(
RequestTypeToUmaMetric(request_type_),
base::Time::Now() - request_started_timestamp_);
DCHECK_EQ(source, request_.get());
DVLOG(1) << "Got response from " << source->GetOriginalURL();
std::string data;
source->GetResponseAsString(&data);
DVLOG(1) << "Response body: " << data;
scoped_ptr<base::DictionaryValue> response_dict;
int response_code = source->GetResponseCode();
switch (response_code) {
// HTTP_BAD_REQUEST means the arguments are invalid. No point retrying.
case net::HTTP_BAD_REQUEST: {
request_type_ = NO_PENDING_REQUEST;
HandleWalletError(WalletClient::BAD_REQUEST);
return;
}
// HTTP_OK holds a valid response and HTTP_INTERNAL_SERVER_ERROR holds an
// error code and message for the user.
case net::HTTP_OK:
case net::HTTP_INTERNAL_SERVER_ERROR: {
scoped_ptr<Value> message_value(base::JSONReader::Read(data));
if (message_value.get() &&
message_value->IsType(Value::TYPE_DICTIONARY)) {
response_dict.reset(
static_cast<base::DictionaryValue*>(message_value.release()));
}
if (response_code == net::HTTP_INTERNAL_SERVER_ERROR) {
request_type_ = NO_PENDING_REQUEST;
std::string error_type;
if (!response_dict->GetString(kErrorTypeKey, &error_type)) {
HandleWalletError(WalletClient::UNKNOWN_ERROR);
return;
}
HandleWalletError(StringToErrorType(error_type));
return;
}
break;
}
// Anything else is an error.
default:
request_type_ = NO_PENDING_REQUEST;
HandleNetworkError(response_code);
return;
}
RequestType type = request_type_;
request_type_ = NO_PENDING_REQUEST;
if (!(type == ACCEPT_LEGAL_DOCUMENTS || type == SEND_STATUS) &&
!response_dict) {
HandleMalformedResponse();
return;
}
switch (type) {
case ACCEPT_LEGAL_DOCUMENTS:
delegate_->OnDidAcceptLegalDocuments();
break;
case AUTHENTICATE_INSTRUMENT: {
std::string auth_result;
if (response_dict->GetString(kAuthResultKey, &auth_result)) {
std::string trimmed;
TrimWhitespaceASCII(auth_result,
TRIM_ALL,
&trimmed);
delegate_->OnDidAuthenticateInstrument(
LowerCaseEqualsASCII(trimmed, "success"));
} else {
HandleMalformedResponse();
}
break;
}
case SEND_STATUS:
break;
case GET_FULL_WALLET: {
scoped_ptr<FullWallet> full_wallet(
FullWallet::CreateFullWallet(*response_dict));
if (full_wallet) {
full_wallet->set_one_time_pad(one_time_pad_);
LogRequiredActions(full_wallet->required_actions());
delegate_->OnDidGetFullWallet(full_wallet.Pass());
} else {
HandleMalformedResponse();
}
break;
}
case GET_WALLET_ITEMS: {
scoped_ptr<WalletItems> wallet_items(
WalletItems::CreateWalletItems(*response_dict));
if (wallet_items) {
LogRequiredActions(wallet_items->required_actions());
delegate_->OnDidGetWalletItems(wallet_items.Pass());
} else {
HandleMalformedResponse();
}
break;
}
case SAVE_ADDRESS: {
std::string shipping_address_id;
std::vector<RequiredAction> required_actions;
GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
if (response_dict->GetString(kShippingAddressIdKey,
&shipping_address_id) ||
!required_actions.empty()) {
LogRequiredActions(required_actions);
delegate_->OnDidSaveAddress(shipping_address_id, required_actions);
} else {
HandleMalformedResponse();
}
break;
}
case SAVE_INSTRUMENT: {
std::string instrument_id;
std::vector<RequiredAction> required_actions;
GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
if (response_dict->GetString(kInstrumentIdKey, &instrument_id) ||
!required_actions.empty()) {
LogRequiredActions(required_actions);
delegate_->OnDidSaveInstrument(instrument_id, required_actions);
} else {
HandleMalformedResponse();
}
break;
}
case SAVE_INSTRUMENT_AND_ADDRESS: {
std::string instrument_id;
response_dict->GetString(kInstrumentIdKey, &instrument_id);
std::string shipping_address_id;
response_dict->GetString(kShippingAddressIdKey,
&shipping_address_id);
std::vector<RequiredAction> required_actions;
GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
if ((!instrument_id.empty() && !shipping_address_id.empty()) ||
!required_actions.empty()) {
LogRequiredActions(required_actions);
delegate_->OnDidSaveInstrumentAndAddress(instrument_id,
shipping_address_id,
required_actions);
} else {
HandleMalformedResponse();
}
break;
}
case UPDATE_ADDRESS: {
std::string address_id;
std::vector<RequiredAction> required_actions;
GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
if (response_dict->GetString(kShippingAddressIdKey, &address_id) ||
!required_actions.empty()) {
LogRequiredActions(required_actions);
delegate_->OnDidUpdateAddress(address_id, required_actions);
} else {
HandleMalformedResponse();
}
break;
}
case UPDATE_INSTRUMENT: {
std::string instrument_id;
std::vector<RequiredAction> required_actions;
GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
if (response_dict->GetString(kInstrumentIdKey, &instrument_id) ||
!required_actions.empty()) {
LogRequiredActions(required_actions);
delegate_->OnDidUpdateInstrument(instrument_id, required_actions);
} else {
HandleMalformedResponse();
}
break;
}
case NO_PENDING_REQUEST:
NOTREACHED();
}
request_.reset();
StartNextPendingRequest();
}
void WalletClient::StartNextPendingRequest() {
if (pending_requests_.empty())
return;
base::Closure next_request = pending_requests_.front();
pending_requests_.pop();
next_request.Run();
}
void WalletClient::HandleMalformedResponse() {
// Called to inform exponential backoff logic of the error.
request_->ReceivedContentWasMalformed();
delegate_->OnMalformedResponse();
delegate_->GetMetricLogger().LogWalletErrorMetric(
delegate_->GetDialogType(), AutofillMetrics::WALLET_MALFORMED_RESPONSE);
}
void WalletClient::HandleNetworkError(int response_code) {
delegate_->OnNetworkError(response_code);
delegate_->GetMetricLogger().LogWalletErrorMetric(
delegate_->GetDialogType(), AutofillMetrics::WALLET_NETWORK_ERROR);
}
void WalletClient::HandleWalletError(WalletClient::ErrorType error_type) {
delegate_->OnWalletError(error_type);
delegate_->GetMetricLogger().LogWalletErrorMetric(
delegate_->GetDialogType(), ErrorTypeToUmaMetric(error_type));
}
void WalletClient::OnDidEncryptOneTimePad(
const std::string& encrypted_one_time_pad,
const std::string& session_material) {
DCHECK_EQ(GET_FULL_WALLET, request_type_);
pending_request_body_.SetString(kEncryptedOtpKey, encrypted_one_time_pad);
pending_request_body_.SetString(kSessionMaterialKey, session_material);
std::string post_body;
base::JSONWriter::Write(&pending_request_body_, &post_body);
pending_request_body_.Clear();
MakeWalletRequest(GetGetFullWalletUrl(), post_body);
}
void WalletClient::OnDidEscrowInstrumentInformation(
const std::string& escrow_handle) {
DCHECK(request_type_ == SAVE_INSTRUMENT ||
request_type_ == SAVE_INSTRUMENT_AND_ADDRESS);
pending_request_body_.SetString(kInstrumentEscrowHandleKey, escrow_handle);
std::string post_body;
base::JSONWriter::Write(&pending_request_body_, &post_body);
pending_request_body_.Clear();
MakeWalletRequest(GetSaveToWalletUrl(), post_body);
}
void WalletClient::OnDidEscrowCardVerificationNumber(
const std::string& escrow_handle) {
DCHECK(request_type_ == AUTHENTICATE_INSTRUMENT ||
request_type_ == UPDATE_INSTRUMENT);
pending_request_body_.SetString(kInstrumentEscrowHandleKey, escrow_handle);
std::string post_body;
base::JSONWriter::Write(&pending_request_body_, &post_body);
pending_request_body_.Clear();
if (request_type_ == AUTHENTICATE_INSTRUMENT)
MakeWalletRequest(GetAuthenticateInstrumentUrl(), post_body);
else
MakeWalletRequest(GetSaveToWalletUrl(), post_body);
}
void WalletClient::OnDidMakeRequest() {
delegate_->GetMetricLogger().LogWalletErrorMetric(
delegate_->GetDialogType(),
AutofillMetrics::WALLET_ERROR_BASELINE_ISSUED_REQUEST);
}
void WalletClient::OnNetworkError(int response_code) {
HandleNetworkError(response_code);
}
void WalletClient::OnMalformedResponse() {
delegate_->OnMalformedResponse();
delegate_->GetMetricLogger().LogWalletErrorMetric(
delegate_->GetDialogType(), AutofillMetrics::WALLET_MALFORMED_RESPONSE);
}
// Logs an UMA metric for each of the |required_actions|.
void WalletClient::LogRequiredActions(
const std::vector<RequiredAction>& required_actions) const {
for (size_t i = 0; i < required_actions.size(); ++i) {
delegate_->GetMetricLogger().LogWalletRequiredActionMetric(
delegate_->GetDialogType(),
RequiredActionToUmaMetric(required_actions[i]));
}
}
AutofillMetrics::WalletApiCallMetric WalletClient::RequestTypeToUmaMetric(
RequestType request_type) const {
switch (request_type) {
case ACCEPT_LEGAL_DOCUMENTS:
return AutofillMetrics::ACCEPT_LEGAL_DOCUMENTS;
case AUTHENTICATE_INSTRUMENT:
return AutofillMetrics::AUTHENTICATE_INSTRUMENT;
case GET_FULL_WALLET:
return AutofillMetrics::GET_FULL_WALLET;
case GET_WALLET_ITEMS:
return AutofillMetrics::GET_WALLET_ITEMS;
case SAVE_ADDRESS:
return AutofillMetrics::SAVE_ADDRESS;
case SAVE_INSTRUMENT:
return AutofillMetrics::SAVE_INSTRUMENT;
case SAVE_INSTRUMENT_AND_ADDRESS:
return AutofillMetrics::SAVE_INSTRUMENT_AND_ADDRESS;
case SEND_STATUS:
return AutofillMetrics::SEND_STATUS;
case UPDATE_ADDRESS:
return AutofillMetrics::UPDATE_ADDRESS;
case UPDATE_INSTRUMENT:
return AutofillMetrics::UPDATE_INSTRUMENT;
case NO_PENDING_REQUEST:
NOTREACHED();
return AutofillMetrics::UNKNOWN_API_CALL;
}
NOTREACHED();
return AutofillMetrics::UNKNOWN_API_CALL;
}
} // namespace wallet
} // namespace autofill