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/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 
     20 namespace autofill {
     21 namespace wallet {
     22 
     23 namespace {
     24 
     25 const char kLegalDocumentUrl[] =
     26     "https://wallet.google.com/legaldocument?docId=";
     27 const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html";
     28 
     29 // TODO(estade): move to base/.
     30 template<class T>
     31 bool 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 
     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;
     60 }
     61 
     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;
     81 }
     82 
     83 }  // anonymous namespace
     84 
     85 WalletItems::MaskedInstrument::MaskedInstrument(
     86     const base::string16& descriptive_name,
     87     const WalletItems::MaskedInstrument::Type& type,
     88     const std::vector<base::string16>& supported_currencies,
     89     const base::string16& last_four_digits,
     90     int expiration_month,
     91     int expiration_year,
     92     scoped_ptr<Address> address,
     93     const WalletItems::MaskedInstrument::Status& status,
     94     const std::string& object_id)
     95     : descriptive_name_(descriptive_name),
     96       type_(type),
     97       supported_currencies_(supported_currencies),
     98       last_four_digits_(last_four_digits),
     99       expiration_month_(expiration_month),
    100       expiration_year_(expiration_year),
    101       address_(address.Pass()),
    102       status_(status),
    103       object_id_(object_id) {
    104   DCHECK(address_.get());
    105 }
    106 
    107 WalletItems::MaskedInstrument::~MaskedInstrument() {}
    108 
    109 scoped_ptr<WalletItems::MaskedInstrument>
    110     WalletItems::MaskedInstrument::CreateMaskedInstrument(
    111     const base::DictionaryValue& dictionary) {
    112   std::string type_string;
    113   Type type;
    114   if (dictionary.GetString("type", &type_string)) {
    115     type = TypeFromString(type_string);
    116   } else {
    117     DLOG(ERROR) << "Response from Google Wallet missing card type";
    118     return scoped_ptr<MaskedInstrument>();
    119   }
    120 
    121   base::string16 last_four_digits;
    122   if (!dictionary.GetString("last_four_digits", &last_four_digits)) {
    123     DLOG(ERROR) << "Response from Google Wallet missing last four digits";
    124     return scoped_ptr<MaskedInstrument>();
    125   }
    126 
    127   std::string status_string;
    128   Status status;
    129   if (dictionary.GetString("status", &status_string)) {
    130     status = StatusFromString(status_string);
    131   } else {
    132     DLOG(ERROR) << "Response from Google Wallet missing status";
    133     return scoped_ptr<MaskedInstrument>();
    134   }
    135 
    136   std::string object_id;
    137   if (!dictionary.GetString("object_id", &object_id)) {
    138     DLOG(ERROR) << "Response from Google Wallet missing object id";
    139     return scoped_ptr<MaskedInstrument>();
    140   }
    141 
    142   const DictionaryValue* address_dict;
    143   if (!dictionary.GetDictionary("billing_address", &address_dict)) {
    144     DLOG(ERROR) << "Response from Google wallet missing address";
    145     return scoped_ptr<MaskedInstrument>();
    146   }
    147   scoped_ptr<Address> address = Address::CreateDisplayAddress(*address_dict);
    148 
    149   if (!address.get()) {
    150     DLOG(ERROR) << "Response from Google wallet contained malformed address";
    151     return scoped_ptr<MaskedInstrument>();
    152   }
    153 
    154   std::vector<base::string16> supported_currencies;
    155   const ListValue* supported_currency_list;
    156   if (dictionary.GetList("supported_currency", &supported_currency_list)) {
    157     for (size_t i = 0; i < supported_currency_list->GetSize(); ++i) {
    158       base::string16 currency;
    159       if (supported_currency_list->GetString(i, &currency))
    160         supported_currencies.push_back(currency);
    161     }
    162   } else {
    163     DVLOG(1) << "Response from Google Wallet missing supported currency";
    164   }
    165 
    166   int expiration_month;
    167   if (!dictionary.GetInteger("expiration_month", &expiration_month))
    168     DVLOG(1) << "Response from Google Wallet missing expiration month";
    169 
    170   int expiration_year;
    171   if (!dictionary.GetInteger("expiration_year", &expiration_year))
    172     DVLOG(1) << "Response from Google Wallet missing expiration year";
    173 
    174   base::string16 descriptive_name;
    175   if (!dictionary.GetString("descriptive_name", &descriptive_name))
    176     DVLOG(1) << "Response from Google Wallet missing descriptive name";
    177 
    178   return scoped_ptr<MaskedInstrument>(new MaskedInstrument(descriptive_name,
    179                                                            type,
    180                                                            supported_currencies,
    181                                                            last_four_digits,
    182                                                            expiration_month,
    183                                                            expiration_year,
    184                                                            address.Pass(),
    185                                                            status,
    186                                                            object_id));
    187 }
    188 
    189 bool WalletItems::MaskedInstrument::operator==(
    190     const WalletItems::MaskedInstrument& other) const {
    191   if (descriptive_name_ != other.descriptive_name_)
    192     return false;
    193   if (type_ != other.type_)
    194     return false;
    195   if (supported_currencies_ != other.supported_currencies_)
    196     return false;
    197   if (last_four_digits_ != other.last_four_digits_)
    198     return false;
    199   if (expiration_month_ != other.expiration_month_)
    200     return false;
    201   if (expiration_year_ != other.expiration_year_)
    202     return false;
    203   if (address_.get()) {
    204     if (other.address_.get()) {
    205       if (*address_.get() != *other.address_.get())
    206         return false;
    207     } else {
    208       return false;
    209     }
    210   } else if (other.address_.get()) {
    211     return false;
    212   }
    213   if (status_ != other.status_)
    214     return false;
    215   if (object_id_ != other.object_id_)
    216     return false;
    217   return true;
    218 }
    219 
    220 bool WalletItems::MaskedInstrument::operator!=(
    221     const WalletItems::MaskedInstrument& other) const {
    222   return !(*this == other);
    223 }
    224 
    225 const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById(
    226     const std::string& object_id) const {
    227   if (object_id.empty())
    228     return NULL;
    229 
    230   for (size_t i = 0; i < instruments_.size(); ++i) {
    231     if (instruments_[i]->object_id() == object_id)
    232       return instruments_[i];
    233   }
    234 
    235   return NULL;
    236 }
    237 
    238 bool WalletItems::HasRequiredAction(RequiredAction action) const {
    239   DCHECK(ActionAppliesToWalletItems(action));
    240   return std::find(required_actions_.begin(),
    241                    required_actions_.end(),
    242                    action) != required_actions_.end();
    243 }
    244 
    245 base::string16 WalletItems::MaskedInstrument::DisplayName() const {
    246 #if defined(OS_ANDROID)
    247   // TODO(aruslan): improve this stub implementation.
    248   return descriptive_name();
    249 #else
    250   return descriptive_name();
    251 #endif
    252 }
    253 
    254 base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const {
    255 #if defined(OS_ANDROID)
    256   // TODO(aruslan): improve this stub implementation.
    257   return address().DisplayName();
    258 #else
    259   return base::string16();
    260 #endif
    261 }
    262 
    263 base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const {
    264   base::string16 display_type;
    265 
    266   if (type_ == AMEX)
    267     display_type = CreditCard::TypeForDisplay(kAmericanExpressCard);
    268   else if (type_ == DISCOVER)
    269     display_type = CreditCard::TypeForDisplay(kDiscoverCard);
    270   else if (type_ == MASTER_CARD)
    271     display_type = CreditCard::TypeForDisplay(kMasterCard);
    272   else if (type_ == VISA)
    273     display_type = CreditCard::TypeForDisplay(kVisaCard);
    274   else
    275     display_type = CreditCard::TypeForDisplay(kGenericCard);
    276 
    277   // TODO(dbeam): i18n.
    278   return display_type + ASCIIToUTF16(" - ") + last_four_digits();
    279 }
    280 
    281 const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const {
    282   int idr = 0;
    283   switch (type_) {
    284     case AMEX:
    285       idr = IDR_AUTOFILL_CC_AMEX;
    286       break;
    287 
    288     case DISCOVER:
    289       idr = IDR_AUTOFILL_CC_DISCOVER;
    290       break;
    291 
    292     case MASTER_CARD:
    293       idr = IDR_AUTOFILL_CC_MASTERCARD;
    294       break;
    295 
    296     case VISA:
    297       idr = IDR_AUTOFILL_CC_VISA;
    298       break;
    299 
    300     case SOLO:
    301     case MAESTRO:
    302     case SWITCH:
    303     case UNKNOWN:
    304       idr = IDR_AUTOFILL_CC_GENERIC;
    305       break;
    306   }
    307 
    308   return ResourceBundle::GetSharedInstance().GetImageNamed(idr);
    309 }
    310 
    311 base::string16 WalletItems::MaskedInstrument::GetInfo(
    312     const AutofillType& type,
    313     const std::string& app_locale) const {
    314   if (type.group() != CREDIT_CARD)
    315     return address().GetInfo(type, app_locale);
    316 
    317   switch (type.GetStorableType()) {
    318     case CREDIT_CARD_NAME:
    319       return address().recipient_name();
    320 
    321     case CREDIT_CARD_NUMBER:
    322       return DisplayName();
    323 
    324     case CREDIT_CARD_EXP_4_DIGIT_YEAR:
    325       return base::IntToString16(expiration_year());
    326 
    327     case CREDIT_CARD_VERIFICATION_CODE:
    328       break;
    329 
    330     default:
    331       NOTREACHED();
    332   }
    333 
    334   return base::string16();
    335 }
    336 
    337 WalletItems::LegalDocument::~LegalDocument() {}
    338 
    339 scoped_ptr<WalletItems::LegalDocument>
    340     WalletItems::LegalDocument::CreateLegalDocument(
    341     const base::DictionaryValue& dictionary) {
    342   std::string id;
    343   if (!dictionary.GetString("legal_document_id", &id)) {
    344     DLOG(ERROR) << "Response from Google Wallet missing legal document id";
    345     return scoped_ptr<LegalDocument>();
    346   }
    347 
    348   base::string16 display_name;
    349   if (!dictionary.GetString("display_name", &display_name)) {
    350     DLOG(ERROR) << "Response from Google Wallet missing display name";
    351     return scoped_ptr<LegalDocument>();
    352   }
    353 
    354   return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name));
    355 }
    356 
    357 scoped_ptr<WalletItems::LegalDocument>
    358     WalletItems::LegalDocument::CreatePrivacyPolicyDocument() {
    359   return scoped_ptr<LegalDocument>(new LegalDocument(
    360       GURL(kPrivacyNoticeUrl),
    361       l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK)));
    362 }
    363 
    364 bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
    365   return id_ == other.id_ &&
    366          url_ == other.url_ &&
    367          display_name_ == other.display_name_;
    368 }
    369 
    370 bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
    371   return !(*this == other);
    372 }
    373 
    374 WalletItems::LegalDocument::LegalDocument(const std::string& id,
    375                                           const base::string16& display_name)
    376     : id_(id),
    377       url_(kLegalDocumentUrl + id),
    378       display_name_(display_name) {}
    379 
    380 WalletItems::LegalDocument::LegalDocument(const GURL& url,
    381                                           const base::string16& display_name)
    382     : url_(url),
    383       display_name_(display_name) {}
    384 
    385 WalletItems::WalletItems(const std::vector<RequiredAction>& required_actions,
    386                          const std::string& google_transaction_id,
    387                          const std::string& default_instrument_id,
    388                          const std::string& default_address_id,
    389                          const std::string& obfuscated_gaia_id)
    390     : required_actions_(required_actions),
    391       google_transaction_id_(google_transaction_id),
    392       default_instrument_id_(default_instrument_id),
    393       default_address_id_(default_address_id),
    394       obfuscated_gaia_id_(obfuscated_gaia_id) {}
    395 
    396 WalletItems::~WalletItems() {}
    397 
    398 scoped_ptr<WalletItems>
    399     WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) {
    400   std::vector<RequiredAction> required_action;
    401   const ListValue* required_action_list;
    402   if (dictionary.GetList("required_action", &required_action_list)) {
    403     for (size_t i = 0; i < required_action_list->GetSize(); ++i) {
    404       std::string action_string;
    405       if (required_action_list->GetString(i, &action_string)) {
    406         RequiredAction action = ParseRequiredActionFromString(action_string);
    407         if (!ActionAppliesToWalletItems(action)) {
    408           DLOG(ERROR) << "Response from Google wallet with bad required action:"
    409                          " \"" << action_string << "\"";
    410           return scoped_ptr<WalletItems>();
    411         }
    412         required_action.push_back(action);
    413       }
    414     }
    415   } else {
    416     DVLOG(1) << "Response from Google wallet missing required actions";
    417   }
    418 
    419   std::string google_transaction_id;
    420   if (!dictionary.GetString("google_transaction_id", &google_transaction_id) &&
    421       required_action.empty()) {
    422     DLOG(ERROR) << "Response from Google wallet missing google transaction id";
    423     return scoped_ptr<WalletItems>();
    424   }
    425 
    426   std::string default_instrument_id;
    427   if (!dictionary.GetString("default_instrument_id", &default_instrument_id))
    428     DVLOG(1) << "Response from Google wallet missing default instrument id";
    429 
    430   std::string default_address_id;
    431   if (!dictionary.GetString("default_address_id", &default_address_id))
    432     DVLOG(1) << "Response from Google wallet missing default_address_id";
    433 
    434   std::string obfuscated_gaia_id;
    435   if (!dictionary.GetString("obfuscated_gaia_id", &obfuscated_gaia_id))
    436     DVLOG(1) << "Response from Google wallet missing obfuscated gaia id";
    437 
    438   scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action,
    439                                                        google_transaction_id,
    440                                                        default_instrument_id,
    441                                                        default_address_id,
    442                                                        obfuscated_gaia_id));
    443 
    444   const ListValue* legal_docs;
    445   if (dictionary.GetList("required_legal_document", &legal_docs)) {
    446     for (size_t i = 0; i < legal_docs->GetSize(); ++i) {
    447       const DictionaryValue* legal_doc_dict;
    448       if (legal_docs->GetDictionary(i, &legal_doc_dict)) {
    449         scoped_ptr<LegalDocument> legal_doc(
    450             LegalDocument::CreateLegalDocument(*legal_doc_dict));
    451         if (legal_doc.get()) {
    452           wallet_items->AddLegalDocument(legal_doc.Pass());
    453         } else {
    454           DLOG(ERROR) << "Malformed legal document in response from "
    455                          "Google wallet";
    456           return scoped_ptr<WalletItems>();
    457         }
    458       }
    459     }
    460 
    461     if (!legal_docs->empty()) {
    462       // Always append the privacy policy link as well.
    463       wallet_items->AddLegalDocument(
    464           LegalDocument::CreatePrivacyPolicyDocument());
    465     }
    466   } else {
    467     DVLOG(1) << "Response from Google wallet missing legal docs";
    468   }
    469 
    470   const ListValue* instruments;
    471   if (dictionary.GetList("instrument", &instruments)) {
    472     for (size_t i = 0; i < instruments->GetSize(); ++i) {
    473       const DictionaryValue* instrument_dict;
    474       if (instruments->GetDictionary(i, &instrument_dict)) {
    475         scoped_ptr<MaskedInstrument> instrument(
    476             MaskedInstrument::CreateMaskedInstrument(*instrument_dict));
    477         if (instrument.get())
    478           wallet_items->AddInstrument(instrument.Pass());
    479         else
    480           DLOG(ERROR) << "Malformed instrument in response from Google Wallet";
    481       }
    482     }
    483   } else {
    484     DVLOG(1) << "Response from Google wallet missing instruments";
    485   }
    486 
    487   const ListValue* addresses;
    488   if (dictionary.GetList("address", &addresses)) {
    489     for (size_t i = 0; i < addresses->GetSize(); ++i) {
    490       const DictionaryValue* address_dict;
    491       if (addresses->GetDictionary(i, &address_dict)) {
    492         scoped_ptr<Address> address(
    493             Address::CreateAddressWithID(*address_dict));
    494         if (address.get())
    495           wallet_items->AddAddress(address.Pass());
    496         else
    497           DLOG(ERROR) << "Malformed address in response from Google Wallet";
    498       }
    499     }
    500   } else {
    501     DVLOG(1) << "Response from Google wallet missing addresses";
    502   }
    503 
    504   return wallet_items.Pass();
    505 }
    506 
    507 bool WalletItems::operator==(const WalletItems& other) const {
    508   return google_transaction_id_ == other.google_transaction_id_ &&
    509          default_instrument_id_ == other.default_instrument_id_ &&
    510          default_address_id_ == other.default_address_id_ &&
    511          required_actions_ == other.required_actions_ &&
    512          obfuscated_gaia_id_ == other.obfuscated_gaia_id_ &&
    513          VectorsAreEqual<MaskedInstrument>(instruments(),
    514                                            other.instruments()) &&
    515          VectorsAreEqual<Address>(addresses(), other.addresses()) &&
    516          VectorsAreEqual<LegalDocument>(legal_documents(),
    517                                         other.legal_documents());
    518 }
    519 
    520 bool WalletItems::operator!=(const WalletItems& other) const {
    521   return !(*this == other);
    522 }
    523 
    524 }  // namespace wallet
    525 }  // namespace autofill
    526