// 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/message_loop/message_loop.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 "components/autofill/content/browser/wallet/form_field_error.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 "components/autofill/core/browser/autofill_metrics.h" #include "crypto/random.h" #include "google_apis/google_api_keys.h" #include "net/base/escape.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 kFormEncodedMimeType[] = "application/x-www-form-urlencoded"; const char kJsonMimeType[] = "application/json"; const char kEscrowNewInstrumentFormat[] = "request_content_type=application/json&request=%s&cvn=%s&card_number=%s"; const char kEscrowCardVerificationNumberFormat[] = "request_content_type=application/json&request=%s&cvn=%s"; const char kGetFullWalletRequestFormat[] = "request_content_type=application/json&request=%s&otp=%s:%s"; const size_t kOneTimePadLength = 6; // The maximum number of bits in the one time pad that the server is willing to // accept. const size_t kMaxBits = 56; // The minimum number of bits in the one time pad that the server is willing to // accept. const size_t kMinBits = 40; 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, "unsupported_merchant")) return WalletClient::UNSUPPORTED_MERCHANT; 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, "unsupported_api_version")) return WalletClient::UNSUPPORTED_API_VERSION; return WalletClient::UNKNOWN_ERROR; } // Get the more specific WalletClient::ErrorType when the error is // |BUYER_ACCOUNT_ERROR|. WalletClient::ErrorType BuyerErrorStringToErrorType( const std::string& message_type_for_buyer) { std::string trimmed; TrimWhitespaceASCII(message_type_for_buyer, TRIM_ALL, &trimmed); if (LowerCaseEqualsASCII(trimmed, "bla_country_not_supported")) return WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED; if (LowerCaseEqualsASCII(trimmed, "buyer_kyc_error")) return WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS; return WalletClient::BUYER_ACCOUNT_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* 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); } } } void GetFormFieldErrors(const base::DictionaryValue& dict, std::vector* form_errors) { DCHECK(form_errors->empty()); const base::ListValue* form_errors_list; if (!dict.GetList("form_field_error", &form_errors_list)) return; for (size_t i = 0; i < form_errors_list->GetSize(); ++i) { const base::DictionaryValue* dictionary; if (form_errors_list->GetDictionary(i, &dictionary)) form_errors->push_back(FormFieldError::CreateFormFieldError(*dictionary)); } } // 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_LEGAL_ADDRESS_NOT_SUPPORTED: return AutofillMetrics::WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED; 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::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS: return AutofillMetrics::WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS; case WalletClient::SERVICE_UNAVAILABLE: return AutofillMetrics::WALLET_SERVICE_UNAVAILABLE; case WalletClient::UNSUPPORTED_API_VERSION: return AutofillMetrics::WALLET_UNSUPPORTED_API_VERSION; case WalletClient::UNSUPPORTED_MERCHANT: return AutofillMetrics::WALLET_UNSUPPORTED_MERCHANT; case WalletClient::MALFORMED_RESPONSE: return AutofillMetrics::WALLET_MALFORMED_RESPONSE; case WalletClient::NETWORK_ERROR: return AutofillMetrics::WALLET_NETWORK_ERROR; 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 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 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 kMessageTypeForBuyerKey[] = "wallet_error.message_type_for_buyer"; const char kNewWalletUser[] = "new_wallet_user"; const char kPhoneNumberRequired[] = "phone_number_required"; 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 kShippingAddressIdKey[] = "shipping_address_id"; const char kShippingAddressKey[] = "shipping_address"; const char kShippingAddressRequired[] = "shipping_address_required"; const char kUpgradedBillingAddressKey[] = "upgraded_billing_address"; const char kUpgradedInstrumentIdKey[] = "upgraded_instrument_id"; const char kUseMinimalAddresses[] = "use_minimal_addresses"; } // namespace WalletClient::FullWalletRequest::FullWalletRequest( const std::string& instrument_id, const std::string& address_id, const std::string& google_transaction_id, const std::vector risk_capabilities, bool new_wallet_user) : instrument_id(instrument_id), address_id(address_id), google_transaction_id(google_transaction_id), risk_capabilities(risk_capabilities), new_wallet_user(new_wallet_user) {} WalletClient::FullWalletRequest::~FullWalletRequest() {} WalletClient::WalletClient(net::URLRequestContextGetter* context_getter, WalletClientDelegate* delegate, const GURL& source_url) : context_getter_(context_getter), delegate_(delegate), user_index_(0U), source_url_(source_url), request_type_(NO_PENDING_REQUEST), one_time_pad_(kOneTimePadLength), weak_ptr_factory_(this) { DCHECK(context_getter_.get()); DCHECK(delegate_); } WalletClient::~WalletClient() {} void WalletClient::AcceptLegalDocuments( const std::vector& documents, const std::string& google_transaction_id) { if (documents.empty()) return; std::vector document_ids; for (size_t i = 0; i < documents.size(); ++i) { document_ids.push_back(documents[i]->id()); } DoAcceptLegalDocuments(document_ids, google_transaction_id); } void WalletClient::AuthenticateInstrument( const std::string& instrument_id, const std::string& card_verification_number) { base::DictionaryValue request_dict; request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey()); request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData()); request_dict.SetString(kInstrumentIdKey, instrument_id); std::string json_payload; base::JSONWriter::Write(&request_dict, &json_payload); std::string escaped_card_verification_number = net::EscapeUrlEncodedData( card_verification_number, true); std::string post_body = base::StringPrintf( kEscrowCardVerificationNumberFormat, net::EscapeUrlEncodedData(json_payload, true).c_str(), escaped_card_verification_number.c_str()); MakeWalletRequest(GetAuthenticateInstrumentUrl(user_index_), post_body, kFormEncodedMimeType, AUTHENTICATE_INSTRUMENT); } void WalletClient::GetFullWallet(const FullWalletRequest& full_wallet_request) { base::DictionaryValue request_dict; request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey()); request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData()); request_dict.SetBoolean(kUseMinimalAddresses, false); request_dict.SetBoolean(kPhoneNumberRequired, true); request_dict.SetBoolean(kNewWalletUser, full_wallet_request.new_wallet_user); request_dict.SetString(kSelectedInstrumentIdKey, full_wallet_request.instrument_id); request_dict.SetString(kSelectedAddressIdKey, full_wallet_request.address_id); request_dict.SetString( kMerchantDomainKey, source_url_.GetWithEmptyPath().spec()); request_dict.SetString(kGoogleTransactionIdKey, full_wallet_request.google_transaction_id); request_dict.SetString(kFeatureKey, "REQUEST_AUTOCOMPLETE"); scoped_ptr risk_capabilities_list(new base::ListValue()); for (std::vector::const_iterator it = full_wallet_request.risk_capabilities.begin(); it != full_wallet_request.risk_capabilities.end(); ++it) { risk_capabilities_list->AppendString(RiskCapabilityToString(*it)); } request_dict.Set(kRiskCapabilitiesKey, risk_capabilities_list.release()); std::string json_payload; base::JSONWriter::Write(&request_dict, &json_payload); crypto::RandBytes(&(one_time_pad_[0]), one_time_pad_.size()); size_t num_bits = one_time_pad_.size() * 8; DCHECK_LE(num_bits, kMaxBits); DCHECK_GE(num_bits, kMinBits); std::string post_body = base::StringPrintf( kGetFullWalletRequestFormat, net::EscapeUrlEncodedData(json_payload, true).c_str(), base::HexEncode(&num_bits, 1).c_str(), base::HexEncode(&(one_time_pad_[0]), one_time_pad_.size()).c_str()); MakeWalletRequest(GetGetFullWalletUrl(user_index_), post_body, kFormEncodedMimeType, GET_FULL_WALLET); } void WalletClient::SaveToWallet( scoped_ptr instrument, scoped_ptr
address, const WalletItems::MaskedInstrument* reference_instrument, const Address* reference_address) { DCHECK(instrument || 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.SetBoolean(kUseMinimalAddresses, false); request_dict.SetBoolean(kPhoneNumberRequired, true); std::string primary_account_number; std::string card_verification_number; if (instrument) { primary_account_number = net::EscapeUrlEncodedData( UTF16ToUTF8(instrument->primary_account_number()), true); card_verification_number = net::EscapeUrlEncodedData( UTF16ToUTF8(instrument->card_verification_number()), true); if (!reference_instrument) { request_dict.Set(kInstrumentKey, instrument->ToDictionary().release()); request_dict.SetString(kInstrumentPhoneNumberKey, instrument->address()->phone_number()); } else { DCHECK(!reference_instrument->object_id().empty()); int new_month = instrument->expiration_month(); int new_year = instrument->expiration_year(); bool expiration_date_changed = new_month != reference_instrument->expiration_month() || new_year != reference_instrument->expiration_year(); DCHECK(instrument->address() || expiration_date_changed); request_dict.SetString(kUpgradedInstrumentIdKey, reference_instrument->object_id()); if (instrument->address()) { request_dict.SetString(kInstrumentPhoneNumberKey, instrument->address()->phone_number()); request_dict.Set( kUpgradedBillingAddressKey, instrument->address()->ToDictionaryWithoutID().release()); } if (expiration_date_changed) { // Updating expiration date requires a CVC. DCHECK(!instrument->card_verification_number().empty()); request_dict.SetInteger(kInstrumentExpMonthKey, instrument->expiration_month()); request_dict.SetInteger(kInstrumentExpYearKey, instrument->expiration_year()); } if (request_dict.HasKey(kInstrumentKey)) request_dict.SetString(kInstrumentType, "CREDIT_CARD"); } } if (address) { if (reference_address) { address->set_object_id(reference_address->object_id()); DCHECK(!address->object_id().empty()); } request_dict.Set(kShippingAddressKey, address->ToDictionaryWithID().release()); } std::string json_payload; base::JSONWriter::Write(&request_dict, &json_payload); if (!card_verification_number.empty()) { std::string post_body; if (!primary_account_number.empty()) { post_body = base::StringPrintf( kEscrowNewInstrumentFormat, net::EscapeUrlEncodedData(json_payload, true).c_str(), card_verification_number.c_str(), primary_account_number.c_str()); } else { post_body = base::StringPrintf( kEscrowCardVerificationNumberFormat, net::EscapeUrlEncodedData(json_payload, true).c_str(), card_verification_number.c_str()); } MakeWalletRequest(GetSaveToWalletUrl(user_index_), post_body, kFormEncodedMimeType, SAVE_TO_WALLET); } else { MakeWalletRequest(GetSaveToWalletNoEscrowUrl(user_index_), json_payload, kJsonMimeType, SAVE_TO_WALLET); } } void WalletClient::GetWalletItems() { base::DictionaryValue request_dict; request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey()); request_dict.SetString(kMerchantDomainKey, source_url_.GetWithEmptyPath().spec()); request_dict.SetBoolean(kShippingAddressRequired, delegate_->IsShippingAddressRequired()); request_dict.SetBoolean(kUseMinimalAddresses, false); request_dict.SetBoolean(kPhoneNumberRequired, true); std::string post_body; base::JSONWriter::Write(&request_dict, &post_body); MakeWalletRequest(GetGetWalletItemsUrl(user_index_), post_body, kJsonMimeType, GET_WALLET_ITEMS); } bool WalletClient::HasRequestInProgress() const { return request_; } void WalletClient::CancelRequests() { request_.reset(); request_type_ = NO_PENDING_REQUEST; while (!pending_requests_.empty()) { pending_requests_.pop(); } } void WalletClient::SetUserIndex(size_t user_index) { CancelRequests(); user_index_ = user_index; } void WalletClient::DoAcceptLegalDocuments( const std::vector& document_ids, const std::string& google_transaction_id) { 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 docs_list(new base::ListValue()); for (std::vector::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(user_index_), post_body, kJsonMimeType, ACCEPT_LEGAL_DOCUMENTS); } void WalletClient::MakeWalletRequest(const GURL& url, const std::string& post_body, const std::string& mime_type, RequestType request_type) { if (HasRequestInProgress()) { pending_requests_.push(base::Bind(&WalletClient::MakeWalletRequest, base::Unretained(this), url, post_body, mime_type, request_type)); return; } DCHECK_EQ(request_type_, NO_PENDING_REQUEST); request_type_ = request_type; 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(mime_type, post_body); request_->AddExtraRequestHeader("Authorization: GoogleLogin auth=" + delegate_->GetWalletCookieValue()); DVLOG(1) << "Setting authorization header value to " << delegate_->GetWalletCookieValue(); request_started_timestamp_ = base::Time::Now(); request_->Start(); delegate_->GetMetricLogger().LogWalletErrorMetric( AutofillMetrics::WALLET_ERROR_BASELINE_ISSUED_REQUEST); delegate_->GetMetricLogger().LogWalletRequiredActionMetric( 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(); // |request_|, which is aliased to |source|, might continue to be used in this // |method, but should be freed once control leaves the method. scoped_ptr scoped_request(request_.Pass()); // Prepare to start the next pending request. This is queued up as an // asynchronous message because |this| WalletClient instance can be destroyed // before the end of the method in response to the current incoming request. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&WalletClient::StartNextPendingRequest, weak_ptr_factory_.GetWeakPtr()));; std::string data; source->GetResponseAsString(&data); DVLOG(1) << "Response body: " << data; scoped_ptr response_dict; int response_code = source->GetResponseCode(); delegate_->GetMetricLogger().LogWalletResponseCode(response_code); 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(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 message_value(base::JSONReader::Read(data)); if (message_value.get() && message_value->IsType(base::Value::TYPE_DICTIONARY)) { response_dict.reset( static_cast(message_value.release())); } if (response_code == net::HTTP_INTERNAL_SERVER_ERROR) { request_type_ = NO_PENDING_REQUEST; std::string error_type_string; if (!response_dict->GetString(kErrorTypeKey, &error_type_string)) { HandleWalletError(UNKNOWN_ERROR); return; } WalletClient::ErrorType error_type = StringToErrorType(error_type_string); if (error_type == BUYER_ACCOUNT_ERROR) { // If the error_type is |BUYER_ACCOUNT_ERROR|, then // message_type_for_buyer field contains more specific information // about the error. std::string message_type_for_buyer_string; if (response_dict->GetString(kMessageTypeForBuyerKey, &message_type_for_buyer_string)) { error_type = BuyerErrorStringToErrorType( message_type_for_buyer_string); } } HandleWalletError(error_type); return; } break; } // Anything else is an error. default: request_type_ = NO_PENDING_REQUEST; HandleWalletError(NETWORK_ERROR); return; } RequestType type = request_type_; request_type_ = NO_PENDING_REQUEST; if (type != ACCEPT_LEGAL_DOCUMENTS && !response_dict) { HandleMalformedResponse(type, scoped_request.get()); 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(type, scoped_request.get()); } break; } case GET_FULL_WALLET: { scoped_ptr 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(type, scoped_request.get()); } break; } case GET_WALLET_ITEMS: { scoped_ptr wallet_items( WalletItems::CreateWalletItems(*response_dict)); if (wallet_items) { LogRequiredActions(wallet_items->required_actions()); delegate_->OnDidGetWalletItems(wallet_items.Pass()); } else { HandleMalformedResponse(type, scoped_request.get()); } break; } case SAVE_TO_WALLET: { std::string instrument_id; response_dict->GetString(kInstrumentIdKey, &instrument_id); std::string shipping_address_id; response_dict->GetString(kShippingAddressIdKey, &shipping_address_id); std::vector required_actions; GetRequiredActionsForSaveToWallet(*response_dict, &required_actions); std::vector form_errors; GetFormFieldErrors(*response_dict, &form_errors); if (instrument_id.empty() && shipping_address_id.empty() && required_actions.empty()) { HandleMalformedResponse(type, scoped_request.get()); } else { LogRequiredActions(required_actions); delegate_->OnDidSaveToWallet(instrument_id, shipping_address_id, required_actions, form_errors); } break; } case NO_PENDING_REQUEST: NOTREACHED(); } } void WalletClient::StartNextPendingRequest() { if (pending_requests_.empty()) return; base::Closure next_request = pending_requests_.front(); pending_requests_.pop(); next_request.Run(); } void WalletClient::HandleMalformedResponse(RequestType request_type, net::URLFetcher* request) { // Called to inform exponential backoff logic of the error. request->ReceivedContentWasMalformed(); // Record failed API call in metrics. delegate_->GetMetricLogger().LogWalletMalformedResponseMetric( RequestTypeToUmaMetric(request_type)); HandleWalletError(MALFORMED_RESPONSE); } void WalletClient::HandleWalletError(WalletClient::ErrorType error_type) { std::string error_message; switch (error_type) { case WalletClient::BAD_REQUEST: error_message = "WALLET_BAD_REQUEST"; break; case WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED: error_message = "WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED"; break; case WalletClient::BUYER_ACCOUNT_ERROR: error_message = "WALLET_BUYER_ACCOUNT_ERROR"; break; case WalletClient::INTERNAL_ERROR: error_message = "WALLET_INTERNAL_ERROR"; break; case WalletClient::INVALID_PARAMS: error_message = "WALLET_INVALID_PARAMS"; break; case WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS: error_message = "WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS"; break; case WalletClient::SERVICE_UNAVAILABLE: error_message = "WALLET_SERVICE_UNAVAILABLE"; break; case WalletClient::UNSUPPORTED_API_VERSION: error_message = "WALLET_UNSUPPORTED_API_VERSION"; break; case WalletClient::UNSUPPORTED_MERCHANT: error_message = "WALLET_UNSUPPORTED_MERCHANT"; break; case WalletClient::MALFORMED_RESPONSE: error_message = "WALLET_MALFORMED_RESPONSE"; break; case WalletClient::NETWORK_ERROR: error_message = "WALLET_NETWORK_ERROR"; break; case WalletClient::UNKNOWN_ERROR: error_message = "WALLET_UNKNOWN_ERROR"; break; } DVLOG(1) << "Wallet encountered a " << error_message; delegate_->OnWalletError(error_type); delegate_->GetMetricLogger().LogWalletErrorMetric( ErrorTypeToUmaMetric(error_type)); } // Logs an UMA metric for each of the |required_actions|. void WalletClient::LogRequiredActions( const std::vector& required_actions) const { for (size_t i = 0; i < required_actions.size(); ++i) { delegate_->GetMetricLogger().LogWalletRequiredActionMetric( 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_TO_WALLET: return AutofillMetrics::SAVE_TO_WALLET; case NO_PENDING_REQUEST: NOTREACHED(); return AutofillMetrics::UNKNOWN_API_CALL; } NOTREACHED(); return AutofillMetrics::UNKNOWN_API_CALL; } } // namespace wallet } // namespace autofill