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 "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/content/browser/wallet/gaia_account.h"
     12 #include "components/autofill/core/browser/autofill_type.h"
     13 #include "components/autofill/core/browser/credit_card.h"
     14 #include "grit/component_strings.h"
     15 #include "grit/webkit_resources.h"
     16 #include "ui/base/l10n/l10n_util.h"
     17 #include "ui/base/resource/resource_bundle.h"
     18 #include "ui/gfx/image/image.h"
     19 #include "url/gurl.h"
     20 
     21 namespace autofill {
     22 namespace wallet {
     23 
     24 namespace {
     25 
     26 const char kLegalDocumentUrl[] =
     27     "https://wallet.google.com/legaldocument?docId=";
     28 const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html";
     29 
     30 // TODO(estade): move to base/.
     31 template<class T>
     32 bool VectorsAreEqual(const std::vector<T*>& a, const std::vector<T*>& b) {
     33   if (a.size() != b.size())
     34     return false;
     35 
     36   for (size_t i = 0; i < a.size(); ++i) {
     37     if (*a[i] != *b[i])
     38       return false;
     39   }
     40 
     41   return true;
     42 }
     43 
     44 WalletItems::MaskedInstrument::Type
     45     TypeFromString(const std::string& type_string) {
     46   if (type_string == "VISA")
     47     return WalletItems::MaskedInstrument::VISA;
     48   if (type_string == "MASTER_CARD")
     49     return WalletItems::MaskedInstrument::MASTER_CARD;
     50   if (type_string == "AMEX")
     51     return WalletItems::MaskedInstrument::AMEX;
     52   if (type_string == "DISCOVER")
     53     return WalletItems::MaskedInstrument::DISCOVER;
     54   if (type_string == "SOLO")
     55     return WalletItems::MaskedInstrument::SOLO;
     56   if (type_string == "MAESTRO")
     57     return WalletItems::MaskedInstrument::MAESTRO;
     58   if (type_string == "SWITCH")
     59     return WalletItems::MaskedInstrument::SWITCH;
     60   return WalletItems::MaskedInstrument::UNKNOWN;
     61 }
     62 
     63 WalletItems::MaskedInstrument::Status
     64     StatusFromString(const std::string& status_string) {
     65   if (status_string == "AMEX_NOT_SUPPORTED")
     66     return WalletItems::MaskedInstrument::AMEX_NOT_SUPPORTED;
     67   if (status_string == "PENDING")
     68     return WalletItems::MaskedInstrument::PENDING;
     69   if (status_string == "VALID")
     70     return WalletItems::MaskedInstrument::VALID;
     71   if (status_string == "DECLINED")
     72     return WalletItems::MaskedInstrument::DECLINED;
     73   if (status_string == "DISABLED_FOR_THIS_MERCHANT")
     74     return WalletItems::MaskedInstrument::DISABLED_FOR_THIS_MERCHANT;
     75   if (status_string == "UNSUPPORTED_COUNTRY")
     76     return WalletItems::MaskedInstrument::UNSUPPORTED_COUNTRY;
     77   if (status_string == "EXPIRED")
     78     return WalletItems::MaskedInstrument::EXPIRED;
     79   if (status_string == "BILLING_INCOMPLETE")
     80     return WalletItems::MaskedInstrument::BILLING_INCOMPLETE;
     81   return WalletItems::MaskedInstrument::INAPPLICABLE;
     82 }
     83 
     84 base::string16 DisplayStringFromType(WalletItems::MaskedInstrument::Type type) {
     85   switch (type) {
     86     case WalletItems::MaskedInstrument::AMEX:
     87       return CreditCard::TypeForDisplay(kAmericanExpressCard);
     88     case WalletItems::MaskedInstrument::DISCOVER:
     89       return CreditCard::TypeForDisplay(kDiscoverCard);
     90     case WalletItems::MaskedInstrument::MASTER_CARD:
     91       return CreditCard::TypeForDisplay(kMasterCard);
     92     case WalletItems::MaskedInstrument::VISA:
     93       return CreditCard::TypeForDisplay(kVisaCard);
     94     default:
     95       return CreditCard::TypeForDisplay(kGenericCard);
     96   }
     97 }
     98 
     99 }  // anonymous namespace
    100 
    101 WalletItems::MaskedInstrument::MaskedInstrument(
    102     const base::string16& descriptive_name,
    103     const WalletItems::MaskedInstrument::Type& type,
    104     const std::vector<base::string16>& supported_currencies,
    105     const base::string16& last_four_digits,
    106     int expiration_month,
    107     int expiration_year,
    108     scoped_ptr<Address> address,
    109     const WalletItems::MaskedInstrument::Status& status,
    110     const std::string& object_id)
    111     : descriptive_name_(descriptive_name),
    112       type_(type),
    113       supported_currencies_(supported_currencies),
    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 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   std::vector<base::string16> supported_currencies;
    171   const ListValue* supported_currency_list;
    172   if (dictionary.GetList("supported_currency", &supported_currency_list)) {
    173     for (size_t i = 0; i < supported_currency_list->GetSize(); ++i) {
    174       base::string16 currency;
    175       if (supported_currency_list->GetString(i, &currency))
    176         supported_currencies.push_back(currency);
    177     }
    178   } else {
    179     DVLOG(1) << "Response from Google Wallet missing supported currency";
    180   }
    181 
    182   int expiration_month;
    183   if (!dictionary.GetInteger("expiration_month", &expiration_month))
    184     DVLOG(1) << "Response from Google Wallet missing expiration month";
    185 
    186   int expiration_year;
    187   if (!dictionary.GetInteger("expiration_year", &expiration_year))
    188     DVLOG(1) << "Response from Google Wallet missing expiration year";
    189 
    190   base::string16 descriptive_name;
    191   if (!dictionary.GetString("descriptive_name", &descriptive_name))
    192     DVLOG(1) << "Response from Google Wallet missing descriptive name";
    193 
    194   return scoped_ptr<MaskedInstrument>(new MaskedInstrument(descriptive_name,
    195                                                            type,
    196                                                            supported_currencies,
    197                                                            last_four_digits,
    198                                                            expiration_month,
    199                                                            expiration_year,
    200                                                            address.Pass(),
    201                                                            status,
    202                                                            object_id));
    203 }
    204 
    205 bool WalletItems::MaskedInstrument::operator==(
    206     const WalletItems::MaskedInstrument& other) const {
    207   if (descriptive_name_ != other.descriptive_name_)
    208     return false;
    209   if (type_ != other.type_)
    210     return false;
    211   if (supported_currencies_ != other.supported_currencies_)
    212     return false;
    213   if (last_four_digits_ != other.last_four_digits_)
    214     return false;
    215   if (expiration_month_ != other.expiration_month_)
    216     return false;
    217   if (expiration_year_ != other.expiration_year_)
    218     return false;
    219   if (address_) {
    220     if (other.address_) {
    221       if (*address_ != *other.address_)
    222         return false;
    223     } else {
    224       return false;
    225     }
    226   } else if (other.address_) {
    227     return false;
    228   }
    229   if (status_ != other.status_)
    230     return false;
    231   if (object_id_ != other.object_id_)
    232     return false;
    233   return true;
    234 }
    235 
    236 bool WalletItems::MaskedInstrument::operator!=(
    237     const WalletItems::MaskedInstrument& other) const {
    238   return !(*this == other);
    239 }
    240 
    241 const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById(
    242     const std::string& object_id) const {
    243   if (object_id.empty())
    244     return NULL;
    245 
    246   for (size_t i = 0; i < instruments_.size(); ++i) {
    247     if (instruments_[i]->object_id() == object_id)
    248       return instruments_[i];
    249   }
    250 
    251   return NULL;
    252 }
    253 
    254 bool WalletItems::HasRequiredAction(RequiredAction action) const {
    255   DCHECK(ActionAppliesToWalletItems(action));
    256   return std::find(required_actions_.begin(),
    257                    required_actions_.end(),
    258                    action) != required_actions_.end();
    259 }
    260 
    261 bool WalletItems::SupportsCard(const base::string16& card_number,
    262                                base::string16* message) const {
    263   std::string card_type = CreditCard::GetCreditCardType(card_number);
    264 
    265   if (card_type == kVisaCard ||
    266       card_type == kMasterCard ||
    267       card_type == kDiscoverCard) {
    268     return true;
    269   }
    270 
    271   if (card_type == kAmericanExpressCard) {
    272     if (amex_permission_ == AMEX_ALLOWED)
    273       return true;
    274 
    275     *message = l10n_util::GetStringUTF16(
    276         IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET_FOR_MERCHANT);
    277     return false;
    278   }
    279 
    280   *message = l10n_util::GetStringUTF16(
    281       IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET);
    282    return false;
    283 }
    284 
    285 std::string WalletItems::ObfuscatedGaiaId() const {
    286   if (active_account_index_ >= gaia_accounts_.size())
    287     return std::string();
    288 
    289   return gaia_accounts_[active_account_index_]->obfuscated_id();
    290 }
    291 
    292 base::string16 WalletItems::MaskedInstrument::DisplayName() const {
    293 #if defined(OS_ANDROID)
    294   // TODO(aruslan): improve this stub implementation.
    295   return descriptive_name();
    296 #else
    297   return descriptive_name();
    298 #endif
    299 }
    300 
    301 base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const {
    302 #if defined(OS_ANDROID)
    303   // TODO(aruslan): improve this stub implementation.
    304   return address().DisplayName();
    305 #else
    306   return base::string16();
    307 #endif
    308 }
    309 
    310 base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const {
    311   // TODO(dbeam): i18n.
    312   return DisplayStringFromType(type_) + ASCIIToUTF16(" - ") +
    313          last_four_digits();
    314 }
    315 
    316 const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const {
    317   int idr = 0;
    318   switch (type_) {
    319     case AMEX:
    320       idr = IDR_AUTOFILL_CC_AMEX;
    321       break;
    322 
    323     case DISCOVER:
    324       idr = IDR_AUTOFILL_CC_DISCOVER;
    325       break;
    326 
    327     case MASTER_CARD:
    328       idr = IDR_AUTOFILL_CC_MASTERCARD;
    329       break;
    330 
    331     case VISA:
    332       idr = IDR_AUTOFILL_CC_VISA;
    333       break;
    334 
    335     case SOLO:
    336     case MAESTRO:
    337     case SWITCH:
    338     case UNKNOWN:
    339       idr = IDR_AUTOFILL_CC_GENERIC;
    340       break;
    341   }
    342 
    343   return ResourceBundle::GetSharedInstance().GetImageNamed(idr);
    344 }
    345 
    346 base::string16 WalletItems::MaskedInstrument::GetInfo(
    347     const AutofillType& type,
    348     const std::string& app_locale) const {
    349   if (type.group() != CREDIT_CARD)
    350     return address().GetInfo(type, app_locale);
    351 
    352   switch (type.GetStorableType()) {
    353     case CREDIT_CARD_NAME:
    354       return address().recipient_name();
    355 
    356     case CREDIT_CARD_NUMBER:
    357       return DisplayName();
    358 
    359     case CREDIT_CARD_EXP_4_DIGIT_YEAR:
    360       return base::IntToString16(expiration_year());
    361 
    362     case CREDIT_CARD_VERIFICATION_CODE:
    363       break;
    364 
    365     case CREDIT_CARD_TYPE:
    366       return DisplayStringFromType(type_);
    367 
    368     default:
    369       NOTREACHED();
    370   }
    371 
    372   return base::string16();
    373 }
    374 
    375 WalletItems::LegalDocument::~LegalDocument() {}
    376 
    377 scoped_ptr<WalletItems::LegalDocument>
    378     WalletItems::LegalDocument::CreateLegalDocument(
    379     const base::DictionaryValue& dictionary) {
    380   std::string id;
    381   if (!dictionary.GetString("legal_document_id", &id)) {
    382     DLOG(ERROR) << "Response from Google Wallet missing legal document id";
    383     return scoped_ptr<LegalDocument>();
    384   }
    385 
    386   base::string16 display_name;
    387   if (!dictionary.GetString("display_name", &display_name)) {
    388     DLOG(ERROR) << "Response from Google Wallet missing display name";
    389     return scoped_ptr<LegalDocument>();
    390   }
    391 
    392   return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name));
    393 }
    394 
    395 scoped_ptr<WalletItems::LegalDocument>
    396     WalletItems::LegalDocument::CreatePrivacyPolicyDocument() {
    397   return scoped_ptr<LegalDocument>(new LegalDocument(
    398       GURL(kPrivacyNoticeUrl),
    399       l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK)));
    400 }
    401 
    402 bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
    403   return id_ == other.id_ &&
    404          url_ == other.url_ &&
    405          display_name_ == other.display_name_;
    406 }
    407 
    408 bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
    409   return !(*this == other);
    410 }
    411 
    412 WalletItems::LegalDocument::LegalDocument(const std::string& id,
    413                                           const base::string16& display_name)
    414     : id_(id),
    415       url_(kLegalDocumentUrl + id),
    416       display_name_(display_name) {}
    417 
    418 WalletItems::LegalDocument::LegalDocument(const GURL& url,
    419                                           const base::string16& display_name)
    420     : url_(url),
    421       display_name_(display_name) {}
    422 
    423 WalletItems::WalletItems(const std::vector<RequiredAction>& required_actions,
    424                          const std::string& google_transaction_id,
    425                          const std::string& default_instrument_id,
    426                          const std::string& default_address_id,
    427                          AmexPermission amex_permission)
    428     : required_actions_(required_actions),
    429       google_transaction_id_(google_transaction_id),
    430       default_instrument_id_(default_instrument_id),
    431       default_address_id_(default_address_id),
    432       active_account_index_(std::numeric_limits<size_t>::max()),
    433       amex_permission_(amex_permission) {}
    434 
    435 WalletItems::~WalletItems() {}
    436 
    437 scoped_ptr<WalletItems>
    438     WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) {
    439   std::vector<RequiredAction> required_action;
    440   const ListValue* required_action_list;
    441   if (dictionary.GetList("required_action", &required_action_list)) {
    442     for (size_t i = 0; i < required_action_list->GetSize(); ++i) {
    443       std::string action_string;
    444       if (required_action_list->GetString(i, &action_string)) {
    445         RequiredAction action = ParseRequiredActionFromString(action_string);
    446         if (!ActionAppliesToWalletItems(action)) {
    447           DLOG(ERROR) << "Response from Google wallet with bad required action:"
    448                          " \"" << action_string << "\"";
    449           return scoped_ptr<WalletItems>();
    450         }
    451         required_action.push_back(action);
    452       }
    453     }
    454   } else {
    455     DVLOG(1) << "Response from Google wallet missing required actions";
    456   }
    457 
    458   std::string google_transaction_id;
    459   if (!dictionary.GetString("google_transaction_id", &google_transaction_id) &&
    460       required_action.empty()) {
    461     DLOG(ERROR) << "Response from Google wallet missing google transaction id";
    462     return scoped_ptr<WalletItems>();
    463   }
    464 
    465   std::string default_instrument_id;
    466   if (!dictionary.GetString("default_instrument_id", &default_instrument_id))
    467     DVLOG(1) << "Response from Google wallet missing default instrument id";
    468 
    469   std::string default_address_id;
    470   if (!dictionary.GetString("default_address_id", &default_address_id))
    471     DVLOG(1) << "Response from Google wallet missing default_address_id";
    472 
    473   // obfuscated_gaia_id is deprecated.
    474 
    475   bool amex_disallowed = true;
    476   if (!dictionary.GetBoolean("amex_disallowed", &amex_disallowed))
    477     DVLOG(1) << "Response from Google wallet missing the amex_disallowed field";
    478   AmexPermission amex_permission =
    479       amex_disallowed ? AMEX_DISALLOWED : AMEX_ALLOWED;
    480 
    481   scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action,
    482                                                        google_transaction_id,
    483                                                        default_instrument_id,
    484                                                        default_address_id,
    485                                                        amex_permission));
    486   std::vector<std::string> gaia_accounts;
    487   const base::ListValue* gaia_profiles;
    488   if (dictionary.GetList("gaia_profile", &gaia_profiles)) {
    489     for (size_t i = 0; i < gaia_profiles->GetSize(); ++i) {
    490       const base::DictionaryValue* account_dict;
    491       std::string email;
    492       if (!gaia_profiles->GetDictionary(i, &account_dict))
    493         continue;
    494 
    495       scoped_ptr<GaiaAccount> gaia_account(
    496           GaiaAccount::Create(*account_dict));
    497       if (gaia_account)
    498         wallet_items->AddAccount(gaia_account.Pass());
    499     }
    500   } else {
    501     DVLOG(1) << "Response from Google wallet missing GAIA accounts";
    502   }
    503 
    504   const ListValue* legal_docs;
    505   if (dictionary.GetList("required_legal_document", &legal_docs)) {
    506     for (size_t i = 0; i < legal_docs->GetSize(); ++i) {
    507       const DictionaryValue* legal_doc_dict;
    508       if (legal_docs->GetDictionary(i, &legal_doc_dict)) {
    509         scoped_ptr<LegalDocument> legal_doc(
    510             LegalDocument::CreateLegalDocument(*legal_doc_dict));
    511         if (legal_doc)
    512           wallet_items->AddLegalDocument(legal_doc.Pass());
    513         else
    514           return scoped_ptr<WalletItems>();
    515       }
    516     }
    517 
    518     if (!legal_docs->empty()) {
    519       // Always append the privacy policy link as well.
    520       wallet_items->AddLegalDocument(
    521           LegalDocument::CreatePrivacyPolicyDocument());
    522     }
    523   } else {
    524     DVLOG(1) << "Response from Google wallet missing legal docs";
    525   }
    526 
    527   const ListValue* instruments;
    528   if (dictionary.GetList("instrument", &instruments)) {
    529     for (size_t i = 0; i < instruments->GetSize(); ++i) {
    530       const DictionaryValue* instrument_dict;
    531       if (instruments->GetDictionary(i, &instrument_dict)) {
    532         scoped_ptr<MaskedInstrument> instrument(
    533             MaskedInstrument::CreateMaskedInstrument(*instrument_dict));
    534         if (instrument)
    535           wallet_items->AddInstrument(instrument.Pass());
    536       }
    537     }
    538   } else {
    539     DVLOG(1) << "Response from Google wallet missing instruments";
    540   }
    541 
    542   const ListValue* addresses;
    543   if (dictionary.GetList("address", &addresses)) {
    544     for (size_t i = 0; i < addresses->GetSize(); ++i) {
    545       const DictionaryValue* address_dict;
    546       if (addresses->GetDictionary(i, &address_dict)) {
    547         scoped_ptr<Address> address(
    548             Address::CreateAddressWithID(*address_dict));
    549         if (address)
    550           wallet_items->AddAddress(address.Pass());
    551       }
    552     }
    553   } else {
    554     DVLOG(1) << "Response from Google wallet missing addresses";
    555   }
    556 
    557   return wallet_items.Pass();
    558 }
    559 
    560 void WalletItems::AddAccount(scoped_ptr<GaiaAccount> account) {
    561   if (account->index() != gaia_accounts_.size()) {
    562     DVLOG(1) << "Tried to add account out of order";
    563     return;
    564   }
    565 
    566   if (account->is_active())
    567     active_account_index_ = account->index();
    568 
    569   gaia_accounts_.push_back(account.release());
    570 }
    571 
    572 bool WalletItems::operator==(const WalletItems& other) const {
    573   return google_transaction_id_ == other.google_transaction_id_ &&
    574          default_instrument_id_ == other.default_instrument_id_ &&
    575          default_address_id_ == other.default_address_id_ &&
    576          required_actions_ == other.required_actions_ &&
    577          // This check is technically redundant, but is useful for tests.
    578          ObfuscatedGaiaId() == other.ObfuscatedGaiaId() &&
    579          active_account_index() == other.active_account_index() &&
    580          VectorsAreEqual<GaiaAccount>(gaia_accounts(),
    581                                       other.gaia_accounts()) &&
    582          VectorsAreEqual<MaskedInstrument>(instruments(),
    583                                             other.instruments()) &&
    584          VectorsAreEqual<Address>(addresses(), other.addresses()) &&
    585          VectorsAreEqual<LegalDocument>(legal_documents(),
    586                                          other.legal_documents());
    587 }
    588 
    589 bool WalletItems::operator!=(const WalletItems& other) const {
    590   return !(*this == other);
    591 }
    592 
    593 }  // namespace wallet
    594 }  // namespace autofill
    595