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