Home | History | Annotate | Download | only in wallet
      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 <limits>
      8 
      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"
     21 #include "url/gurl.h"
     22 
     23 namespace autofill {
     24 namespace wallet {
     25 
     26 namespace {
     27 
     28 const char kLegalDocumentUrl[] =
     29     "https://wallet.google.com/legaldocument?docId=";
     30 const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html";
     31 
     32 // TODO(estade): move to base/.
     33 template<class T>
     34 bool VectorsAreEqual(const std::vector<T*>& a, const std::vector<T*>& b) {
     35   if (a.size() != b.size())
     36     return false;
     37 
     38   for (size_t i = 0; i < a.size(); ++i) {
     39     if (*a[i] != *b[i])
     40       return false;
     41   }
     42 
     43   return true;
     44 }
     45 
     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;
     63 }
     64 
     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;
     84 }
     85 
     86 base::string16 DisplayStringFromType(WalletItems::MaskedInstrument::Type type) {
     87   switch (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);
     96     default:
     97       return CreditCard::TypeForDisplay(kGenericCard);
     98   }
     99 }
    100 
    101 }  // anonymous namespace
    102 
    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,
    108     int expiration_year,
    109     scoped_ptr<Address> address,
    110     const WalletItems::MaskedInstrument::Status& status,
    111     const std::string& object_id)
    112     : descriptive_name_(descriptive_name),
    113       type_(type),
    114       last_four_digits_(last_four_digits),
    115       expiration_month_(expiration_month),
    116       expiration_year_(expiration_year),
    117       address_(address.Pass()),
    118       status_(status),
    119       object_id_(object_id) {
    120   DCHECK(address_);
    121 }
    122 
    123 WalletItems::MaskedInstrument::~MaskedInstrument() {}
    124 
    125 scoped_ptr<WalletItems::MaskedInstrument>
    126     WalletItems::MaskedInstrument::CreateMaskedInstrument(
    127     const base::DictionaryValue& dictionary) {
    128   std::string type_string;
    129   Type type;
    130   if (dictionary.GetString("type", &type_string)) {
    131     type = TypeFromString(type_string);
    132   } else {
    133     DLOG(ERROR) << "Response from Google Wallet missing card type";
    134     return scoped_ptr<MaskedInstrument>();
    135   }
    136 
    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>();
    141   }
    142 
    143   std::string status_string;
    144   Status status;
    145   if (dictionary.GetString("status", &status_string)) {
    146     status = StatusFromString(status_string);
    147   } else {
    148     DLOG(ERROR) << "Response from Google Wallet missing status";
    149     return scoped_ptr<MaskedInstrument>();
    150   }
    151 
    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>();
    156   }
    157 
    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>();
    162   }
    163   scoped_ptr<Address> address = Address::CreateDisplayAddress(*address_dict);
    164 
    165   if (!address) {
    166     DLOG(ERROR) << "Response from Google wallet contained malformed address";
    167     return scoped_ptr<MaskedInstrument>();
    168   }
    169 
    170   int expiration_month;
    171   if (!dictionary.GetInteger("expiration_month", &expiration_month))
    172     DVLOG(1) << "Response from Google Wallet missing expiration month";
    173 
    174   int expiration_year;
    175   if (!dictionary.GetInteger("expiration_year", &expiration_year))
    176     DVLOG(1) << "Response from Google Wallet missing expiration year";
    177 
    178   base::string16 descriptive_name;
    179   if (!dictionary.GetString("descriptive_name", &descriptive_name))
    180     DVLOG(1) << "Response from Google Wallet missing descriptive name";
    181 
    182   return scoped_ptr<MaskedInstrument>(new MaskedInstrument(descriptive_name,
    183                                                            type,
    184                                                            last_four_digits,
    185                                                            expiration_month,
    186                                                            expiration_year,
    187                                                            address.Pass(),
    188                                                            status,
    189                                                            object_id));
    190 }
    191 
    192 bool WalletItems::MaskedInstrument::operator==(
    193     const WalletItems::MaskedInstrument& other) const {
    194   if (descriptive_name_ != other.descriptive_name_)
    195     return false;
    196   if (type_ != other.type_)
    197     return false;
    198   if (last_four_digits_ != other.last_four_digits_)
    199     return false;
    200   if (expiration_month_ != other.expiration_month_)
    201     return false;
    202   if (expiration_year_ != other.expiration_year_)
    203     return false;
    204   if (address_) {
    205     if (other.address_) {
    206       if (*address_ != *other.address_)
    207         return false;
    208     } else {
    209       return false;
    210     }
    211   } else if (other.address_) {
    212     return false;
    213   }
    214   if (status_ != other.status_)
    215     return false;
    216   if (object_id_ != other.object_id_)
    217     return false;
    218   return true;
    219 }
    220 
    221 bool WalletItems::MaskedInstrument::operator!=(
    222     const WalletItems::MaskedInstrument& other) const {
    223   return !(*this == other);
    224 }
    225 
    226 const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById(
    227     const std::string& object_id) const {
    228   if (object_id.empty())
    229     return NULL;
    230 
    231   for (size_t i = 0; i < instruments_.size(); ++i) {
    232     if (instruments_[i]->object_id() == object_id)
    233       return instruments_[i];
    234   }
    235 
    236   return NULL;
    237 }
    238 
    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();
    244 }
    245 
    246 bool WalletItems::SupportsCard(const base::string16& card_number,
    247                                base::string16* message) const {
    248   std::string card_type = CreditCard::GetCreditCardType(card_number);
    249 
    250   if (card_type == kVisaCard ||
    251       card_type == kMasterCard ||
    252       card_type == kDiscoverCard) {
    253     return true;
    254   }
    255 
    256   if (card_type == kAmericanExpressCard) {
    257     if (amex_permission_ == AMEX_ALLOWED)
    258       return true;
    259 
    260     *message = l10n_util::GetStringUTF16(
    261         IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET_FOR_MERCHANT);
    262     return false;
    263   }
    264 
    265   *message = l10n_util::GetStringUTF16(
    266       IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET);
    267    return false;
    268 }
    269 
    270 std::string WalletItems::ObfuscatedGaiaId() const {
    271   if (active_account_index_ >= gaia_accounts_.size())
    272     return std::string();
    273 
    274   return gaia_accounts_[active_account_index_]->obfuscated_id();
    275 }
    276 
    277 base::string16 WalletItems::MaskedInstrument::DisplayName() const {
    278 #if defined(OS_ANDROID)
    279   // TODO(aruslan): improve this stub implementation.
    280   return descriptive_name();
    281 #else
    282   return descriptive_name();
    283 #endif
    284 }
    285 
    286 base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const {
    287 #if defined(OS_ANDROID)
    288   // TODO(aruslan): improve this stub implementation.
    289   return address().DisplayName();
    290 #else
    291   return base::string16();
    292 #endif
    293 }
    294 
    295 base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const {
    296   // TODO(dbeam): i18n.
    297   return DisplayStringFromType(type_) + base::ASCIIToUTF16(" - ") +
    298          last_four_digits();
    299 }
    300 
    301 const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const {
    302   int idr = 0;
    303   switch (type_) {
    304     case AMEX:
    305       idr = IDR_AUTOFILL_CC_AMEX;
    306       break;
    307 
    308     case DISCOVER:
    309       idr = IDR_AUTOFILL_CC_DISCOVER;
    310       break;
    311 
    312     case MASTER_CARD:
    313       idr = IDR_AUTOFILL_CC_MASTERCARD;
    314       break;
    315 
    316     case VISA:
    317       idr = IDR_AUTOFILL_CC_VISA;
    318       break;
    319 
    320     case SOLO:
    321     case MAESTRO:
    322     case SWITCH:
    323     case UNKNOWN:
    324       idr = IDR_AUTOFILL_CC_GENERIC;
    325       break;
    326   }
    327 
    328   return ResourceBundle::GetSharedInstance().GetImageNamed(idr);
    329 }
    330 
    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);
    336 
    337   switch (type.GetStorableType()) {
    338     case CREDIT_CARD_NAME:
    339       return address().recipient_name();
    340 
    341     case CREDIT_CARD_NUMBER:
    342       return DisplayName();
    343 
    344     case CREDIT_CARD_EXP_4_DIGIT_YEAR:
    345       return base::IntToString16(expiration_year());
    346 
    347     case CREDIT_CARD_VERIFICATION_CODE:
    348       break;
    349 
    350     case CREDIT_CARD_TYPE:
    351       return DisplayStringFromType(type_);
    352 
    353     default:
    354       NOTREACHED();
    355   }
    356 
    357   return base::string16();
    358 }
    359 
    360 WalletItems::LegalDocument::~LegalDocument() {}
    361 
    362 scoped_ptr<WalletItems::LegalDocument>
    363     WalletItems::LegalDocument::CreateLegalDocument(
    364     const base::DictionaryValue& dictionary) {
    365   std::string id;
    366   if (!dictionary.GetString("legal_document_id", &id)) {
    367     DLOG(ERROR) << "Response from Google Wallet missing legal document id";
    368     return scoped_ptr<LegalDocument>();
    369   }
    370 
    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>();
    375   }
    376 
    377   return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name));
    378 }
    379 
    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)));
    385 }
    386 
    387 bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
    388   return id_ == other.id_ &&
    389          url_ == other.url_ &&
    390          display_name_ == other.display_name_;
    391 }
    392 
    393 bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
    394   return !(*this == other);
    395 }
    396 
    397 WalletItems::LegalDocument::LegalDocument(const std::string& id,
    398                                           const base::string16& display_name)
    399     : id_(id),
    400       url_(kLegalDocumentUrl + id),
    401       display_name_(display_name) {}
    402 
    403 WalletItems::LegalDocument::LegalDocument(const GURL& url,
    404                                           const base::string16& display_name)
    405     : url_(url),
    406       display_name_(display_name) {}
    407 
    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) {}
    419 
    420 WalletItems::~WalletItems() {}
    421 
    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>();
    435         }
    436         required_action.push_back(action);
    437       }
    438     }
    439   } else {
    440     DVLOG(1) << "Response from Google wallet missing required actions";
    441   }
    442 
    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>();
    448   }
    449 
    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";
    453 
    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";
    457 
    458   // obfuscated_gaia_id is deprecated.
    459 
    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;
    465 
    466   scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action,
    467                                                        google_transaction_id,
    468                                                        default_instrument_id,
    469                                                        default_address_id,
    470                                                        amex_permission));
    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;
    476       std::string email;
    477       if (!gaia_profiles->GetDictionary(i, &account_dict))
    478         continue;
    479 
    480       scoped_ptr<GaiaAccount> gaia_account(
    481           GaiaAccount::Create(*account_dict));
    482       if (gaia_account)
    483         wallet_items->AddAccount(gaia_account.Pass());
    484     }
    485   } else {
    486     DVLOG(1) << "Response from Google wallet missing GAIA accounts";
    487   }
    488 
    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));
    496         if (legal_doc)
    497           wallet_items->AddLegalDocument(legal_doc.Pass());
    498         else
    499           return scoped_ptr<WalletItems>();
    500       }
    501     }
    502 
    503     if (!legal_docs->empty()) {
    504       // Always append the privacy policy link as well.
    505       wallet_items->AddLegalDocument(
    506           LegalDocument::CreatePrivacyPolicyDocument());
    507     }
    508   } else {
    509     DVLOG(1) << "Response from Google wallet missing legal docs";
    510   }
    511 
    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));
    519         if (instrument)
    520           wallet_items->AddInstrument(instrument.Pass());
    521       }
    522     }
    523   } else {
    524     DVLOG(1) << "Response from Google wallet missing instruments";
    525   }
    526 
    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));
    534         if (address)
    535           wallet_items->AddAddress(address.Pass());
    536       }
    537     }
    538   } else {
    539     DVLOG(1) << "Response from Google wallet missing addresses";
    540   }
    541 
    542   return wallet_items.Pass();
    543 }
    544 
    545 void WalletItems::AddAccount(scoped_ptr<GaiaAccount> account) {
    546   if (account->index() != gaia_accounts_.size()) {
    547     DVLOG(1) << "Tried to add account out of order";
    548     return;
    549   }
    550 
    551   if (account->is_active())
    552     active_account_index_ = account->index();
    553 
    554   gaia_accounts_.push_back(account.release());
    555 }
    556 
    557 bool WalletItems::operator==(const WalletItems& other) const {
    558   return google_transaction_id_ == other.google_transaction_id_ &&
    559          default_instrument_id_ == other.default_instrument_id_ &&
    560          default_address_id_ == other.default_address_id_ &&
    561          required_actions_ == other.required_actions_ &&
    562          // This check is technically redundant, but is useful for tests.
    563          ObfuscatedGaiaId() == other.ObfuscatedGaiaId() &&
    564          active_account_index() == other.active_account_index() &&
    565          VectorsAreEqual<GaiaAccount>(gaia_accounts(),
    566                                       other.gaia_accounts()) &&
    567          VectorsAreEqual<MaskedInstrument>(instruments(),
    568                                             other.instruments()) &&
    569          VectorsAreEqual<Address>(addresses(), other.addresses()) &&
    570          VectorsAreEqual<LegalDocument>(legal_documents(),
    571                                          other.legal_documents());
    572 }
    573 
    574 bool WalletItems::operator!=(const WalletItems& other) const {
    575   return !(*this == other);
    576 }
    577 
    578 }  // namespace wallet
    579 }  // namespace autofill
    580