wallet_items.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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 "googleurl/src/gurl.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
20namespace autofill {
21namespace wallet {
22
23namespace {
24
25const char kLegalDocumentUrl[] =
26    "https://wallet.google.com/legaldocument?docId=";
27const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html";
28
29// TODO(estade): move to base/.
30template<class T>
31bool 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
43WalletItems::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
62WalletItems::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
85WalletItems::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
107WalletItems::MaskedInstrument::~MaskedInstrument() {}
108
109scoped_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
189bool 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
220bool WalletItems::MaskedInstrument::operator!=(
221    const WalletItems::MaskedInstrument& other) const {
222  return !(*this == other);
223}
224
225const 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
238bool 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
245base::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
254base::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
263base::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
281const 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
311base::string16 WalletItems::MaskedInstrument::GetInfo(
312    AutofillFieldType type,
313    const std::string& app_locale) const {
314  if (AutofillType(type).group() != AutofillType::CREDIT_CARD)
315    return address().GetInfo(type, app_locale);
316
317  switch (type) {
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
337WalletItems::LegalDocument::~LegalDocument() {}
338
339scoped_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
357scoped_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
364bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
365  return id_ == other.id_ &&
366         url_ == other.url_ &&
367         display_name_ == other.display_name_;
368}
369
370bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
371  return !(*this == other);
372}
373
374WalletItems::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
380WalletItems::LegalDocument::LegalDocument(const GURL& url,
381                                          const base::string16& display_name)
382    : url_(url),
383      display_name_(display_name) {}
384
385WalletItems::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
396WalletItems::~WalletItems() {}
397
398scoped_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
507bool 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
520bool WalletItems::operator!=(const WalletItems& other) const {
521  return !(*this == other);
522}
523
524}  // namespace wallet
525}  // namespace autofill
526