wallet_items.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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 260bool WalletItems::SupportsCard(const base::string16& card_number, 261 base::string16* message) const { 262 std::string card_type = CreditCard::GetCreditCardType(card_number); 263 264 if (card_type == kVisaCard || 265 card_type == kMasterCard || 266 card_type == kDiscoverCard) { 267 return true; 268 } 269 270 if (card_type == kAmericanExpressCard) { 271 if (amex_permission_ == AMEX_ALLOWED) 272 return true; 273 274 *message = l10n_util::GetStringUTF16( 275 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET_FOR_MERCHANT); 276 return false; 277 } 278 279 *message = l10n_util::GetStringUTF16( 280 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET); 281 return false; 282} 283 284base::string16 WalletItems::MaskedInstrument::DisplayName() const { 285#if defined(OS_ANDROID) 286 // TODO(aruslan): improve this stub implementation. 287 return descriptive_name(); 288#else 289 return descriptive_name(); 290#endif 291} 292 293base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const { 294#if defined(OS_ANDROID) 295 // TODO(aruslan): improve this stub implementation. 296 return address().DisplayName(); 297#else 298 return base::string16(); 299#endif 300} 301 302base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const { 303 // TODO(dbeam): i18n. 304 return DisplayStringFromType(type_) + ASCIIToUTF16(" - ") + 305 last_four_digits(); 306} 307 308const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const { 309 int idr = 0; 310 switch (type_) { 311 case AMEX: 312 idr = IDR_AUTOFILL_CC_AMEX; 313 break; 314 315 case DISCOVER: 316 idr = IDR_AUTOFILL_CC_DISCOVER; 317 break; 318 319 case MASTER_CARD: 320 idr = IDR_AUTOFILL_CC_MASTERCARD; 321 break; 322 323 case VISA: 324 idr = IDR_AUTOFILL_CC_VISA; 325 break; 326 327 case SOLO: 328 case MAESTRO: 329 case SWITCH: 330 case UNKNOWN: 331 idr = IDR_AUTOFILL_CC_GENERIC; 332 break; 333 } 334 335 return ResourceBundle::GetSharedInstance().GetImageNamed(idr); 336} 337 338base::string16 WalletItems::MaskedInstrument::GetInfo( 339 const AutofillType& type, 340 const std::string& app_locale) const { 341 if (type.group() != CREDIT_CARD) 342 return address().GetInfo(type, app_locale); 343 344 switch (type.GetStorableType()) { 345 case CREDIT_CARD_NAME: 346 return address().recipient_name(); 347 348 case CREDIT_CARD_NUMBER: 349 return DisplayName(); 350 351 case CREDIT_CARD_EXP_4_DIGIT_YEAR: 352 return base::IntToString16(expiration_year()); 353 354 case CREDIT_CARD_VERIFICATION_CODE: 355 break; 356 357 case CREDIT_CARD_TYPE: 358 return DisplayStringFromType(type_); 359 360 default: 361 NOTREACHED(); 362 } 363 364 return base::string16(); 365} 366 367WalletItems::LegalDocument::~LegalDocument() {} 368 369scoped_ptr<WalletItems::LegalDocument> 370 WalletItems::LegalDocument::CreateLegalDocument( 371 const base::DictionaryValue& dictionary) { 372 std::string id; 373 if (!dictionary.GetString("legal_document_id", &id)) { 374 DLOG(ERROR) << "Response from Google Wallet missing legal document id"; 375 return scoped_ptr<LegalDocument>(); 376 } 377 378 base::string16 display_name; 379 if (!dictionary.GetString("display_name", &display_name)) { 380 DLOG(ERROR) << "Response from Google Wallet missing display name"; 381 return scoped_ptr<LegalDocument>(); 382 } 383 384 return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name)); 385} 386 387scoped_ptr<WalletItems::LegalDocument> 388 WalletItems::LegalDocument::CreatePrivacyPolicyDocument() { 389 return scoped_ptr<LegalDocument>(new LegalDocument( 390 GURL(kPrivacyNoticeUrl), 391 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK))); 392} 393 394bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const { 395 return id_ == other.id_ && 396 url_ == other.url_ && 397 display_name_ == other.display_name_; 398} 399 400bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const { 401 return !(*this == other); 402} 403 404WalletItems::LegalDocument::LegalDocument(const std::string& id, 405 const base::string16& display_name) 406 : id_(id), 407 url_(kLegalDocumentUrl + id), 408 display_name_(display_name) {} 409 410WalletItems::LegalDocument::LegalDocument(const GURL& url, 411 const base::string16& display_name) 412 : url_(url), 413 display_name_(display_name) {} 414 415WalletItems::WalletItems(const std::vector<RequiredAction>& required_actions, 416 const std::string& google_transaction_id, 417 const std::string& default_instrument_id, 418 const std::string& default_address_id, 419 const std::string& obfuscated_gaia_id, 420 AmexPermission amex_permission) 421 : required_actions_(required_actions), 422 google_transaction_id_(google_transaction_id), 423 default_instrument_id_(default_instrument_id), 424 default_address_id_(default_address_id), 425 obfuscated_gaia_id_(obfuscated_gaia_id), 426 amex_permission_(amex_permission) {} 427 428WalletItems::~WalletItems() {} 429 430scoped_ptr<WalletItems> 431 WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) { 432 std::vector<RequiredAction> required_action; 433 const ListValue* required_action_list; 434 if (dictionary.GetList("required_action", &required_action_list)) { 435 for (size_t i = 0; i < required_action_list->GetSize(); ++i) { 436 std::string action_string; 437 if (required_action_list->GetString(i, &action_string)) { 438 RequiredAction action = ParseRequiredActionFromString(action_string); 439 if (!ActionAppliesToWalletItems(action)) { 440 DLOG(ERROR) << "Response from Google wallet with bad required action:" 441 " \"" << action_string << "\""; 442 return scoped_ptr<WalletItems>(); 443 } 444 required_action.push_back(action); 445 } 446 } 447 } else { 448 DVLOG(1) << "Response from Google wallet missing required actions"; 449 } 450 451 std::string google_transaction_id; 452 if (!dictionary.GetString("google_transaction_id", &google_transaction_id) && 453 required_action.empty()) { 454 DLOG(ERROR) << "Response from Google wallet missing google transaction id"; 455 return scoped_ptr<WalletItems>(); 456 } 457 458 std::string default_instrument_id; 459 if (!dictionary.GetString("default_instrument_id", &default_instrument_id)) 460 DVLOG(1) << "Response from Google wallet missing default instrument id"; 461 462 std::string default_address_id; 463 if (!dictionary.GetString("default_address_id", &default_address_id)) 464 DVLOG(1) << "Response from Google wallet missing default_address_id"; 465 466 std::string obfuscated_gaia_id; 467 if (!dictionary.GetString("obfuscated_gaia_id", &obfuscated_gaia_id)) 468 DVLOG(1) << "Response from Google wallet missing obfuscated gaia id"; 469 470 bool amex_disallowed = true; 471 if (!dictionary.GetBoolean("amex_disallowed", &amex_disallowed)) 472 DVLOG(1) << "Response from Google wallet missing the amex_disallowed field"; 473 AmexPermission amex_permission = 474 amex_disallowed ? AMEX_DISALLOWED : AMEX_ALLOWED; 475 476 scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action, 477 google_transaction_id, 478 default_instrument_id, 479 default_address_id, 480 obfuscated_gaia_id, 481 amex_permission)); 482 483 const ListValue* legal_docs; 484 if (dictionary.GetList("required_legal_document", &legal_docs)) { 485 for (size_t i = 0; i < legal_docs->GetSize(); ++i) { 486 const DictionaryValue* legal_doc_dict; 487 if (legal_docs->GetDictionary(i, &legal_doc_dict)) { 488 scoped_ptr<LegalDocument> legal_doc( 489 LegalDocument::CreateLegalDocument(*legal_doc_dict)); 490 if (legal_doc.get()) { 491 wallet_items->AddLegalDocument(legal_doc.Pass()); 492 } else { 493 DLOG(ERROR) << "Malformed legal document in response from " 494 "Google wallet"; 495 return scoped_ptr<WalletItems>(); 496 } 497 } 498 } 499 500 if (!legal_docs->empty()) { 501 // Always append the privacy policy link as well. 502 wallet_items->AddLegalDocument( 503 LegalDocument::CreatePrivacyPolicyDocument()); 504 } 505 } else { 506 DVLOG(1) << "Response from Google wallet missing legal docs"; 507 } 508 509 const ListValue* instruments; 510 if (dictionary.GetList("instrument", &instruments)) { 511 for (size_t i = 0; i < instruments->GetSize(); ++i) { 512 const DictionaryValue* instrument_dict; 513 if (instruments->GetDictionary(i, &instrument_dict)) { 514 scoped_ptr<MaskedInstrument> instrument( 515 MaskedInstrument::CreateMaskedInstrument(*instrument_dict)); 516 if (instrument.get()) 517 wallet_items->AddInstrument(instrument.Pass()); 518 else 519 DLOG(ERROR) << "Malformed instrument in response from Google Wallet"; 520 } 521 } 522 } else { 523 DVLOG(1) << "Response from Google wallet missing instruments"; 524 } 525 526 const ListValue* addresses; 527 if (dictionary.GetList("address", &addresses)) { 528 for (size_t i = 0; i < addresses->GetSize(); ++i) { 529 const DictionaryValue* address_dict; 530 if (addresses->GetDictionary(i, &address_dict)) { 531 scoped_ptr<Address> address( 532 Address::CreateAddressWithID(*address_dict)); 533 if (address.get()) 534 wallet_items->AddAddress(address.Pass()); 535 else 536 DLOG(ERROR) << "Malformed address in response from Google Wallet"; 537 } 538 } 539 } else { 540 DVLOG(1) << "Response from Google wallet missing addresses"; 541 } 542 543 return wallet_items.Pass(); 544} 545 546bool WalletItems::operator==(const WalletItems& other) const { 547 return google_transaction_id_ == other.google_transaction_id_ && 548 default_instrument_id_ == other.default_instrument_id_ && 549 default_address_id_ == other.default_address_id_ && 550 required_actions_ == other.required_actions_ && 551 obfuscated_gaia_id_ == other.obfuscated_gaia_id_ && 552 VectorsAreEqual<MaskedInstrument>(instruments(), 553 other.instruments()) && 554 VectorsAreEqual<Address>(addresses(), other.addresses()) && 555 VectorsAreEqual<LegalDocument>(legal_documents(), 556 other.legal_documents()); 557} 558 559bool WalletItems::operator!=(const WalletItems& other) const { 560 return !(*this == other); 561} 562 563} // namespace wallet 564} // namespace autofill 565