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
RiskCapabilityToString(WalletClient::RiskCapability risk_capability)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
StringToErrorType(const std::string & error_type)66 WalletClient::ErrorType StringToErrorType(const std::string& error_type) {
67 std::string trimmed;
68 base::TrimWhitespaceASCII(error_type, base::TRIM_ALL, &trimmed);
69 if (LowerCaseEqualsASCII(trimmed, "buyer_account_error"))
70 return WalletClient::BUYER_ACCOUNT_ERROR;
71 if (LowerCaseEqualsASCII(trimmed, "unsupported_merchant"))
72 return WalletClient::UNSUPPORTED_MERCHANT;
73 if (LowerCaseEqualsASCII(trimmed, "internal_error"))
74 return WalletClient::INTERNAL_ERROR;
75 if (LowerCaseEqualsASCII(trimmed, "invalid_params"))
76 return WalletClient::INVALID_PARAMS;
77 if (LowerCaseEqualsASCII(trimmed, "service_unavailable"))
78 return WalletClient::SERVICE_UNAVAILABLE;
79 if (LowerCaseEqualsASCII(trimmed, "unsupported_api_version"))
80 return WalletClient::UNSUPPORTED_API_VERSION;
81 if (LowerCaseEqualsASCII(trimmed, "unsupported_user_agent"))
82 return WalletClient::UNSUPPORTED_USER_AGENT_OR_API_KEY;
83 if (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|.
BuyerErrorStringToErrorType(const std::string & message_type_for_buyer)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 (LowerCaseEqualsASCII(trimmed, "bla_country_not_supported"))
97 return WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED;
98 if (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.
GetRequiredActionsForSaveToWallet(const base::DictionaryValue & dict,std::vector<RequiredAction> * required_actions)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
GetFormFieldErrors(const base::DictionaryValue & dict,std::vector<FormFieldError> * form_errors)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.
ErrorTypeToUmaMetric(WalletClient::ErrorType error_type)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.
RequiredActionToUmaMetric(RequiredAction required_action)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
FullWalletRequest(const std::string & instrument_id,const std::string & address_id,const std::string & google_transaction_id,const std::vector<RiskCapability> risk_capabilities,bool new_wallet_user)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
~FullWalletRequest()258 WalletClient::FullWalletRequest::~FullWalletRequest() {}
259
WalletClient(net::URLRequestContextGetter * context_getter,WalletClientDelegate * delegate,const GURL & source_url)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
~WalletClient()274 WalletClient::~WalletClient() {}
275
AcceptLegalDocuments(const std::vector<WalletItems::LegalDocument * > & documents,const std::string & google_transaction_id)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
AuthenticateInstrument(const std::string & instrument_id,const std::string & card_verification_number)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
GetFullWallet(const FullWalletRequest & full_wallet_request)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
SaveToWallet(scoped_ptr<Instrument> instrument,scoped_ptr<Address> address,const WalletItems::MaskedInstrument * reference_instrument,const Address * reference_address)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
GetWalletItems(const base::string16 & amount,const base::string16 & currency)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
HasRequestInProgress() const487 bool WalletClient::HasRequestInProgress() const {
488 return request_;
489 }
490
CancelRequest()491 void WalletClient::CancelRequest() {
492 request_.reset();
493 request_type_ = NO_REQUEST;
494 }
495
SetUserIndex(size_t user_index)496 void WalletClient::SetUserIndex(size_t user_index) {
497 CancelRequest();
498 user_index_ = user_index;
499 }
500
DoAcceptLegalDocuments(const std::vector<std::string> & document_ids,const std::string & google_transaction_id)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
MakeWalletRequest(const GURL & url,const std::string & post_body,const std::string & mime_type,RequestType request_type)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_.reset(net::URLFetcher::Create(
534 0, url, net::URLFetcher::POST, this));
535 request_->SetRequestContext(context_getter_.get());
536 DVLOG(1) << "Making request to " << url << " with post_body=" << post_body;
537 request_->SetUploadData(mime_type, post_body);
538 request_->AddExtraRequestHeader("Authorization: GoogleLogin auth=" +
539 delegate_->GetWalletCookieValue());
540 DVLOG(1) << "Setting authorization header value to "
541 << delegate_->GetWalletCookieValue();
542 request_started_timestamp_ = base::Time::Now();
543 request_->Start();
544
545 delegate_->GetMetricLogger().LogWalletErrorMetric(
546 AutofillMetrics::WALLET_ERROR_BASELINE_ISSUED_REQUEST);
547 delegate_->GetMetricLogger().LogWalletRequiredActionMetric(
548 AutofillMetrics::WALLET_REQUIRED_ACTION_BASELINE_ISSUED_REQUEST);
549 }
550
551 // TODO(ahutter): Add manual retry logic if it's necessary.
OnURLFetchComplete(const net::URLFetcher * source)552 void WalletClient::OnURLFetchComplete(
553 const net::URLFetcher* source) {
554 delegate_->GetMetricLogger().LogWalletApiCallDuration(
555 RequestTypeToUmaMetric(request_type_),
556 base::Time::Now() - request_started_timestamp_);
557
558 DCHECK_EQ(source, request_.get());
559 DVLOG(1) << "Got response from " << source->GetOriginalURL();
560
561 // |request_|, which is aliased to |source|, might continue to be used in this
562 // |method, but should be freed once control leaves the method.
563 scoped_ptr<net::URLFetcher> scoped_request(request_.Pass());
564
565 std::string data;
566 source->GetResponseAsString(&data);
567 DVLOG(1) << "Response body: " << data;
568
569 scoped_ptr<base::DictionaryValue> response_dict;
570
571 int response_code = source->GetResponseCode();
572 delegate_->GetMetricLogger().LogWalletResponseCode(response_code);
573
574 switch (response_code) {
575 // HTTP_BAD_REQUEST means the arguments are invalid. No point retrying.
576 case net::HTTP_BAD_REQUEST: {
577 request_type_ = NO_REQUEST;
578 HandleWalletError(BAD_REQUEST);
579 return;
580 }
581
582 // Valid response.
583 case net::HTTP_OK: {
584 scoped_ptr<base::Value> message_value(base::JSONReader::Read(data));
585 if (message_value.get() &&
586 message_value->IsType(base::Value::TYPE_DICTIONARY)) {
587 response_dict.reset(
588 static_cast<base::DictionaryValue*>(message_value.release()));
589 }
590 break;
591 }
592
593 // Response contains an error to show the user.
594 case net::HTTP_FORBIDDEN:
595 case net::HTTP_INTERNAL_SERVER_ERROR: {
596 scoped_ptr<base::Value> message_value(base::JSONReader::Read(data));
597 if (message_value.get() &&
598 message_value->IsType(base::Value::TYPE_DICTIONARY)) {
599 response_dict.reset(
600 static_cast<base::DictionaryValue*>(message_value.release()));
601 }
602
603 request_type_ = NO_REQUEST;
604
605 std::string error_type_string;
606 if (!response_dict->GetString(kErrorTypeKey, &error_type_string)) {
607 HandleWalletError(UNKNOWN_ERROR);
608 return;
609 }
610 WalletClient::ErrorType error_type = StringToErrorType(error_type_string);
611 if (error_type == BUYER_ACCOUNT_ERROR) {
612 // If the error_type is |BUYER_ACCOUNT_ERROR|, then
613 // message_type_for_buyer field contains more specific information
614 // about the error.
615 std::string message_type_for_buyer_string;
616 if (response_dict->GetString(kMessageTypeForBuyerKey,
617 &message_type_for_buyer_string)) {
618 error_type =
619 BuyerErrorStringToErrorType(message_type_for_buyer_string);
620 }
621 }
622
623 HandleWalletError(error_type);
624 return;
625 }
626
627 // Handle anything else as a generic error.
628 default:
629 request_type_ = NO_REQUEST;
630 HandleWalletError(NETWORK_ERROR);
631 return;
632 }
633
634 RequestType type = request_type_;
635 request_type_ = NO_REQUEST;
636
637 if (type != ACCEPT_LEGAL_DOCUMENTS && !response_dict) {
638 HandleMalformedResponse(type, scoped_request.get());
639 return;
640 }
641
642 switch (type) {
643 case ACCEPT_LEGAL_DOCUMENTS:
644 delegate_->OnDidAcceptLegalDocuments();
645 break;
646
647 case AUTHENTICATE_INSTRUMENT: {
648 std::string auth_result;
649 if (response_dict->GetString(kAuthResultKey, &auth_result)) {
650 std::string trimmed;
651 base::TrimWhitespaceASCII(auth_result, base::TRIM_ALL, &trimmed);
652 delegate_->OnDidAuthenticateInstrument(
653 LowerCaseEqualsASCII(trimmed, "success"));
654 } else {
655 HandleMalformedResponse(type, scoped_request.get());
656 }
657 break;
658 }
659
660 case GET_FULL_WALLET: {
661 scoped_ptr<FullWallet> full_wallet(
662 FullWallet::CreateFullWallet(*response_dict));
663 if (full_wallet) {
664 full_wallet->set_one_time_pad(one_time_pad_);
665 LogRequiredActions(full_wallet->required_actions());
666 delegate_->OnDidGetFullWallet(full_wallet.Pass());
667 } else {
668 HandleMalformedResponse(type, scoped_request.get());
669 }
670 break;
671 }
672
673 case GET_WALLET_ITEMS: {
674 scoped_ptr<WalletItems> wallet_items(
675 WalletItems::CreateWalletItems(*response_dict));
676 if (wallet_items) {
677 LogRequiredActions(wallet_items->required_actions());
678 delegate_->OnDidGetWalletItems(wallet_items.Pass());
679 } else {
680 HandleMalformedResponse(type, scoped_request.get());
681 }
682 break;
683 }
684
685 case SAVE_TO_WALLET: {
686 std::string instrument_id;
687 response_dict->GetString(kInstrumentIdKey, &instrument_id);
688 std::string shipping_address_id;
689 response_dict->GetString(kShippingAddressIdKey,
690 &shipping_address_id);
691 std::vector<RequiredAction> required_actions;
692 GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
693 std::vector<FormFieldError> form_errors;
694 GetFormFieldErrors(*response_dict, &form_errors);
695 if (instrument_id.empty() && shipping_address_id.empty() &&
696 required_actions.empty()) {
697 HandleMalformedResponse(type, scoped_request.get());
698 } else {
699 LogRequiredActions(required_actions);
700 delegate_->OnDidSaveToWallet(instrument_id,
701 shipping_address_id,
702 required_actions,
703 form_errors);
704 }
705 break;
706 }
707
708 case NO_REQUEST:
709 NOTREACHED();
710 }
711 }
712
HandleMalformedResponse(RequestType request_type,net::URLFetcher * request)713 void WalletClient::HandleMalformedResponse(RequestType request_type,
714 net::URLFetcher* request) {
715 // Called to inform exponential backoff logic of the error.
716 request->ReceivedContentWasMalformed();
717 // Record failed API call in metrics.
718 delegate_->GetMetricLogger().LogWalletMalformedResponseMetric(
719 RequestTypeToUmaMetric(request_type));
720 HandleWalletError(MALFORMED_RESPONSE);
721 }
722
HandleWalletError(WalletClient::ErrorType error_type)723 void WalletClient::HandleWalletError(WalletClient::ErrorType error_type) {
724 std::string error_message;
725 switch (error_type) {
726 case WalletClient::BAD_REQUEST:
727 error_message = "WALLET_BAD_REQUEST";
728 break;
729 case WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED:
730 error_message = "WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED";
731 break;
732 case WalletClient::BUYER_ACCOUNT_ERROR:
733 error_message = "WALLET_BUYER_ACCOUNT_ERROR";
734 break;
735 case WalletClient::INTERNAL_ERROR:
736 error_message = "WALLET_INTERNAL_ERROR";
737 break;
738 case WalletClient::INVALID_PARAMS:
739 error_message = "WALLET_INVALID_PARAMS";
740 break;
741 case WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS:
742 error_message = "WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS";
743 break;
744 case WalletClient::SPENDING_LIMIT_EXCEEDED:
745 error_message = "SPENDING_LIMIT_EXCEEDED";
746 break;
747 case WalletClient::SERVICE_UNAVAILABLE:
748 error_message = "WALLET_SERVICE_UNAVAILABLE";
749 break;
750 case WalletClient::UNSUPPORTED_API_VERSION:
751 error_message = "WALLET_UNSUPPORTED_API_VERSION";
752 break;
753 case WalletClient::UNSUPPORTED_MERCHANT:
754 error_message = "WALLET_UNSUPPORTED_MERCHANT";
755 break;
756 case WalletClient::MALFORMED_RESPONSE:
757 error_message = "WALLET_MALFORMED_RESPONSE";
758 break;
759 case WalletClient::NETWORK_ERROR:
760 error_message = "WALLET_NETWORK_ERROR";
761 break;
762 case WalletClient::UNKNOWN_ERROR:
763 error_message = "WALLET_UNKNOWN_ERROR";
764 break;
765 case WalletClient::UNSUPPORTED_USER_AGENT_OR_API_KEY:
766 error_message = "WALLET_UNSUPPORTED_USER_AGENT_OR_API_KEY";
767 break;
768 }
769
770 DVLOG(1) << "Wallet encountered a " << error_message;
771
772 delegate_->OnWalletError(error_type);
773 delegate_->GetMetricLogger().LogWalletErrorMetric(
774 ErrorTypeToUmaMetric(error_type));
775 }
776
777 // Logs an UMA metric for each of the |required_actions|.
LogRequiredActions(const std::vector<RequiredAction> & required_actions) const778 void WalletClient::LogRequiredActions(
779 const std::vector<RequiredAction>& required_actions) const {
780 for (size_t i = 0; i < required_actions.size(); ++i) {
781 delegate_->GetMetricLogger().LogWalletRequiredActionMetric(
782 RequiredActionToUmaMetric(required_actions[i]));
783 }
784 }
785
RequestTypeToUmaMetric(RequestType request_type) const786 AutofillMetrics::WalletApiCallMetric WalletClient::RequestTypeToUmaMetric(
787 RequestType request_type) const {
788 switch (request_type) {
789 case ACCEPT_LEGAL_DOCUMENTS:
790 return AutofillMetrics::ACCEPT_LEGAL_DOCUMENTS;
791 case AUTHENTICATE_INSTRUMENT:
792 return AutofillMetrics::AUTHENTICATE_INSTRUMENT;
793 case GET_FULL_WALLET:
794 return AutofillMetrics::GET_FULL_WALLET;
795 case GET_WALLET_ITEMS:
796 return AutofillMetrics::GET_WALLET_ITEMS;
797 case SAVE_TO_WALLET:
798 return AutofillMetrics::SAVE_TO_WALLET;
799 case NO_REQUEST:
800 NOTREACHED();
801 return AutofillMetrics::UNKNOWN_API_CALL;
802 }
803
804 NOTREACHED();
805 return AutofillMetrics::UNKNOWN_API_CALL;
806 }
807
808 } // namespace wallet
809 } // namespace autofill
810