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