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"
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_scaled_resources.h"
17 #include "grit/components_strings.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/image/image.h"
28 const char kLegalDocumentUrl
[] =
29 "https://wallet.google.com/legaldocument?docId=";
30 const char kPrivacyNoticeUrl
[] = "https://wallet.google.com/files/privacy.html";
32 // TODO(estade): move to base/.
34 bool VectorsAreEqual(const std::vector
<T
*>& a
, const std::vector
<T
*>& b
) {
35 if (a
.size() != b
.size())
38 for (size_t i
= 0; i
< a
.size(); ++i
) {
46 WalletItems::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
;
65 WalletItems::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
;
86 base::string16
DisplayStringFromType(WalletItems::MaskedInstrument::Type 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
);
97 return CreditCard::TypeForDisplay(kGenericCard
);
101 } // anonymous namespace
103 WalletItems::MaskedInstrument::MaskedInstrument(
104 const base::string16
& descriptive_name
,
105 const WalletItems::MaskedInstrument::Type
& type
,
106 const base::string16
& last_four_digits
,
107 int expiration_month
,
109 scoped_ptr
<Address
> address
,
110 const WalletItems::MaskedInstrument::Status
& status
,
111 const std::string
& object_id
)
112 : descriptive_name_(descriptive_name
),
114 last_four_digits_(last_four_digits
),
115 expiration_month_(expiration_month
),
116 expiration_year_(expiration_year
),
117 address_(address
.Pass()),
119 object_id_(object_id
) {
123 WalletItems::MaskedInstrument::~MaskedInstrument() {}
125 scoped_ptr
<WalletItems::MaskedInstrument
>
126 WalletItems::MaskedInstrument::CreateMaskedInstrument(
127 const base::DictionaryValue
& dictionary
) {
128 std::string type_string
;
130 if (dictionary
.GetString("type", &type_string
)) {
131 type
= TypeFromString(type_string
);
133 DLOG(ERROR
) << "Response from Google Wallet missing card type";
134 return scoped_ptr
<MaskedInstrument
>();
137 base::string16 last_four_digits
;
138 if (!dictionary
.GetString("last_four_digits", &last_four_digits
)) {
139 DLOG(ERROR
) << "Response from Google Wallet missing last four digits";
140 return scoped_ptr
<MaskedInstrument
>();
143 std::string status_string
;
145 if (dictionary
.GetString("status", &status_string
)) {
146 status
= StatusFromString(status_string
);
148 DLOG(ERROR
) << "Response from Google Wallet missing status";
149 return scoped_ptr
<MaskedInstrument
>();
152 std::string object_id
;
153 if (!dictionary
.GetString("object_id", &object_id
)) {
154 DLOG(ERROR
) << "Response from Google Wallet missing object id";
155 return scoped_ptr
<MaskedInstrument
>();
158 const base::DictionaryValue
* address_dict
;
159 if (!dictionary
.GetDictionary("billing_address", &address_dict
)) {
160 DLOG(ERROR
) << "Response from Google wallet missing address";
161 return scoped_ptr
<MaskedInstrument
>();
163 scoped_ptr
<Address
> address
= Address::CreateDisplayAddress(*address_dict
);
166 DLOG(ERROR
) << "Response from Google wallet contained malformed address";
167 return scoped_ptr
<MaskedInstrument
>();
170 int expiration_month
;
171 if (!dictionary
.GetInteger("expiration_month", &expiration_month
))
172 DVLOG(1) << "Response from Google Wallet missing expiration month";
175 if (!dictionary
.GetInteger("expiration_year", &expiration_year
))
176 DVLOG(1) << "Response from Google Wallet missing expiration year";
178 base::string16 descriptive_name
;
179 if (!dictionary
.GetString("descriptive_name", &descriptive_name
))
180 DVLOG(1) << "Response from Google Wallet missing descriptive name";
182 return scoped_ptr
<MaskedInstrument
>(new MaskedInstrument(descriptive_name
,
192 bool WalletItems::MaskedInstrument::operator==(
193 const WalletItems::MaskedInstrument
& other
) const {
194 if (descriptive_name_
!= other
.descriptive_name_
)
196 if (type_
!= other
.type_
)
198 if (last_four_digits_
!= other
.last_four_digits_
)
200 if (expiration_month_
!= other
.expiration_month_
)
202 if (expiration_year_
!= other
.expiration_year_
)
205 if (other
.address_
) {
206 if (*address_
!= *other
.address_
)
211 } else if (other
.address_
) {
214 if (status_
!= other
.status_
)
216 if (object_id_
!= other
.object_id_
)
221 bool WalletItems::MaskedInstrument::operator!=(
222 const WalletItems::MaskedInstrument
& other
) const {
223 return !(*this == other
);
226 const WalletItems::MaskedInstrument
* WalletItems::GetInstrumentById(
227 const std::string
& object_id
) const {
228 if (object_id
.empty())
231 for (size_t i
= 0; i
< instruments_
.size(); ++i
) {
232 if (instruments_
[i
]->object_id() == object_id
)
233 return instruments_
[i
];
239 bool WalletItems::HasRequiredAction(RequiredAction action
) const {
240 DCHECK(ActionAppliesToWalletItems(action
));
241 return std::find(required_actions_
.begin(),
242 required_actions_
.end(),
243 action
) != required_actions_
.end();
246 bool WalletItems::SupportsCard(const base::string16
& card_number
,
247 base::string16
* message
) const {
248 const char* const card_type
= CreditCard::GetCreditCardType(card_number
);
250 if (card_type
== kVisaCard
||
251 card_type
== kMasterCard
||
252 card_type
== kDiscoverCard
) {
256 if (card_type
== kAmericanExpressCard
) {
257 if (amex_permission_
== AMEX_ALLOWED
)
260 *message
= l10n_util::GetStringUTF16(
261 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET_FOR_MERCHANT
);
265 *message
= l10n_util::GetStringUTF16(
266 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET
);
270 std::string
WalletItems::ObfuscatedGaiaId() const {
271 if (active_account_index_
>= gaia_accounts_
.size())
272 return std::string();
274 return gaia_accounts_
[active_account_index_
]->obfuscated_id();
277 base::string16
WalletItems::MaskedInstrument::DisplayName() const {
278 #if defined(OS_ANDROID)
279 // TODO(aruslan): improve this stub implementation.
280 return descriptive_name();
282 return descriptive_name();
286 base::string16
WalletItems::MaskedInstrument::DisplayNameDetail() const {
287 #if defined(OS_ANDROID)
288 // TODO(aruslan): improve this stub implementation.
289 return address().DisplayName();
291 return base::string16();
295 base::string16
WalletItems::MaskedInstrument::TypeAndLastFourDigits() const {
296 // TODO(dbeam): i18n.
297 return DisplayStringFromType(type_
) + base::ASCIIToUTF16(" - ") +
301 const gfx::Image
& WalletItems::MaskedInstrument::CardIcon() const {
305 idr
= IDR_AUTOFILL_CC_AMEX
;
309 idr
= IDR_AUTOFILL_CC_DISCOVER
;
313 idr
= IDR_AUTOFILL_CC_MASTERCARD
;
317 idr
= IDR_AUTOFILL_CC_VISA
;
324 idr
= IDR_AUTOFILL_CC_GENERIC
;
328 return ResourceBundle::GetSharedInstance().GetImageNamed(idr
);
331 base::string16
WalletItems::MaskedInstrument::GetInfo(
332 const AutofillType
& type
,
333 const std::string
& app_locale
) const {
334 if (type
.group() != CREDIT_CARD
)
335 return address().GetInfo(type
, app_locale
);
337 switch (type
.GetStorableType()) {
338 case CREDIT_CARD_NAME
:
339 return address().recipient_name();
341 case CREDIT_CARD_NUMBER
:
342 return DisplayName();
344 case CREDIT_CARD_EXP_4_DIGIT_YEAR
:
345 return base::IntToString16(expiration_year());
347 case CREDIT_CARD_VERIFICATION_CODE
:
350 case CREDIT_CARD_TYPE
:
351 return DisplayStringFromType(type_
);
357 return base::string16();
360 WalletItems::LegalDocument::~LegalDocument() {}
362 scoped_ptr
<WalletItems::LegalDocument
>
363 WalletItems::LegalDocument::CreateLegalDocument(
364 const base::DictionaryValue
& dictionary
) {
366 if (!dictionary
.GetString("legal_document_id", &id
)) {
367 DLOG(ERROR
) << "Response from Google Wallet missing legal document id";
368 return scoped_ptr
<LegalDocument
>();
371 base::string16 display_name
;
372 if (!dictionary
.GetString("display_name", &display_name
)) {
373 DLOG(ERROR
) << "Response from Google Wallet missing display name";
374 return scoped_ptr
<LegalDocument
>();
377 return scoped_ptr
<LegalDocument
>(new LegalDocument(id
, display_name
));
380 scoped_ptr
<WalletItems::LegalDocument
>
381 WalletItems::LegalDocument::CreatePrivacyPolicyDocument() {
382 return scoped_ptr
<LegalDocument
>(new LegalDocument(
383 GURL(kPrivacyNoticeUrl
),
384 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK
)));
387 bool WalletItems::LegalDocument::operator==(const LegalDocument
& other
) const {
388 return id_
== other
.id_
&&
389 url_
== other
.url_
&&
390 display_name_
== other
.display_name_
;
393 bool WalletItems::LegalDocument::operator!=(const LegalDocument
& other
) const {
394 return !(*this == other
);
397 WalletItems::LegalDocument::LegalDocument(const std::string
& id
,
398 const base::string16
& display_name
)
400 url_(kLegalDocumentUrl
+ id
),
401 display_name_(display_name
) {}
403 WalletItems::LegalDocument::LegalDocument(const GURL
& url
,
404 const base::string16
& display_name
)
406 display_name_(display_name
) {}
408 WalletItems::WalletItems(const std::vector
<RequiredAction
>& required_actions
,
409 const std::string
& google_transaction_id
,
410 const std::string
& default_instrument_id
,
411 const std::string
& default_address_id
,
412 AmexPermission amex_permission
)
413 : required_actions_(required_actions
),
414 google_transaction_id_(google_transaction_id
),
415 default_instrument_id_(default_instrument_id
),
416 default_address_id_(default_address_id
),
417 active_account_index_(std::numeric_limits
<size_t>::max()),
418 amex_permission_(amex_permission
) {}
420 WalletItems::~WalletItems() {}
422 scoped_ptr
<WalletItems
>
423 WalletItems::CreateWalletItems(const base::DictionaryValue
& dictionary
) {
424 std::vector
<RequiredAction
> required_action
;
425 const base::ListValue
* required_action_list
;
426 if (dictionary
.GetList("required_action", &required_action_list
)) {
427 for (size_t i
= 0; i
< required_action_list
->GetSize(); ++i
) {
428 std::string action_string
;
429 if (required_action_list
->GetString(i
, &action_string
)) {
430 RequiredAction action
= ParseRequiredActionFromString(action_string
);
431 if (!ActionAppliesToWalletItems(action
)) {
432 DLOG(ERROR
) << "Response from Google wallet with bad required action:"
433 " \"" << action_string
<< "\"";
434 return scoped_ptr
<WalletItems
>();
436 required_action
.push_back(action
);
440 DVLOG(1) << "Response from Google wallet missing required actions";
443 std::string google_transaction_id
;
444 if (!dictionary
.GetString("google_transaction_id", &google_transaction_id
) &&
445 required_action
.empty()) {
446 DLOG(ERROR
) << "Response from Google wallet missing google transaction id";
447 return scoped_ptr
<WalletItems
>();
450 std::string default_instrument_id
;
451 if (!dictionary
.GetString("default_instrument_id", &default_instrument_id
))
452 DVLOG(1) << "Response from Google wallet missing default instrument id";
454 std::string default_address_id
;
455 if (!dictionary
.GetString("default_address_id", &default_address_id
))
456 DVLOG(1) << "Response from Google wallet missing default_address_id";
458 // obfuscated_gaia_id is deprecated.
460 bool amex_disallowed
= true;
461 if (!dictionary
.GetBoolean("amex_disallowed", &amex_disallowed
))
462 DVLOG(1) << "Response from Google wallet missing the amex_disallowed field";
463 AmexPermission amex_permission
=
464 amex_disallowed
? AMEX_DISALLOWED
: AMEX_ALLOWED
;
466 scoped_ptr
<WalletItems
> wallet_items(new WalletItems(required_action
,
467 google_transaction_id
,
468 default_instrument_id
,
471 std::vector
<std::string
> gaia_accounts
;
472 const base::ListValue
* gaia_profiles
;
473 if (dictionary
.GetList("gaia_profile", &gaia_profiles
)) {
474 for (size_t i
= 0; i
< gaia_profiles
->GetSize(); ++i
) {
475 const base::DictionaryValue
* account_dict
;
477 if (!gaia_profiles
->GetDictionary(i
, &account_dict
))
480 scoped_ptr
<GaiaAccount
> gaia_account(
481 GaiaAccount::Create(*account_dict
));
483 wallet_items
->AddAccount(gaia_account
.Pass());
486 DVLOG(1) << "Response from Google wallet missing GAIA accounts";
489 const base::ListValue
* legal_docs
;
490 if (dictionary
.GetList("required_legal_document", &legal_docs
)) {
491 for (size_t i
= 0; i
< legal_docs
->GetSize(); ++i
) {
492 const base::DictionaryValue
* legal_doc_dict
;
493 if (legal_docs
->GetDictionary(i
, &legal_doc_dict
)) {
494 scoped_ptr
<LegalDocument
> legal_doc(
495 LegalDocument::CreateLegalDocument(*legal_doc_dict
));
497 wallet_items
->AddLegalDocument(legal_doc
.Pass());
499 return scoped_ptr
<WalletItems
>();
503 if (!legal_docs
->empty()) {
504 // Always append the privacy policy link as well.
505 wallet_items
->AddLegalDocument(
506 LegalDocument::CreatePrivacyPolicyDocument());
509 DVLOG(1) << "Response from Google wallet missing legal docs";
512 const base::ListValue
* instruments
;
513 if (dictionary
.GetList("instrument", &instruments
)) {
514 for (size_t i
= 0; i
< instruments
->GetSize(); ++i
) {
515 const base::DictionaryValue
* instrument_dict
;
516 if (instruments
->GetDictionary(i
, &instrument_dict
)) {
517 scoped_ptr
<MaskedInstrument
> instrument(
518 MaskedInstrument::CreateMaskedInstrument(*instrument_dict
));
520 wallet_items
->AddInstrument(instrument
.Pass());
524 DVLOG(1) << "Response from Google wallet missing instruments";
527 const base::ListValue
* addresses
;
528 if (dictionary
.GetList("address", &addresses
)) {
529 for (size_t i
= 0; i
< addresses
->GetSize(); ++i
) {
530 const base::DictionaryValue
* address_dict
;
531 if (addresses
->GetDictionary(i
, &address_dict
)) {
532 scoped_ptr
<Address
> address(
533 Address::CreateAddressWithID(*address_dict
));
535 wallet_items
->AddAddress(address
.Pass());
539 DVLOG(1) << "Response from Google wallet missing addresses";
542 const base::ListValue
* allowed_shipping_countries
;
543 if (dictionary
.GetList("allowed_shipping_spec_by_country",
544 &allowed_shipping_countries
)) {
545 for (size_t i
= 0; i
< allowed_shipping_countries
->GetSize(); ++i
) {
546 const base::DictionaryValue
* country_spec
;
547 std::string country_code
;
548 if (allowed_shipping_countries
->GetDictionary(i
, &country_spec
) &&
549 country_spec
->GetString("country_code", &country_code
)) {
550 wallet_items
->AddAllowedShippingCountry(country_code
);
554 DVLOG(1) << "Response from Google wallet missing allowed shipping"
558 return wallet_items
.Pass();
561 void WalletItems::AddAccount(scoped_ptr
<GaiaAccount
> account
) {
562 if (account
->index() != gaia_accounts_
.size()) {
563 DVLOG(1) << "Tried to add account out of order";
567 if (account
->is_active())
568 active_account_index_
= account
->index();
570 gaia_accounts_
.push_back(account
.release());
573 bool WalletItems::operator==(const WalletItems
& other
) const {
574 return google_transaction_id_
== other
.google_transaction_id_
&&
575 default_instrument_id_
== other
.default_instrument_id_
&&
576 default_address_id_
== other
.default_address_id_
&&
577 required_actions_
== other
.required_actions_
&&
578 // This check is technically redundant, but is useful for tests.
579 ObfuscatedGaiaId() == other
.ObfuscatedGaiaId() &&
580 active_account_index() == other
.active_account_index() &&
581 VectorsAreEqual
<GaiaAccount
>(gaia_accounts(),
582 other
.gaia_accounts()) &&
583 VectorsAreEqual
<MaskedInstrument
>(instruments(),
584 other
.instruments()) &&
585 VectorsAreEqual
<Address
>(addresses(), other
.addresses()) &&
586 VectorsAreEqual
<LegalDocument
>(legal_documents(),
587 other
.legal_documents()) &&
588 allowed_shipping_countries() == other
.allowed_shipping_countries();
591 bool WalletItems::operator!=(const WalletItems
& other
) const {
592 return !(*this == other
);
595 } // namespace wallet
596 } // namespace autofill