wallet_items.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_items.h" 6 7#include <limits> 8 9#include "base/logging.h" 10#include "base/strings/string_number_conversions.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/values.h" 13#include "components/autofill/content/browser/wallet/gaia_account.h" 14#include "components/autofill/core/browser/autofill_type.h" 15#include "components/autofill/core/browser/credit_card.h" 16#include "grit/component_strings.h" 17#include "grit/webkit_resources.h" 18#include "ui/base/l10n/l10n_util.h" 19#include "ui/base/resource/resource_bundle.h" 20#include "ui/gfx/image/image.h" 21#include "url/gurl.h" 22 23namespace autofill { 24namespace wallet { 25 26namespace { 27 28const char kLegalDocumentUrl[] = 29 "https://wallet.google.com/legaldocument?docId="; 30const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html"; 31 32// TODO(estade): move to base/. 33template<class T> 34bool VectorsAreEqual(const std::vector<T*>& a, const std::vector<T*>& b) { 35 if (a.size() != b.size()) 36 return false; 37 38 for (size_t i = 0; i < a.size(); ++i) { 39 if (*a[i] != *b[i]) 40 return false; 41 } 42 43 return true; 44} 45 46WalletItems::MaskedInstrument::Type 47 TypeFromString(const std::string& type_string) { 48 if (type_string == "VISA") 49 return WalletItems::MaskedInstrument::VISA; 50 if (type_string == "MASTER_CARD") 51 return WalletItems::MaskedInstrument::MASTER_CARD; 52 if (type_string == "AMEX") 53 return WalletItems::MaskedInstrument::AMEX; 54 if (type_string == "DISCOVER") 55 return WalletItems::MaskedInstrument::DISCOVER; 56 if (type_string == "SOLO") 57 return WalletItems::MaskedInstrument::SOLO; 58 if (type_string == "MAESTRO") 59 return WalletItems::MaskedInstrument::MAESTRO; 60 if (type_string == "SWITCH") 61 return WalletItems::MaskedInstrument::SWITCH; 62 return WalletItems::MaskedInstrument::UNKNOWN; 63} 64 65WalletItems::MaskedInstrument::Status 66 StatusFromString(const std::string& status_string) { 67 if (status_string == "AMEX_NOT_SUPPORTED") 68 return WalletItems::MaskedInstrument::AMEX_NOT_SUPPORTED; 69 if (status_string == "PENDING") 70 return WalletItems::MaskedInstrument::PENDING; 71 if (status_string == "VALID") 72 return WalletItems::MaskedInstrument::VALID; 73 if (status_string == "DECLINED") 74 return WalletItems::MaskedInstrument::DECLINED; 75 if (status_string == "DISABLED_FOR_THIS_MERCHANT") 76 return WalletItems::MaskedInstrument::DISABLED_FOR_THIS_MERCHANT; 77 if (status_string == "UNSUPPORTED_COUNTRY") 78 return WalletItems::MaskedInstrument::UNSUPPORTED_COUNTRY; 79 if (status_string == "EXPIRED") 80 return WalletItems::MaskedInstrument::EXPIRED; 81 if (status_string == "BILLING_INCOMPLETE") 82 return WalletItems::MaskedInstrument::BILLING_INCOMPLETE; 83 return WalletItems::MaskedInstrument::INAPPLICABLE; 84} 85 86base::string16 DisplayStringFromType(WalletItems::MaskedInstrument::Type type) { 87 switch (type) { 88 case WalletItems::MaskedInstrument::AMEX: 89 return CreditCard::TypeForDisplay(kAmericanExpressCard); 90 case WalletItems::MaskedInstrument::DISCOVER: 91 return CreditCard::TypeForDisplay(kDiscoverCard); 92 case WalletItems::MaskedInstrument::MASTER_CARD: 93 return CreditCard::TypeForDisplay(kMasterCard); 94 case WalletItems::MaskedInstrument::VISA: 95 return CreditCard::TypeForDisplay(kVisaCard); 96 default: 97 return CreditCard::TypeForDisplay(kGenericCard); 98 } 99} 100 101} // anonymous namespace 102 103WalletItems::MaskedInstrument::MaskedInstrument( 104 const base::string16& descriptive_name, 105 const WalletItems::MaskedInstrument::Type& type, 106 const std::vector<base::string16>& supported_currencies, 107 const base::string16& last_four_digits, 108 int expiration_month, 109 int expiration_year, 110 scoped_ptr<Address> address, 111 const WalletItems::MaskedInstrument::Status& status, 112 const std::string& object_id) 113 : descriptive_name_(descriptive_name), 114 type_(type), 115 supported_currencies_(supported_currencies), 116 last_four_digits_(last_four_digits), 117 expiration_month_(expiration_month), 118 expiration_year_(expiration_year), 119 address_(address.Pass()), 120 status_(status), 121 object_id_(object_id) { 122 DCHECK(address_); 123} 124 125WalletItems::MaskedInstrument::~MaskedInstrument() {} 126 127scoped_ptr<WalletItems::MaskedInstrument> 128 WalletItems::MaskedInstrument::CreateMaskedInstrument( 129 const base::DictionaryValue& dictionary) { 130 std::string type_string; 131 Type type; 132 if (dictionary.GetString("type", &type_string)) { 133 type = TypeFromString(type_string); 134 } else { 135 DLOG(ERROR) << "Response from Google Wallet missing card type"; 136 return scoped_ptr<MaskedInstrument>(); 137 } 138 139 base::string16 last_four_digits; 140 if (!dictionary.GetString("last_four_digits", &last_four_digits)) { 141 DLOG(ERROR) << "Response from Google Wallet missing last four digits"; 142 return scoped_ptr<MaskedInstrument>(); 143 } 144 145 std::string status_string; 146 Status status; 147 if (dictionary.GetString("status", &status_string)) { 148 status = StatusFromString(status_string); 149 } else { 150 DLOG(ERROR) << "Response from Google Wallet missing status"; 151 return scoped_ptr<MaskedInstrument>(); 152 } 153 154 std::string object_id; 155 if (!dictionary.GetString("object_id", &object_id)) { 156 DLOG(ERROR) << "Response from Google Wallet missing object id"; 157 return scoped_ptr<MaskedInstrument>(); 158 } 159 160 const base::DictionaryValue* address_dict; 161 if (!dictionary.GetDictionary("billing_address", &address_dict)) { 162 DLOG(ERROR) << "Response from Google wallet missing address"; 163 return scoped_ptr<MaskedInstrument>(); 164 } 165 scoped_ptr<Address> address = Address::CreateDisplayAddress(*address_dict); 166 167 if (!address) { 168 DLOG(ERROR) << "Response from Google wallet contained malformed address"; 169 return scoped_ptr<MaskedInstrument>(); 170 } 171 172 std::vector<base::string16> supported_currencies; 173 const base::ListValue* supported_currency_list; 174 if (dictionary.GetList("supported_currency", &supported_currency_list)) { 175 for (size_t i = 0; i < supported_currency_list->GetSize(); ++i) { 176 base::string16 currency; 177 if (supported_currency_list->GetString(i, ¤cy)) 178 supported_currencies.push_back(currency); 179 } 180 } else { 181 DVLOG(1) << "Response from Google Wallet missing supported currency"; 182 } 183 184 int expiration_month; 185 if (!dictionary.GetInteger("expiration_month", &expiration_month)) 186 DVLOG(1) << "Response from Google Wallet missing expiration month"; 187 188 int expiration_year; 189 if (!dictionary.GetInteger("expiration_year", &expiration_year)) 190 DVLOG(1) << "Response from Google Wallet missing expiration year"; 191 192 base::string16 descriptive_name; 193 if (!dictionary.GetString("descriptive_name", &descriptive_name)) 194 DVLOG(1) << "Response from Google Wallet missing descriptive name"; 195 196 return scoped_ptr<MaskedInstrument>(new MaskedInstrument(descriptive_name, 197 type, 198 supported_currencies, 199 last_four_digits, 200 expiration_month, 201 expiration_year, 202 address.Pass(), 203 status, 204 object_id)); 205} 206 207bool WalletItems::MaskedInstrument::operator==( 208 const WalletItems::MaskedInstrument& other) const { 209 if (descriptive_name_ != other.descriptive_name_) 210 return false; 211 if (type_ != other.type_) 212 return false; 213 if (supported_currencies_ != other.supported_currencies_) 214 return false; 215 if (last_four_digits_ != other.last_four_digits_) 216 return false; 217 if (expiration_month_ != other.expiration_month_) 218 return false; 219 if (expiration_year_ != other.expiration_year_) 220 return false; 221 if (address_) { 222 if (other.address_) { 223 if (*address_ != *other.address_) 224 return false; 225 } else { 226 return false; 227 } 228 } else if (other.address_) { 229 return false; 230 } 231 if (status_ != other.status_) 232 return false; 233 if (object_id_ != other.object_id_) 234 return false; 235 return true; 236} 237 238bool WalletItems::MaskedInstrument::operator!=( 239 const WalletItems::MaskedInstrument& other) const { 240 return !(*this == other); 241} 242 243const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById( 244 const std::string& object_id) const { 245 if (object_id.empty()) 246 return NULL; 247 248 for (size_t i = 0; i < instruments_.size(); ++i) { 249 if (instruments_[i]->object_id() == object_id) 250 return instruments_[i]; 251 } 252 253 return NULL; 254} 255 256bool WalletItems::HasRequiredAction(RequiredAction action) const { 257 DCHECK(ActionAppliesToWalletItems(action)); 258 return std::find(required_actions_.begin(), 259 required_actions_.end(), 260 action) != required_actions_.end(); 261} 262 263bool WalletItems::SupportsCard(const base::string16& card_number, 264 base::string16* message) const { 265 std::string card_type = CreditCard::GetCreditCardType(card_number); 266 267 if (card_type == kVisaCard || 268 card_type == kMasterCard || 269 card_type == kDiscoverCard) { 270 return true; 271 } 272 273 if (card_type == kAmericanExpressCard) { 274 if (amex_permission_ == AMEX_ALLOWED) 275 return true; 276 277 *message = l10n_util::GetStringUTF16( 278 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET_FOR_MERCHANT); 279 return false; 280 } 281 282 *message = l10n_util::GetStringUTF16( 283 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET); 284 return false; 285} 286 287std::string WalletItems::ObfuscatedGaiaId() const { 288 if (active_account_index_ >= gaia_accounts_.size()) 289 return std::string(); 290 291 return gaia_accounts_[active_account_index_]->obfuscated_id(); 292} 293 294base::string16 WalletItems::MaskedInstrument::DisplayName() const { 295#if defined(OS_ANDROID) 296 // TODO(aruslan): improve this stub implementation. 297 return descriptive_name(); 298#else 299 return descriptive_name(); 300#endif 301} 302 303base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const { 304#if defined(OS_ANDROID) 305 // TODO(aruslan): improve this stub implementation. 306 return address().DisplayName(); 307#else 308 return base::string16(); 309#endif 310} 311 312base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const { 313 // TODO(dbeam): i18n. 314 return DisplayStringFromType(type_) + base::ASCIIToUTF16(" - ") + 315 last_four_digits(); 316} 317 318const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const { 319 int idr = 0; 320 switch (type_) { 321 case AMEX: 322 idr = IDR_AUTOFILL_CC_AMEX; 323 break; 324 325 case DISCOVER: 326 idr = IDR_AUTOFILL_CC_DISCOVER; 327 break; 328 329 case MASTER_CARD: 330 idr = IDR_AUTOFILL_CC_MASTERCARD; 331 break; 332 333 case VISA: 334 idr = IDR_AUTOFILL_CC_VISA; 335 break; 336 337 case SOLO: 338 case MAESTRO: 339 case SWITCH: 340 case UNKNOWN: 341 idr = IDR_AUTOFILL_CC_GENERIC; 342 break; 343 } 344 345 return ResourceBundle::GetSharedInstance().GetImageNamed(idr); 346} 347 348base::string16 WalletItems::MaskedInstrument::GetInfo( 349 const AutofillType& type, 350 const std::string& app_locale) const { 351 if (type.group() != CREDIT_CARD) 352 return address().GetInfo(type, app_locale); 353 354 switch (type.GetStorableType()) { 355 case CREDIT_CARD_NAME: 356 return address().recipient_name(); 357 358 case CREDIT_CARD_NUMBER: 359 return DisplayName(); 360 361 case CREDIT_CARD_EXP_4_DIGIT_YEAR: 362 return base::IntToString16(expiration_year()); 363 364 case CREDIT_CARD_VERIFICATION_CODE: 365 break; 366 367 case CREDIT_CARD_TYPE: 368 return DisplayStringFromType(type_); 369 370 default: 371 NOTREACHED(); 372 } 373 374 return base::string16(); 375} 376 377WalletItems::LegalDocument::~LegalDocument() {} 378 379scoped_ptr<WalletItems::LegalDocument> 380 WalletItems::LegalDocument::CreateLegalDocument( 381 const base::DictionaryValue& dictionary) { 382 std::string id; 383 if (!dictionary.GetString("legal_document_id", &id)) { 384 DLOG(ERROR) << "Response from Google Wallet missing legal document id"; 385 return scoped_ptr<LegalDocument>(); 386 } 387 388 base::string16 display_name; 389 if (!dictionary.GetString("display_name", &display_name)) { 390 DLOG(ERROR) << "Response from Google Wallet missing display name"; 391 return scoped_ptr<LegalDocument>(); 392 } 393 394 return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name)); 395} 396 397scoped_ptr<WalletItems::LegalDocument> 398 WalletItems::LegalDocument::CreatePrivacyPolicyDocument() { 399 return scoped_ptr<LegalDocument>(new LegalDocument( 400 GURL(kPrivacyNoticeUrl), 401 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK))); 402} 403 404bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const { 405 return id_ == other.id_ && 406 url_ == other.url_ && 407 display_name_ == other.display_name_; 408} 409 410bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const { 411 return !(*this == other); 412} 413 414WalletItems::LegalDocument::LegalDocument(const std::string& id, 415 const base::string16& display_name) 416 : id_(id), 417 url_(kLegalDocumentUrl + id), 418 display_name_(display_name) {} 419 420WalletItems::LegalDocument::LegalDocument(const GURL& url, 421 const base::string16& display_name) 422 : url_(url), 423 display_name_(display_name) {} 424 425WalletItems::WalletItems(const std::vector<RequiredAction>& required_actions, 426 const std::string& google_transaction_id, 427 const std::string& default_instrument_id, 428 const std::string& default_address_id, 429 AmexPermission amex_permission) 430 : required_actions_(required_actions), 431 google_transaction_id_(google_transaction_id), 432 default_instrument_id_(default_instrument_id), 433 default_address_id_(default_address_id), 434 active_account_index_(std::numeric_limits<size_t>::max()), 435 amex_permission_(amex_permission) {} 436 437WalletItems::~WalletItems() {} 438 439scoped_ptr<WalletItems> 440 WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) { 441 std::vector<RequiredAction> required_action; 442 const base::ListValue* required_action_list; 443 if (dictionary.GetList("required_action", &required_action_list)) { 444 for (size_t i = 0; i < required_action_list->GetSize(); ++i) { 445 std::string action_string; 446 if (required_action_list->GetString(i, &action_string)) { 447 RequiredAction action = ParseRequiredActionFromString(action_string); 448 if (!ActionAppliesToWalletItems(action)) { 449 DLOG(ERROR) << "Response from Google wallet with bad required action:" 450 " \"" << action_string << "\""; 451 return scoped_ptr<WalletItems>(); 452 } 453 required_action.push_back(action); 454 } 455 } 456 } else { 457 DVLOG(1) << "Response from Google wallet missing required actions"; 458 } 459 460 std::string google_transaction_id; 461 if (!dictionary.GetString("google_transaction_id", &google_transaction_id) && 462 required_action.empty()) { 463 DLOG(ERROR) << "Response from Google wallet missing google transaction id"; 464 return scoped_ptr<WalletItems>(); 465 } 466 467 std::string default_instrument_id; 468 if (!dictionary.GetString("default_instrument_id", &default_instrument_id)) 469 DVLOG(1) << "Response from Google wallet missing default instrument id"; 470 471 std::string default_address_id; 472 if (!dictionary.GetString("default_address_id", &default_address_id)) 473 DVLOG(1) << "Response from Google wallet missing default_address_id"; 474 475 // obfuscated_gaia_id is deprecated. 476 477 bool amex_disallowed = true; 478 if (!dictionary.GetBoolean("amex_disallowed", &amex_disallowed)) 479 DVLOG(1) << "Response from Google wallet missing the amex_disallowed field"; 480 AmexPermission amex_permission = 481 amex_disallowed ? AMEX_DISALLOWED : AMEX_ALLOWED; 482 483 scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action, 484 google_transaction_id, 485 default_instrument_id, 486 default_address_id, 487 amex_permission)); 488 std::vector<std::string> gaia_accounts; 489 const base::ListValue* gaia_profiles; 490 if (dictionary.GetList("gaia_profile", &gaia_profiles)) { 491 for (size_t i = 0; i < gaia_profiles->GetSize(); ++i) { 492 const base::DictionaryValue* account_dict; 493 std::string email; 494 if (!gaia_profiles->GetDictionary(i, &account_dict)) 495 continue; 496 497 scoped_ptr<GaiaAccount> gaia_account( 498 GaiaAccount::Create(*account_dict)); 499 if (gaia_account) 500 wallet_items->AddAccount(gaia_account.Pass()); 501 } 502 } else { 503 DVLOG(1) << "Response from Google wallet missing GAIA accounts"; 504 } 505 506 const base::ListValue* legal_docs; 507 if (dictionary.GetList("required_legal_document", &legal_docs)) { 508 for (size_t i = 0; i < legal_docs->GetSize(); ++i) { 509 const base::DictionaryValue* legal_doc_dict; 510 if (legal_docs->GetDictionary(i, &legal_doc_dict)) { 511 scoped_ptr<LegalDocument> legal_doc( 512 LegalDocument::CreateLegalDocument(*legal_doc_dict)); 513 if (legal_doc) 514 wallet_items->AddLegalDocument(legal_doc.Pass()); 515 else 516 return scoped_ptr<WalletItems>(); 517 } 518 } 519 520 if (!legal_docs->empty()) { 521 // Always append the privacy policy link as well. 522 wallet_items->AddLegalDocument( 523 LegalDocument::CreatePrivacyPolicyDocument()); 524 } 525 } else { 526 DVLOG(1) << "Response from Google wallet missing legal docs"; 527 } 528 529 const base::ListValue* instruments; 530 if (dictionary.GetList("instrument", &instruments)) { 531 for (size_t i = 0; i < instruments->GetSize(); ++i) { 532 const base::DictionaryValue* instrument_dict; 533 if (instruments->GetDictionary(i, &instrument_dict)) { 534 scoped_ptr<MaskedInstrument> instrument( 535 MaskedInstrument::CreateMaskedInstrument(*instrument_dict)); 536 if (instrument) 537 wallet_items->AddInstrument(instrument.Pass()); 538 } 539 } 540 } else { 541 DVLOG(1) << "Response from Google wallet missing instruments"; 542 } 543 544 const base::ListValue* addresses; 545 if (dictionary.GetList("address", &addresses)) { 546 for (size_t i = 0; i < addresses->GetSize(); ++i) { 547 const base::DictionaryValue* address_dict; 548 if (addresses->GetDictionary(i, &address_dict)) { 549 scoped_ptr<Address> address( 550 Address::CreateAddressWithID(*address_dict)); 551 if (address) 552 wallet_items->AddAddress(address.Pass()); 553 } 554 } 555 } else { 556 DVLOG(1) << "Response from Google wallet missing addresses"; 557 } 558 559 return wallet_items.Pass(); 560} 561 562void WalletItems::AddAccount(scoped_ptr<GaiaAccount> account) { 563 if (account->index() != gaia_accounts_.size()) { 564 DVLOG(1) << "Tried to add account out of order"; 565 return; 566 } 567 568 if (account->is_active()) 569 active_account_index_ = account->index(); 570 571 gaia_accounts_.push_back(account.release()); 572} 573 574bool WalletItems::operator==(const WalletItems& other) const { 575 return google_transaction_id_ == other.google_transaction_id_ && 576 default_instrument_id_ == other.default_instrument_id_ && 577 default_address_id_ == other.default_address_id_ && 578 required_actions_ == other.required_actions_ && 579 // This check is technically redundant, but is useful for tests. 580 ObfuscatedGaiaId() == other.ObfuscatedGaiaId() && 581 active_account_index() == other.active_account_index() && 582 VectorsAreEqual<GaiaAccount>(gaia_accounts(), 583 other.gaia_accounts()) && 584 VectorsAreEqual<MaskedInstrument>(instruments(), 585 other.instruments()) && 586 VectorsAreEqual<Address>(addresses(), other.addresses()) && 587 VectorsAreEqual<LegalDocument>(legal_documents(), 588 other.legal_documents()); 589} 590 591bool WalletItems::operator!=(const WalletItems& other) const { 592 return !(*this == other); 593} 594 595} // namespace wallet 596} // namespace autofill 597