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.
5 #include "components/autofill/content/browser/wallet/wallet_items.h"
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"
25 const char kLegalDocumentUrl[] =
26 "https://wallet.google.com/legaldocument?docId=";
27 const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html";
29 // TODO(estade): move to base/.
31 bool VectorsAreEqual(const std::vector<T*>& a, const std::vector<T*>& b) {
32 if (a.size() != b.size())
35 for (size_t i = 0; i < a.size(); ++i) {
43 WalletItems::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;
62 WalletItems::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;
83 base::string16 DisplayStringFromType(WalletItems::MaskedInstrument::Type 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);
94 return CreditCard::TypeForDisplay(kGenericCard);
98 } // anonymous namespace
100 WalletItems::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,
107 scoped_ptr<Address> address,
108 const WalletItems::MaskedInstrument::Status& status,
109 const std::string& object_id)
110 : descriptive_name_(descriptive_name),
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()),
118 object_id_(object_id) {
119 DCHECK(address_.get());
122 WalletItems::MaskedInstrument::~MaskedInstrument() {}
124 scoped_ptr<WalletItems::MaskedInstrument>
125 WalletItems::MaskedInstrument::CreateMaskedInstrument(
126 const base::DictionaryValue& dictionary) {
127 std::string type_string;
129 if (dictionary.GetString("type", &type_string)) {
130 type = TypeFromString(type_string);
132 DLOG(ERROR) << "Response from Google Wallet missing card type";
133 return scoped_ptr<MaskedInstrument>();
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>();
142 std::string status_string;
144 if (dictionary.GetString("status", &status_string)) {
145 status = StatusFromString(status_string);
147 DLOG(ERROR) << "Response from Google Wallet missing status";
148 return scoped_ptr<MaskedInstrument>();
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>();
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>();
162 scoped_ptr<Address> address = Address::CreateDisplayAddress(*address_dict);
164 if (!address.get()) {
165 DLOG(ERROR) << "Response from Google wallet contained malformed address";
166 return scoped_ptr<MaskedInstrument>();
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);
178 DVLOG(1) << "Response from Google Wallet missing supported currency";
181 int expiration_month;
182 if (!dictionary.GetInteger("expiration_month", &expiration_month))
183 DVLOG(1) << "Response from Google Wallet missing expiration month";
186 if (!dictionary.GetInteger("expiration_year", &expiration_year))
187 DVLOG(1) << "Response from Google Wallet missing expiration year";
189 base::string16 descriptive_name;
190 if (!dictionary.GetString("descriptive_name", &descriptive_name))
191 DVLOG(1) << "Response from Google Wallet missing descriptive name";
193 return scoped_ptr<MaskedInstrument>(new MaskedInstrument(descriptive_name,
195 supported_currencies,
204 bool WalletItems::MaskedInstrument::operator==(
205 const WalletItems::MaskedInstrument& other) const {
206 if (descriptive_name_ != other.descriptive_name_)
208 if (type_ != other.type_)
210 if (supported_currencies_ != other.supported_currencies_)
212 if (last_four_digits_ != other.last_four_digits_)
214 if (expiration_month_ != other.expiration_month_)
216 if (expiration_year_ != other.expiration_year_)
218 if (address_.get()) {
219 if (other.address_.get()) {
220 if (*address_.get() != *other.address_.get())
225 } else if (other.address_.get()) {
228 if (status_ != other.status_)
230 if (object_id_ != other.object_id_)
235 bool WalletItems::MaskedInstrument::operator!=(
236 const WalletItems::MaskedInstrument& other) const {
237 return !(*this == other);
240 const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById(
241 const std::string& object_id) const {
242 if (object_id.empty())
245 for (size_t i = 0; i < instruments_.size(); ++i) {
246 if (instruments_[i]->object_id() == object_id)
247 return instruments_[i];
253 bool 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();
260 bool WalletItems::SupportsCard(const base::string16& card_number,
261 base::string16* message) const {
262 std::string card_type = CreditCard::GetCreditCardType(card_number);
264 if (card_type == kVisaCard ||
265 card_type == kMasterCard ||
266 card_type == kDiscoverCard) {
270 if (card_type == kAmericanExpressCard) {
271 if (amex_permission_ == AMEX_ALLOWED)
274 *message = l10n_util::GetStringUTF16(
275 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET_FOR_MERCHANT);
279 *message = l10n_util::GetStringUTF16(
280 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET);
284 base::string16 WalletItems::MaskedInstrument::DisplayName() const {
285 #if defined(OS_ANDROID)
286 // TODO(aruslan): improve this stub implementation.
287 return descriptive_name();
289 return descriptive_name();
293 base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const {
294 #if defined(OS_ANDROID)
295 // TODO(aruslan): improve this stub implementation.
296 return address().DisplayName();
298 return base::string16();
302 base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const {
303 // TODO(dbeam): i18n.
304 return DisplayStringFromType(type_) + ASCIIToUTF16(" - ") +
308 const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const {
312 idr = IDR_AUTOFILL_CC_AMEX;
316 idr = IDR_AUTOFILL_CC_DISCOVER;
320 idr = IDR_AUTOFILL_CC_MASTERCARD;
324 idr = IDR_AUTOFILL_CC_VISA;
331 idr = IDR_AUTOFILL_CC_GENERIC;
335 return ResourceBundle::GetSharedInstance().GetImageNamed(idr);
338 base::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);
344 switch (type.GetStorableType()) {
345 case CREDIT_CARD_NAME:
346 return address().recipient_name();
348 case CREDIT_CARD_NUMBER:
349 return DisplayName();
351 case CREDIT_CARD_EXP_4_DIGIT_YEAR:
352 return base::IntToString16(expiration_year());
354 case CREDIT_CARD_VERIFICATION_CODE:
357 case CREDIT_CARD_TYPE:
358 return DisplayStringFromType(type_);
364 return base::string16();
367 WalletItems::LegalDocument::~LegalDocument() {}
369 scoped_ptr<WalletItems::LegalDocument>
370 WalletItems::LegalDocument::CreateLegalDocument(
371 const base::DictionaryValue& dictionary) {
373 if (!dictionary.GetString("legal_document_id", &id)) {
374 DLOG(ERROR) << "Response from Google Wallet missing legal document id";
375 return scoped_ptr<LegalDocument>();
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>();
384 return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name));
387 scoped_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)));
394 bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
395 return id_ == other.id_ &&
396 url_ == other.url_ &&
397 display_name_ == other.display_name_;
400 bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
401 return !(*this == other);
404 WalletItems::LegalDocument::LegalDocument(const std::string& id,
405 const base::string16& display_name)
407 url_(kLegalDocumentUrl + id),
408 display_name_(display_name) {}
410 WalletItems::LegalDocument::LegalDocument(const GURL& url,
411 const base::string16& display_name)
413 display_name_(display_name) {}
415 WalletItems::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) {}
428 WalletItems::~WalletItems() {}
430 scoped_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>();
444 required_action.push_back(action);
448 DVLOG(1) << "Response from Google wallet missing required actions";
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>();
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";
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";
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";
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;
476 scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action,
477 google_transaction_id,
478 default_instrument_id,
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());
493 DLOG(ERROR) << "Malformed legal document in response from "
495 return scoped_ptr<WalletItems>();
500 if (!legal_docs->empty()) {
501 // Always append the privacy policy link as well.
502 wallet_items->AddLegalDocument(
503 LegalDocument::CreatePrivacyPolicyDocument());
506 DVLOG(1) << "Response from Google wallet missing legal docs";
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());
519 DLOG(ERROR) << "Malformed instrument in response from Google Wallet";
523 DVLOG(1) << "Response from Google wallet missing instruments";
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));
534 wallet_items->AddAddress(address.Pass());
536 DLOG(ERROR) << "Malformed address in response from Google Wallet";
540 DVLOG(1) << "Response from Google wallet missing addresses";
543 return wallet_items.Pass();
546 bool 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());
559 bool WalletItems::operator!=(const WalletItems& other) const {
560 return !(*this == other);
563 } // namespace wallet
564 } // namespace autofill