| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/autofill/content/browser/wallet/wallet_client.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/json/json_reader.h" | |
| 9 #include "base/json/json_writer.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 #include "base/message_loop/message_loop.h" | |
| 13 #include "base/strings/string_number_conversions.h" | |
| 14 #include "base/strings/string_util.h" | |
| 15 #include "base/strings/stringprintf.h" | |
| 16 #include "base/strings/utf_string_conversions.h" | |
| 17 #include "components/autofill/content/browser/wallet/form_field_error.h" | |
| 18 #include "components/autofill/content/browser/wallet/instrument.h" | |
| 19 #include "components/autofill/content/browser/wallet/wallet_address.h" | |
| 20 #include "components/autofill/content/browser/wallet/wallet_client_delegate.h" | |
| 21 #include "components/autofill/content/browser/wallet/wallet_items.h" | |
| 22 #include "components/autofill/content/browser/wallet/wallet_service_url.h" | |
| 23 #include "components/autofill/core/browser/autofill_metrics.h" | |
| 24 #include "crypto/random.h" | |
| 25 #include "google_apis/google_api_keys.h" | |
| 26 #include "net/base/escape.h" | |
| 27 #include "net/http/http_status_code.h" | |
| 28 #include "net/url_request/url_fetcher.h" | |
| 29 #include "net/url_request/url_request_context_getter.h" | |
| 30 | |
| 31 namespace autofill { | |
| 32 namespace wallet { | |
| 33 | |
| 34 namespace { | |
| 35 | |
| 36 const char kFormEncodedMimeType[] = "application/x-www-form-urlencoded"; | |
| 37 const char kJsonMimeType[] = "application/json"; | |
| 38 const char kEscrowNewInstrumentFormat[] = | |
| 39 "request_content_type=application/json&request=%s&cvn=%s&card_number=%s"; | |
| 40 const char kEscrowCardVerificationNumberFormat[] = | |
| 41 "request_content_type=application/json&request=%s&cvn=%s"; | |
| 42 const char kGetFullWalletRequestFormat[] = | |
| 43 "request_content_type=application/json&request=%s&otp=%s:%s"; | |
| 44 const size_t kOneTimePadLength = 6; | |
| 45 | |
| 46 // The maximum number of bits in the one time pad that the server is willing to | |
| 47 // accept. | |
| 48 const size_t kMaxBits = 56; | |
| 49 | |
| 50 // The minimum number of bits in the one time pad that the server is willing to | |
| 51 // accept. | |
| 52 const size_t kMinBits = 40; | |
| 53 | |
| 54 std::string RiskCapabilityToString( | |
| 55 WalletClient::RiskCapability risk_capability) { | |
| 56 switch (risk_capability) { | |
| 57 case WalletClient::RELOGIN: | |
| 58 return "RELOGIN"; | |
| 59 case WalletClient::VERIFY_CVC: | |
| 60 return "VERIFY_CVC"; | |
| 61 } | |
| 62 NOTREACHED(); | |
| 63 return "NOT_POSSIBLE"; | |
| 64 } | |
| 65 | |
| 66 WalletClient::ErrorType StringToErrorType(const std::string& error_type) { | |
| 67 std::string trimmed; | |
| 68 base::TrimWhitespaceASCII(error_type, base::TRIM_ALL, &trimmed); | |
| 69 if (base::LowerCaseEqualsASCII(trimmed, "buyer_account_error")) | |
| 70 return WalletClient::BUYER_ACCOUNT_ERROR; | |
| 71 if (base::LowerCaseEqualsASCII(trimmed, "unsupported_merchant")) | |
| 72 return WalletClient::UNSUPPORTED_MERCHANT; | |
| 73 if (base::LowerCaseEqualsASCII(trimmed, "internal_error")) | |
| 74 return WalletClient::INTERNAL_ERROR; | |
| 75 if (base::LowerCaseEqualsASCII(trimmed, "invalid_params")) | |
| 76 return WalletClient::INVALID_PARAMS; | |
| 77 if (base::LowerCaseEqualsASCII(trimmed, "service_unavailable")) | |
| 78 return WalletClient::SERVICE_UNAVAILABLE; | |
| 79 if (base::LowerCaseEqualsASCII(trimmed, "unsupported_api_version")) | |
| 80 return WalletClient::UNSUPPORTED_API_VERSION; | |
| 81 if (base::LowerCaseEqualsASCII(trimmed, "unsupported_user_agent")) | |
| 82 return WalletClient::UNSUPPORTED_USER_AGENT_OR_API_KEY; | |
| 83 if (base::LowerCaseEqualsASCII(trimmed, "spending_limit_exceeded")) | |
| 84 return WalletClient::SPENDING_LIMIT_EXCEEDED; | |
| 85 | |
| 86 DVLOG(1) << "Unknown wallet error string: \"" << error_type << '"'; | |
| 87 return WalletClient::UNKNOWN_ERROR; | |
| 88 } | |
| 89 | |
| 90 // Get the more specific WalletClient::ErrorType when the error is | |
| 91 // |BUYER_ACCOUNT_ERROR|. | |
| 92 WalletClient::ErrorType BuyerErrorStringToErrorType( | |
| 93 const std::string& message_type_for_buyer) { | |
| 94 std::string trimmed; | |
| 95 base::TrimWhitespaceASCII(message_type_for_buyer, base::TRIM_ALL, &trimmed); | |
| 96 if (base::LowerCaseEqualsASCII(trimmed, "bla_country_not_supported")) | |
| 97 return WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED; | |
| 98 if (base::LowerCaseEqualsASCII(trimmed, "buyer_kyc_error")) | |
| 99 return WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS; | |
| 100 | |
| 101 return WalletClient::BUYER_ACCOUNT_ERROR; | |
| 102 } | |
| 103 | |
| 104 // Gets and parses required actions from a SaveToWallet response. Returns | |
| 105 // false if any unknown required actions are seen and true otherwise. | |
| 106 void GetRequiredActionsForSaveToWallet( | |
| 107 const base::DictionaryValue& dict, | |
| 108 std::vector<RequiredAction>* required_actions) { | |
| 109 const base::ListValue* required_action_list; | |
| 110 if (!dict.GetList("required_action", &required_action_list)) | |
| 111 return; | |
| 112 | |
| 113 for (size_t i = 0; i < required_action_list->GetSize(); ++i) { | |
| 114 std::string action_string; | |
| 115 if (required_action_list->GetString(i, &action_string)) { | |
| 116 RequiredAction action = ParseRequiredActionFromString(action_string); | |
| 117 if (!ActionAppliesToSaveToWallet(action)) { | |
| 118 DLOG(ERROR) << "Response from Google wallet with bad required action:" | |
| 119 " \"" << action_string << "\""; | |
| 120 required_actions->clear(); | |
| 121 return; | |
| 122 } | |
| 123 required_actions->push_back(action); | |
| 124 } | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 void GetFormFieldErrors(const base::DictionaryValue& dict, | |
| 129 std::vector<FormFieldError>* form_errors) { | |
| 130 DCHECK(form_errors->empty()); | |
| 131 const base::ListValue* form_errors_list; | |
| 132 if (!dict.GetList("form_field_error", &form_errors_list)) | |
| 133 return; | |
| 134 | |
| 135 for (size_t i = 0; i < form_errors_list->GetSize(); ++i) { | |
| 136 const base::DictionaryValue* dictionary; | |
| 137 if (form_errors_list->GetDictionary(i, &dictionary)) | |
| 138 form_errors->push_back(FormFieldError::CreateFormFieldError(*dictionary)); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 // Converts the |error_type| to the corresponding value from the stable UMA | |
| 143 // metric enumeration. | |
| 144 AutofillMetrics::WalletErrorMetric ErrorTypeToUmaMetric( | |
| 145 WalletClient::ErrorType error_type) { | |
| 146 switch (error_type) { | |
| 147 case WalletClient::BAD_REQUEST: | |
| 148 return AutofillMetrics::WALLET_BAD_REQUEST; | |
| 149 case WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED: | |
| 150 return AutofillMetrics::WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED; | |
| 151 case WalletClient::BUYER_ACCOUNT_ERROR: | |
| 152 return AutofillMetrics::WALLET_BUYER_ACCOUNT_ERROR; | |
| 153 case WalletClient::INTERNAL_ERROR: | |
| 154 return AutofillMetrics::WALLET_INTERNAL_ERROR; | |
| 155 case WalletClient::INVALID_PARAMS: | |
| 156 return AutofillMetrics::WALLET_INVALID_PARAMS; | |
| 157 case WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS: | |
| 158 return AutofillMetrics::WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS; | |
| 159 case WalletClient::SERVICE_UNAVAILABLE: | |
| 160 return AutofillMetrics::WALLET_SERVICE_UNAVAILABLE; | |
| 161 case WalletClient::UNSUPPORTED_API_VERSION: | |
| 162 return AutofillMetrics::WALLET_UNSUPPORTED_API_VERSION; | |
| 163 case WalletClient::UNSUPPORTED_MERCHANT: | |
| 164 return AutofillMetrics::WALLET_UNSUPPORTED_MERCHANT; | |
| 165 case WalletClient::SPENDING_LIMIT_EXCEEDED: | |
| 166 return AutofillMetrics::WALLET_SPENDING_LIMIT_EXCEEDED; | |
| 167 case WalletClient::MALFORMED_RESPONSE: | |
| 168 return AutofillMetrics::WALLET_MALFORMED_RESPONSE; | |
| 169 case WalletClient::NETWORK_ERROR: | |
| 170 return AutofillMetrics::WALLET_NETWORK_ERROR; | |
| 171 case WalletClient::UNKNOWN_ERROR: | |
| 172 return AutofillMetrics::WALLET_UNKNOWN_ERROR; | |
| 173 case WalletClient::UNSUPPORTED_USER_AGENT_OR_API_KEY: | |
| 174 return AutofillMetrics::WALLET_UNSUPPORTED_USER_AGENT_OR_API_KEY; | |
| 175 } | |
| 176 | |
| 177 NOTREACHED(); | |
| 178 return AutofillMetrics::WALLET_UNKNOWN_ERROR; | |
| 179 } | |
| 180 | |
| 181 // Converts the |required_action| to the corresponding value from the stable UMA | |
| 182 // metric enumeration. | |
| 183 AutofillMetrics::WalletRequiredActionMetric RequiredActionToUmaMetric( | |
| 184 RequiredAction required_action) { | |
| 185 switch (required_action) { | |
| 186 case UNKNOWN_TYPE: | |
| 187 return AutofillMetrics::UNKNOWN_REQUIRED_ACTION; | |
| 188 case CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS: | |
| 189 return AutofillMetrics::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS; | |
| 190 case SETUP_WALLET: | |
| 191 return AutofillMetrics::SETUP_WALLET; | |
| 192 case ACCEPT_TOS: | |
| 193 return AutofillMetrics::ACCEPT_TOS; | |
| 194 case GAIA_AUTH: | |
| 195 return AutofillMetrics::GAIA_AUTH; | |
| 196 case UPDATE_EXPIRATION_DATE: | |
| 197 return AutofillMetrics::UPDATE_EXPIRATION_DATE; | |
| 198 case UPGRADE_MIN_ADDRESS: | |
| 199 return AutofillMetrics::UPGRADE_MIN_ADDRESS; | |
| 200 case INVALID_FORM_FIELD: | |
| 201 return AutofillMetrics::INVALID_FORM_FIELD; | |
| 202 case VERIFY_CVV: | |
| 203 return AutofillMetrics::VERIFY_CVV; | |
| 204 case PASSIVE_GAIA_AUTH: | |
| 205 return AutofillMetrics::PASSIVE_GAIA_AUTH; | |
| 206 case REQUIRE_PHONE_NUMBER: | |
| 207 return AutofillMetrics::REQUIRE_PHONE_NUMBER; | |
| 208 } | |
| 209 | |
| 210 NOTREACHED(); | |
| 211 return AutofillMetrics::UNKNOWN_REQUIRED_ACTION; | |
| 212 } | |
| 213 | |
| 214 // Keys for JSON communication with the Online Wallet server. | |
| 215 const char kAcceptedLegalDocumentKey[] = "accepted_legal_document"; | |
| 216 const char kApiKeyKey[] = "api_key"; | |
| 217 const char kAuthResultKey[] = "auth_result"; | |
| 218 const char kErrorTypeKey[] = "wallet_error.error_type"; | |
| 219 const char kFeatureKey[] = "feature"; | |
| 220 const char kGoogleTransactionIdKey[] = "google_transaction_id"; | |
| 221 const char kInstrumentIdKey[] = "instrument_id"; | |
| 222 const char kInstrumentKey[] = "instrument"; | |
| 223 const char kInstrumentExpMonthKey[] = "instrument.credit_card.exp_month"; | |
| 224 const char kInstrumentExpYearKey[] = "instrument.credit_card.exp_year"; | |
| 225 const char kInstrumentType[] = "instrument.type"; | |
| 226 const char kInstrumentPhoneNumberKey[] = "instrument_phone_number"; | |
| 227 const char kMerchantDomainKey[] = "merchant_domain"; | |
| 228 const char kMessageTypeForBuyerKey[] = "wallet_error.message_type_for_buyer"; | |
| 229 const char kNewWalletUser[] = "new_wallet_user"; | |
| 230 const char kPhoneNumberRequired[] = "phone_number_required"; | |
| 231 const char kRiskCapabilitiesKey[] = "supported_risk_challenge"; | |
| 232 const char kRiskParamsKey[] = "risk_params"; | |
| 233 const char kSelectedAddressIdKey[] = "selected_address_id"; | |
| 234 const char kSelectedInstrumentIdKey[] = "selected_instrument_id"; | |
| 235 const char kShippingAddressIdKey[] = "shipping_address_id"; | |
| 236 const char kShippingAddressKey[] = "shipping_address"; | |
| 237 const char kShippingAddressRequired[] = "shipping_address_required"; | |
| 238 const char kTransactionAmountKey[] = "estimated_total_price"; | |
| 239 const char kTransactionCurrencyKey[] = "currency_code"; | |
| 240 const char kUpgradedBillingAddressKey[] = "upgraded_billing_address"; | |
| 241 const char kUpgradedInstrumentIdKey[] = "upgraded_instrument_id"; | |
| 242 const char kUseMinimalAddresses[] = "use_minimal_addresses"; | |
| 243 | |
| 244 } // namespace | |
| 245 | |
| 246 WalletClient::FullWalletRequest::FullWalletRequest( | |
| 247 const std::string& instrument_id, | |
| 248 const std::string& address_id, | |
| 249 const std::string& google_transaction_id, | |
| 250 const std::vector<RiskCapability> risk_capabilities, | |
| 251 bool new_wallet_user) | |
| 252 : instrument_id(instrument_id), | |
| 253 address_id(address_id), | |
| 254 google_transaction_id(google_transaction_id), | |
| 255 risk_capabilities(risk_capabilities), | |
| 256 new_wallet_user(new_wallet_user) {} | |
| 257 | |
| 258 WalletClient::FullWalletRequest::~FullWalletRequest() {} | |
| 259 | |
| 260 WalletClient::WalletClient(net::URLRequestContextGetter* context_getter, | |
| 261 WalletClientDelegate* delegate, | |
| 262 const GURL& source_url) | |
| 263 : context_getter_(context_getter), | |
| 264 delegate_(delegate), | |
| 265 user_index_(0U), | |
| 266 source_url_(source_url), | |
| 267 request_type_(NO_REQUEST), | |
| 268 one_time_pad_(kOneTimePadLength), | |
| 269 weak_ptr_factory_(this) { | |
| 270 DCHECK(context_getter_.get()); | |
| 271 DCHECK(delegate_); | |
| 272 } | |
| 273 | |
| 274 WalletClient::~WalletClient() {} | |
| 275 | |
| 276 void WalletClient::AcceptLegalDocuments( | |
| 277 const std::vector<WalletItems::LegalDocument*>& documents, | |
| 278 const std::string& google_transaction_id) { | |
| 279 if (documents.empty()) | |
| 280 return; | |
| 281 | |
| 282 std::vector<std::string> document_ids; | |
| 283 for (size_t i = 0; i < documents.size(); ++i) { | |
| 284 document_ids.push_back(documents[i]->id()); | |
| 285 } | |
| 286 DoAcceptLegalDocuments(document_ids, google_transaction_id); | |
| 287 } | |
| 288 | |
| 289 void WalletClient::AuthenticateInstrument( | |
| 290 const std::string& instrument_id, | |
| 291 const std::string& card_verification_number) { | |
| 292 base::DictionaryValue request_dict; | |
| 293 request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey()); | |
| 294 request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData()); | |
| 295 request_dict.SetString(kInstrumentIdKey, instrument_id); | |
| 296 | |
| 297 std::string json_payload; | |
| 298 base::JSONWriter::Write(request_dict, &json_payload); | |
| 299 | |
| 300 std::string escaped_card_verification_number = net::EscapeUrlEncodedData( | |
| 301 card_verification_number, true); | |
| 302 | |
| 303 std::string post_body = base::StringPrintf( | |
| 304 kEscrowCardVerificationNumberFormat, | |
| 305 net::EscapeUrlEncodedData(json_payload, true).c_str(), | |
| 306 escaped_card_verification_number.c_str()); | |
| 307 | |
| 308 MakeWalletRequest(GetAuthenticateInstrumentUrl(user_index_), | |
| 309 post_body, | |
| 310 kFormEncodedMimeType, | |
| 311 AUTHENTICATE_INSTRUMENT); | |
| 312 } | |
| 313 | |
| 314 void WalletClient::GetFullWallet(const FullWalletRequest& full_wallet_request) { | |
| 315 base::DictionaryValue request_dict; | |
| 316 request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey()); | |
| 317 request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData()); | |
| 318 request_dict.SetBoolean(kUseMinimalAddresses, false); | |
| 319 request_dict.SetBoolean(kPhoneNumberRequired, true); | |
| 320 request_dict.SetBoolean(kNewWalletUser, full_wallet_request.new_wallet_user); | |
| 321 | |
| 322 request_dict.SetString(kSelectedInstrumentIdKey, | |
| 323 full_wallet_request.instrument_id); | |
| 324 request_dict.SetString(kSelectedAddressIdKey, full_wallet_request.address_id); | |
| 325 request_dict.SetString( | |
| 326 kMerchantDomainKey, | |
| 327 source_url_.GetWithEmptyPath().spec()); | |
| 328 request_dict.SetString(kGoogleTransactionIdKey, | |
| 329 full_wallet_request.google_transaction_id); | |
| 330 request_dict.SetString(kFeatureKey, "REQUEST_AUTOCOMPLETE"); | |
| 331 | |
| 332 scoped_ptr<base::ListValue> risk_capabilities_list(new base::ListValue()); | |
| 333 for (std::vector<RiskCapability>::const_iterator it = | |
| 334 full_wallet_request.risk_capabilities.begin(); | |
| 335 it != full_wallet_request.risk_capabilities.end(); | |
| 336 ++it) { | |
| 337 risk_capabilities_list->AppendString(RiskCapabilityToString(*it)); | |
| 338 } | |
| 339 request_dict.Set(kRiskCapabilitiesKey, risk_capabilities_list.release()); | |
| 340 | |
| 341 std::string json_payload; | |
| 342 base::JSONWriter::Write(request_dict, &json_payload); | |
| 343 | |
| 344 crypto::RandBytes(&(one_time_pad_[0]), one_time_pad_.size()); | |
| 345 | |
| 346 size_t num_bits = one_time_pad_.size() * 8; | |
| 347 DCHECK_LE(num_bits, kMaxBits); | |
| 348 DCHECK_GE(num_bits, kMinBits); | |
| 349 | |
| 350 std::string post_body = base::StringPrintf( | |
| 351 kGetFullWalletRequestFormat, | |
| 352 net::EscapeUrlEncodedData(json_payload, true).c_str(), | |
| 353 base::HexEncode(&num_bits, 1).c_str(), | |
| 354 base::HexEncode(&(one_time_pad_[0]), one_time_pad_.size()).c_str()); | |
| 355 | |
| 356 MakeWalletRequest(GetGetFullWalletUrl(user_index_), | |
| 357 post_body, | |
| 358 kFormEncodedMimeType, | |
| 359 GET_FULL_WALLET); | |
| 360 } | |
| 361 | |
| 362 void WalletClient::SaveToWallet( | |
| 363 scoped_ptr<Instrument> instrument, | |
| 364 scoped_ptr<Address> address, | |
| 365 const WalletItems::MaskedInstrument* reference_instrument, | |
| 366 const Address* reference_address) { | |
| 367 DCHECK(instrument || address); | |
| 368 | |
| 369 base::DictionaryValue request_dict; | |
| 370 request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey()); | |
| 371 request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData()); | |
| 372 request_dict.SetString(kMerchantDomainKey, | |
| 373 source_url_.GetWithEmptyPath().spec()); | |
| 374 request_dict.SetBoolean(kUseMinimalAddresses, false); | |
| 375 request_dict.SetBoolean(kPhoneNumberRequired, true); | |
| 376 | |
| 377 std::string primary_account_number; | |
| 378 std::string card_verification_number; | |
| 379 if (instrument) { | |
| 380 primary_account_number = net::EscapeUrlEncodedData( | |
| 381 base::UTF16ToUTF8(instrument->primary_account_number()), true); | |
| 382 card_verification_number = net::EscapeUrlEncodedData( | |
| 383 base::UTF16ToUTF8(instrument->card_verification_number()), true); | |
| 384 | |
| 385 if (!reference_instrument) { | |
| 386 request_dict.Set(kInstrumentKey, instrument->ToDictionary().release()); | |
| 387 request_dict.SetString(kInstrumentPhoneNumberKey, | |
| 388 instrument->address()->phone_number()); | |
| 389 } else { | |
| 390 DCHECK(!reference_instrument->object_id().empty()); | |
| 391 | |
| 392 int new_month = instrument->expiration_month(); | |
| 393 int new_year = instrument->expiration_year(); | |
| 394 bool expiration_date_changed = | |
| 395 new_month != reference_instrument->expiration_month() || | |
| 396 new_year != reference_instrument->expiration_year(); | |
| 397 | |
| 398 DCHECK(instrument->address() || expiration_date_changed); | |
| 399 | |
| 400 request_dict.SetString(kUpgradedInstrumentIdKey, | |
| 401 reference_instrument->object_id()); | |
| 402 | |
| 403 if (instrument->address()) { | |
| 404 request_dict.SetString(kInstrumentPhoneNumberKey, | |
| 405 instrument->address()->phone_number()); | |
| 406 request_dict.Set( | |
| 407 kUpgradedBillingAddressKey, | |
| 408 instrument->address()->ToDictionaryWithoutID().release()); | |
| 409 } | |
| 410 | |
| 411 if (expiration_date_changed) { | |
| 412 // Updating expiration date requires a CVC. | |
| 413 DCHECK(!instrument->card_verification_number().empty()); | |
| 414 request_dict.SetInteger(kInstrumentExpMonthKey, | |
| 415 instrument->expiration_month()); | |
| 416 request_dict.SetInteger(kInstrumentExpYearKey, | |
| 417 instrument->expiration_year()); | |
| 418 } | |
| 419 | |
| 420 if (request_dict.HasKey(kInstrumentKey)) | |
| 421 request_dict.SetString(kInstrumentType, "CREDIT_CARD"); | |
| 422 } | |
| 423 } | |
| 424 if (address) { | |
| 425 if (reference_address) { | |
| 426 address->set_object_id(reference_address->object_id()); | |
| 427 DCHECK(!address->object_id().empty()); | |
| 428 } | |
| 429 request_dict.Set(kShippingAddressKey, | |
| 430 address->ToDictionaryWithID().release()); | |
| 431 } | |
| 432 | |
| 433 std::string json_payload; | |
| 434 base::JSONWriter::Write(request_dict, &json_payload); | |
| 435 | |
| 436 if (!card_verification_number.empty()) { | |
| 437 std::string post_body; | |
| 438 if (!primary_account_number.empty()) { | |
| 439 post_body = base::StringPrintf( | |
| 440 kEscrowNewInstrumentFormat, | |
| 441 net::EscapeUrlEncodedData(json_payload, true).c_str(), | |
| 442 card_verification_number.c_str(), | |
| 443 primary_account_number.c_str()); | |
| 444 } else { | |
| 445 post_body = base::StringPrintf( | |
| 446 kEscrowCardVerificationNumberFormat, | |
| 447 net::EscapeUrlEncodedData(json_payload, true).c_str(), | |
| 448 card_verification_number.c_str()); | |
| 449 } | |
| 450 MakeWalletRequest(GetSaveToWalletUrl(user_index_), | |
| 451 post_body, | |
| 452 kFormEncodedMimeType, | |
| 453 SAVE_TO_WALLET); | |
| 454 } else { | |
| 455 MakeWalletRequest(GetSaveToWalletNoEscrowUrl(user_index_), | |
| 456 json_payload, | |
| 457 kJsonMimeType, | |
| 458 SAVE_TO_WALLET); | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 void WalletClient::GetWalletItems(const base::string16& amount, | |
| 463 const base::string16& currency) { | |
| 464 base::DictionaryValue request_dict; | |
| 465 request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey()); | |
| 466 request_dict.SetString(kMerchantDomainKey, | |
| 467 source_url_.GetWithEmptyPath().spec()); | |
| 468 request_dict.SetBoolean(kShippingAddressRequired, | |
| 469 delegate_->IsShippingAddressRequired()); | |
| 470 request_dict.SetBoolean(kUseMinimalAddresses, false); | |
| 471 request_dict.SetBoolean(kPhoneNumberRequired, true); | |
| 472 | |
| 473 if (!amount.empty()) | |
| 474 request_dict.SetString(kTransactionAmountKey, amount); | |
| 475 if (!currency.empty()) | |
| 476 request_dict.SetString(kTransactionCurrencyKey, currency); | |
| 477 | |
| 478 std::string post_body; | |
| 479 base::JSONWriter::Write(request_dict, &post_body); | |
| 480 | |
| 481 MakeWalletRequest(GetGetWalletItemsUrl(user_index_), | |
| 482 post_body, | |
| 483 kJsonMimeType, | |
| 484 GET_WALLET_ITEMS); | |
| 485 } | |
| 486 | |
| 487 bool WalletClient::HasRequestInProgress() const { | |
| 488 return request_; | |
| 489 } | |
| 490 | |
| 491 void WalletClient::CancelRequest() { | |
| 492 request_.reset(); | |
| 493 request_type_ = NO_REQUEST; | |
| 494 } | |
| 495 | |
| 496 void WalletClient::SetUserIndex(size_t user_index) { | |
| 497 CancelRequest(); | |
| 498 user_index_ = user_index; | |
| 499 } | |
| 500 | |
| 501 void WalletClient::DoAcceptLegalDocuments( | |
| 502 const std::vector<std::string>& document_ids, | |
| 503 const std::string& google_transaction_id) { | |
| 504 base::DictionaryValue request_dict; | |
| 505 request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey()); | |
| 506 request_dict.SetString(kGoogleTransactionIdKey, google_transaction_id); | |
| 507 request_dict.SetString(kMerchantDomainKey, | |
| 508 source_url_.GetWithEmptyPath().spec()); | |
| 509 scoped_ptr<base::ListValue> docs_list(new base::ListValue()); | |
| 510 for (std::vector<std::string>::const_iterator it = document_ids.begin(); | |
| 511 it != document_ids.end(); ++it) { | |
| 512 if (!it->empty()) | |
| 513 docs_list->AppendString(*it); | |
| 514 } | |
| 515 request_dict.Set(kAcceptedLegalDocumentKey, docs_list.release()); | |
| 516 | |
| 517 std::string post_body; | |
| 518 base::JSONWriter::Write(request_dict, &post_body); | |
| 519 | |
| 520 MakeWalletRequest(GetAcceptLegalDocumentsUrl(user_index_), | |
| 521 post_body, | |
| 522 kJsonMimeType, | |
| 523 ACCEPT_LEGAL_DOCUMENTS); | |
| 524 } | |
| 525 | |
| 526 void WalletClient::MakeWalletRequest(const GURL& url, | |
| 527 const std::string& post_body, | |
| 528 const std::string& mime_type, | |
| 529 RequestType request_type) { | |
| 530 DCHECK_EQ(request_type_, NO_REQUEST); | |
| 531 request_type_ = request_type; | |
| 532 | |
| 533 request_ = net::URLFetcher::Create(0, url, net::URLFetcher::POST, this); | |
| 534 request_->SetRequestContext(context_getter_.get()); | |
| 535 DVLOG(1) << "Making request to " << url << " with post_body=" << post_body; | |
| 536 request_->SetUploadData(mime_type, post_body); | |
| 537 request_->AddExtraRequestHeader("Authorization: GoogleLogin auth=" + | |
| 538 delegate_->GetWalletCookieValue()); | |
| 539 DVLOG(1) << "Setting authorization header value to " | |
| 540 << delegate_->GetWalletCookieValue(); | |
| 541 request_started_timestamp_ = base::Time::Now(); | |
| 542 request_->Start(); | |
| 543 | |
| 544 AutofillMetrics::LogWalletErrorMetric( | |
| 545 AutofillMetrics::WALLET_ERROR_BASELINE_ISSUED_REQUEST); | |
| 546 AutofillMetrics::LogWalletRequiredActionMetric( | |
| 547 AutofillMetrics::WALLET_REQUIRED_ACTION_BASELINE_ISSUED_REQUEST); | |
| 548 } | |
| 549 | |
| 550 // TODO(ahutter): Add manual retry logic if it's necessary. | |
| 551 void WalletClient::OnURLFetchComplete( | |
| 552 const net::URLFetcher* source) { | |
| 553 AutofillMetrics::LogWalletApiCallDuration( | |
| 554 RequestTypeToUmaMetric(request_type_), | |
| 555 base::Time::Now() - request_started_timestamp_); | |
| 556 | |
| 557 DCHECK_EQ(source, request_.get()); | |
| 558 DVLOG(1) << "Got response from " << source->GetOriginalURL(); | |
| 559 | |
| 560 // |request_|, which is aliased to |source|, might continue to be used in this | |
| 561 // |method, but should be freed once control leaves the method. | |
| 562 scoped_ptr<net::URLFetcher> scoped_request(request_.Pass()); | |
| 563 | |
| 564 std::string data; | |
| 565 source->GetResponseAsString(&data); | |
| 566 DVLOG(1) << "Response body: " << data; | |
| 567 | |
| 568 scoped_ptr<base::DictionaryValue> response_dict; | |
| 569 | |
| 570 int response_code = source->GetResponseCode(); | |
| 571 AutofillMetrics::LogWalletResponseCode(response_code); | |
| 572 | |
| 573 switch (response_code) { | |
| 574 // HTTP_BAD_REQUEST means the arguments are invalid. No point retrying. | |
| 575 case net::HTTP_BAD_REQUEST: { | |
| 576 request_type_ = NO_REQUEST; | |
| 577 HandleWalletError(BAD_REQUEST); | |
| 578 return; | |
| 579 } | |
| 580 | |
| 581 // Valid response. | |
| 582 case net::HTTP_OK: { | |
| 583 scoped_ptr<base::Value> message_value = base::JSONReader::Read(data); | |
| 584 if (message_value.get() && | |
| 585 message_value->IsType(base::Value::TYPE_DICTIONARY)) { | |
| 586 response_dict.reset( | |
| 587 static_cast<base::DictionaryValue*>(message_value.release())); | |
| 588 } | |
| 589 break; | |
| 590 } | |
| 591 | |
| 592 // Response contains an error to show the user. | |
| 593 case net::HTTP_FORBIDDEN: | |
| 594 case net::HTTP_INTERNAL_SERVER_ERROR: { | |
| 595 scoped_ptr<base::Value> message_value = base::JSONReader::Read(data); | |
| 596 if (message_value.get() && | |
| 597 message_value->IsType(base::Value::TYPE_DICTIONARY)) { | |
| 598 response_dict.reset( | |
| 599 static_cast<base::DictionaryValue*>(message_value.release())); | |
| 600 } | |
| 601 | |
| 602 request_type_ = NO_REQUEST; | |
| 603 | |
| 604 std::string error_type_string; | |
| 605 if (!response_dict->GetString(kErrorTypeKey, &error_type_string)) { | |
| 606 HandleWalletError(UNKNOWN_ERROR); | |
| 607 return; | |
| 608 } | |
| 609 WalletClient::ErrorType error_type = StringToErrorType(error_type_string); | |
| 610 if (error_type == BUYER_ACCOUNT_ERROR) { | |
| 611 // If the error_type is |BUYER_ACCOUNT_ERROR|, then | |
| 612 // message_type_for_buyer field contains more specific information | |
| 613 // about the error. | |
| 614 std::string message_type_for_buyer_string; | |
| 615 if (response_dict->GetString(kMessageTypeForBuyerKey, | |
| 616 &message_type_for_buyer_string)) { | |
| 617 error_type = | |
| 618 BuyerErrorStringToErrorType(message_type_for_buyer_string); | |
| 619 } | |
| 620 } | |
| 621 | |
| 622 HandleWalletError(error_type); | |
| 623 return; | |
| 624 } | |
| 625 | |
| 626 // Handle anything else as a generic error. | |
| 627 default: | |
| 628 request_type_ = NO_REQUEST; | |
| 629 HandleWalletError(NETWORK_ERROR); | |
| 630 return; | |
| 631 } | |
| 632 | |
| 633 RequestType type = request_type_; | |
| 634 request_type_ = NO_REQUEST; | |
| 635 | |
| 636 if (type != ACCEPT_LEGAL_DOCUMENTS && !response_dict) { | |
| 637 HandleMalformedResponse(type, scoped_request.get()); | |
| 638 return; | |
| 639 } | |
| 640 | |
| 641 switch (type) { | |
| 642 case ACCEPT_LEGAL_DOCUMENTS: | |
| 643 delegate_->OnDidAcceptLegalDocuments(); | |
| 644 break; | |
| 645 | |
| 646 case AUTHENTICATE_INSTRUMENT: { | |
| 647 std::string auth_result; | |
| 648 if (response_dict->GetString(kAuthResultKey, &auth_result)) { | |
| 649 std::string trimmed; | |
| 650 base::TrimWhitespaceASCII(auth_result, base::TRIM_ALL, &trimmed); | |
| 651 delegate_->OnDidAuthenticateInstrument( | |
| 652 base::LowerCaseEqualsASCII(trimmed, "success")); | |
| 653 } else { | |
| 654 HandleMalformedResponse(type, scoped_request.get()); | |
| 655 } | |
| 656 break; | |
| 657 } | |
| 658 | |
| 659 case GET_FULL_WALLET: { | |
| 660 scoped_ptr<FullWallet> full_wallet( | |
| 661 FullWallet::CreateFullWallet(*response_dict)); | |
| 662 if (full_wallet) { | |
| 663 full_wallet->set_one_time_pad(one_time_pad_); | |
| 664 LogRequiredActions(full_wallet->required_actions()); | |
| 665 delegate_->OnDidGetFullWallet(full_wallet.Pass()); | |
| 666 } else { | |
| 667 HandleMalformedResponse(type, scoped_request.get()); | |
| 668 } | |
| 669 break; | |
| 670 } | |
| 671 | |
| 672 case GET_WALLET_ITEMS: { | |
| 673 scoped_ptr<WalletItems> wallet_items( | |
| 674 WalletItems::CreateWalletItems(*response_dict)); | |
| 675 if (wallet_items) { | |
| 676 LogRequiredActions(wallet_items->required_actions()); | |
| 677 delegate_->OnDidGetWalletItems(wallet_items.Pass()); | |
| 678 } else { | |
| 679 HandleMalformedResponse(type, scoped_request.get()); | |
| 680 } | |
| 681 break; | |
| 682 } | |
| 683 | |
| 684 case SAVE_TO_WALLET: { | |
| 685 std::string instrument_id; | |
| 686 response_dict->GetString(kInstrumentIdKey, &instrument_id); | |
| 687 std::string shipping_address_id; | |
| 688 response_dict->GetString(kShippingAddressIdKey, | |
| 689 &shipping_address_id); | |
| 690 std::vector<RequiredAction> required_actions; | |
| 691 GetRequiredActionsForSaveToWallet(*response_dict, &required_actions); | |
| 692 std::vector<FormFieldError> form_errors; | |
| 693 GetFormFieldErrors(*response_dict, &form_errors); | |
| 694 if (instrument_id.empty() && shipping_address_id.empty() && | |
| 695 required_actions.empty()) { | |
| 696 HandleMalformedResponse(type, scoped_request.get()); | |
| 697 } else { | |
| 698 LogRequiredActions(required_actions); | |
| 699 delegate_->OnDidSaveToWallet(instrument_id, | |
| 700 shipping_address_id, | |
| 701 required_actions, | |
| 702 form_errors); | |
| 703 } | |
| 704 break; | |
| 705 } | |
| 706 | |
| 707 case NO_REQUEST: | |
| 708 NOTREACHED(); | |
| 709 } | |
| 710 } | |
| 711 | |
| 712 void WalletClient::HandleMalformedResponse(RequestType request_type, | |
| 713 net::URLFetcher* request) { | |
| 714 // Called to inform exponential backoff logic of the error. | |
| 715 request->ReceivedContentWasMalformed(); | |
| 716 // Record failed API call in metrics. | |
| 717 AutofillMetrics::LogWalletMalformedResponseMetric( | |
| 718 RequestTypeToUmaMetric(request_type)); | |
| 719 HandleWalletError(MALFORMED_RESPONSE); | |
| 720 } | |
| 721 | |
| 722 void WalletClient::HandleWalletError(WalletClient::ErrorType error_type) { | |
| 723 std::string error_message; | |
| 724 switch (error_type) { | |
| 725 case WalletClient::BAD_REQUEST: | |
| 726 error_message = "WALLET_BAD_REQUEST"; | |
| 727 break; | |
| 728 case WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED: | |
| 729 error_message = "WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED"; | |
| 730 break; | |
| 731 case WalletClient::BUYER_ACCOUNT_ERROR: | |
| 732 error_message = "WALLET_BUYER_ACCOUNT_ERROR"; | |
| 733 break; | |
| 734 case WalletClient::INTERNAL_ERROR: | |
| 735 error_message = "WALLET_INTERNAL_ERROR"; | |
| 736 break; | |
| 737 case WalletClient::INVALID_PARAMS: | |
| 738 error_message = "WALLET_INVALID_PARAMS"; | |
| 739 break; | |
| 740 case WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS: | |
| 741 error_message = "WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS"; | |
| 742 break; | |
| 743 case WalletClient::SPENDING_LIMIT_EXCEEDED: | |
| 744 error_message = "SPENDING_LIMIT_EXCEEDED"; | |
| 745 break; | |
| 746 case WalletClient::SERVICE_UNAVAILABLE: | |
| 747 error_message = "WALLET_SERVICE_UNAVAILABLE"; | |
| 748 break; | |
| 749 case WalletClient::UNSUPPORTED_API_VERSION: | |
| 750 error_message = "WALLET_UNSUPPORTED_API_VERSION"; | |
| 751 break; | |
| 752 case WalletClient::UNSUPPORTED_MERCHANT: | |
| 753 error_message = "WALLET_UNSUPPORTED_MERCHANT"; | |
| 754 break; | |
| 755 case WalletClient::MALFORMED_RESPONSE: | |
| 756 error_message = "WALLET_MALFORMED_RESPONSE"; | |
| 757 break; | |
| 758 case WalletClient::NETWORK_ERROR: | |
| 759 error_message = "WALLET_NETWORK_ERROR"; | |
| 760 break; | |
| 761 case WalletClient::UNKNOWN_ERROR: | |
| 762 error_message = "WALLET_UNKNOWN_ERROR"; | |
| 763 break; | |
| 764 case WalletClient::UNSUPPORTED_USER_AGENT_OR_API_KEY: | |
| 765 error_message = "WALLET_UNSUPPORTED_USER_AGENT_OR_API_KEY"; | |
| 766 break; | |
| 767 } | |
| 768 | |
| 769 DVLOG(1) << "Wallet encountered a " << error_message; | |
| 770 | |
| 771 delegate_->OnWalletError(error_type); | |
| 772 AutofillMetrics::LogWalletErrorMetric(ErrorTypeToUmaMetric(error_type)); | |
| 773 } | |
| 774 | |
| 775 // Logs an UMA metric for each of the |required_actions|. | |
| 776 void WalletClient::LogRequiredActions( | |
| 777 const std::vector<RequiredAction>& required_actions) const { | |
| 778 for (size_t i = 0; i < required_actions.size(); ++i) { | |
| 779 AutofillMetrics::LogWalletRequiredActionMetric( | |
| 780 RequiredActionToUmaMetric(required_actions[i])); | |
| 781 } | |
| 782 } | |
| 783 | |
| 784 AutofillMetrics::WalletApiCallMetric WalletClient::RequestTypeToUmaMetric( | |
| 785 RequestType request_type) const { | |
| 786 switch (request_type) { | |
| 787 case ACCEPT_LEGAL_DOCUMENTS: | |
| 788 return AutofillMetrics::ACCEPT_LEGAL_DOCUMENTS; | |
| 789 case AUTHENTICATE_INSTRUMENT: | |
| 790 return AutofillMetrics::AUTHENTICATE_INSTRUMENT; | |
| 791 case GET_FULL_WALLET: | |
| 792 return AutofillMetrics::GET_FULL_WALLET; | |
| 793 case GET_WALLET_ITEMS: | |
| 794 return AutofillMetrics::GET_WALLET_ITEMS; | |
| 795 case SAVE_TO_WALLET: | |
| 796 return AutofillMetrics::SAVE_TO_WALLET; | |
| 797 case NO_REQUEST: | |
| 798 NOTREACHED(); | |
| 799 return AutofillMetrics::UNKNOWN_API_CALL; | |
| 800 } | |
| 801 | |
| 802 NOTREACHED(); | |
| 803 return AutofillMetrics::UNKNOWN_API_CALL; | |
| 804 } | |
| 805 | |
| 806 } // namespace wallet | |
| 807 } // namespace autofill | |
| OLD | NEW |