wallet_items.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/browser/autofill_type.h"
12#include "components/autofill/browser/credit_card.h"
13#include "googleurl/src/gurl.h"
14#include "grit/component_resources.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_ == SOLO)
273    display_type = CreditCard::TypeForDisplay(kSoloCard);
274  else if (type_ == VISA)
275    display_type = CreditCard::TypeForDisplay(kVisaCard);
276  else
277    display_type = CreditCard::TypeForDisplay(kGenericCard);
278
279  // TODO(dbeam): i18n.
280  return display_type + ASCIIToUTF16(" - ") + last_four_digits();
281}
282
283const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const {
284  int idr = 0;
285  switch (type_) {
286    case AMEX:
287      idr = IDR_AUTOFILL_CC_AMEX;
288      break;
289
290    case DISCOVER:
291      idr = IDR_AUTOFILL_CC_DISCOVER;
292      break;
293
294    case MASTER_CARD:
295      idr = IDR_AUTOFILL_CC_MASTERCARD;
296      break;
297
298    case SOLO:
299      idr = IDR_AUTOFILL_CC_SOLO;
300      break;
301
302    case VISA:
303      idr = IDR_AUTOFILL_CC_VISA;
304      break;
305
306    case MAESTRO:
307    case SWITCH:
308    case UNKNOWN:
309      idr = IDR_AUTOFILL_CC_GENERIC;
310      break;
311  }
312
313  return ResourceBundle::GetSharedInstance().GetImageNamed(idr);
314}
315
316base::string16 WalletItems::MaskedInstrument::GetInfo(
317    AutofillFieldType type,
318    const std::string& app_locale) const {
319  if (AutofillType(type).group() != AutofillType::CREDIT_CARD)
320    return address().GetInfo(type, app_locale);
321
322  switch (type) {
323    case CREDIT_CARD_NAME:
324      return address().recipient_name();
325
326    case CREDIT_CARD_NUMBER:
327      return DisplayName();
328
329    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
330      return base::IntToString16(expiration_year());
331
332    case CREDIT_CARD_VERIFICATION_CODE:
333      break;
334
335    default:
336      NOTREACHED();
337  }
338
339  return base::string16();
340}
341
342WalletItems::LegalDocument::~LegalDocument() {}
343
344scoped_ptr<WalletItems::LegalDocument>
345    WalletItems::LegalDocument::CreateLegalDocument(
346    const base::DictionaryValue& dictionary) {
347  std::string id;
348  if (!dictionary.GetString("legal_document_id", &id)) {
349    DLOG(ERROR) << "Response from Google Wallet missing legal document id";
350    return scoped_ptr<LegalDocument>();
351  }
352
353  base::string16 display_name;
354  if (!dictionary.GetString("display_name", &display_name)) {
355    DLOG(ERROR) << "Response from Google Wallet missing display name";
356    return scoped_ptr<LegalDocument>();
357  }
358
359  return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name));
360}
361
362scoped_ptr<WalletItems::LegalDocument>
363    WalletItems::LegalDocument::CreatePrivacyPolicyDocument() {
364  return scoped_ptr<LegalDocument>(new LegalDocument(
365      GURL(kPrivacyNoticeUrl),
366      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK)));
367}
368
369bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
370  return id_ == other.id_ &&
371         url_ == other.url_ &&
372         display_name_ == other.display_name_;
373}
374
375bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
376  return !(*this == other);
377}
378
379WalletItems::LegalDocument::LegalDocument(const std::string& id,
380                                          const base::string16& display_name)
381    : id_(id),
382      url_(kLegalDocumentUrl + id),
383      display_name_(display_name) {}
384
385WalletItems::LegalDocument::LegalDocument(const GURL& url,
386                                          const base::string16& display_name)
387    : url_(url),
388      display_name_(display_name) {}
389
390WalletItems::WalletItems(const std::vector<RequiredAction>& required_actions,
391                         const std::string& google_transaction_id,
392                         const std::string& default_instrument_id,
393                         const std::string& default_address_id,
394                         const std::string& obfuscated_gaia_id)
395    : required_actions_(required_actions),
396      google_transaction_id_(google_transaction_id),
397      default_instrument_id_(default_instrument_id),
398      default_address_id_(default_address_id),
399      obfuscated_gaia_id_(obfuscated_gaia_id) {}
400
401WalletItems::~WalletItems() {}
402
403scoped_ptr<WalletItems>
404    WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) {
405  std::vector<RequiredAction> required_action;
406  const ListValue* required_action_list;
407  if (dictionary.GetList("required_action", &required_action_list)) {
408    for (size_t i = 0; i < required_action_list->GetSize(); ++i) {
409      std::string action_string;
410      if (required_action_list->GetString(i, &action_string)) {
411        RequiredAction action = ParseRequiredActionFromString(action_string);
412        if (!ActionAppliesToWalletItems(action)) {
413          DLOG(ERROR) << "Response from Google wallet with bad required action:"
414                         " \"" << action_string << "\"";
415          return scoped_ptr<WalletItems>();
416        }
417        required_action.push_back(action);
418      }
419    }
420  } else {
421    DVLOG(1) << "Response from Google wallet missing required actions";
422  }
423
424  std::string google_transaction_id;
425  if (!dictionary.GetString("google_transaction_id", &google_transaction_id) &&
426      required_action.empty()) {
427    DLOG(ERROR) << "Response from Google wallet missing google transaction id";
428    return scoped_ptr<WalletItems>();
429  }
430
431  std::string default_instrument_id;
432  if (!dictionary.GetString("default_instrument_id", &default_instrument_id))
433    DVLOG(1) << "Response from Google wallet missing default instrument id";
434
435  std::string default_address_id;
436  if (!dictionary.GetString("default_address_id", &default_address_id))
437    DVLOG(1) << "Response from Google wallet missing default_address_id";
438
439  std::string obfuscated_gaia_id;
440  if (!dictionary.GetString("obfuscated_gaia_id", &obfuscated_gaia_id))
441    DVLOG(1) << "Response from Google wallet missing obfuscated gaia id";
442
443  scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action,
444                                                       google_transaction_id,
445                                                       default_instrument_id,
446                                                       default_address_id,
447                                                       obfuscated_gaia_id));
448
449  const ListValue* legal_docs;
450  if (dictionary.GetList("required_legal_document", &legal_docs)) {
451    for (size_t i = 0; i < legal_docs->GetSize(); ++i) {
452      const DictionaryValue* legal_doc_dict;
453      if (legal_docs->GetDictionary(i, &legal_doc_dict)) {
454        scoped_ptr<LegalDocument> legal_doc(
455            LegalDocument::CreateLegalDocument(*legal_doc_dict));
456        if (legal_doc.get()) {
457          wallet_items->AddLegalDocument(legal_doc.Pass());
458        } else {
459          DLOG(ERROR) << "Malformed legal document in response from "
460                         "Google wallet";
461          return scoped_ptr<WalletItems>();
462        }
463      }
464    }
465
466    if (!legal_docs->empty()) {
467      // Always append the privacy policy link as well.
468      wallet_items->AddLegalDocument(
469          LegalDocument::CreatePrivacyPolicyDocument());
470    }
471  } else {
472    DVLOG(1) << "Response from Google wallet missing legal docs";
473  }
474
475  const ListValue* instruments;
476  if (dictionary.GetList("instrument", &instruments)) {
477    for (size_t i = 0; i < instruments->GetSize(); ++i) {
478      const DictionaryValue* instrument_dict;
479      if (instruments->GetDictionary(i, &instrument_dict)) {
480        scoped_ptr<MaskedInstrument> instrument(
481            MaskedInstrument::CreateMaskedInstrument(*instrument_dict));
482        if (instrument.get())
483          wallet_items->AddInstrument(instrument.Pass());
484        else
485          DLOG(ERROR) << "Malformed instrument in response from Google Wallet";
486      }
487    }
488  } else {
489    DVLOG(1) << "Response from Google wallet missing instruments";
490  }
491
492  const ListValue* addresses;
493  if (dictionary.GetList("address", &addresses)) {
494    for (size_t i = 0; i < addresses->GetSize(); ++i) {
495      const DictionaryValue* address_dict;
496      if (addresses->GetDictionary(i, &address_dict)) {
497        scoped_ptr<Address> address(
498            Address::CreateAddressWithID(*address_dict));
499        if (address.get())
500          wallet_items->AddAddress(address.Pass());
501        else
502          DLOG(ERROR) << "Malformed address in response from Google Wallet";
503      }
504    }
505  } else {
506    DVLOG(1) << "Response from Google wallet missing addresses";
507  }
508
509  return wallet_items.Pass();
510}
511
512bool WalletItems::operator==(const WalletItems& other) const {
513  return google_transaction_id_ == other.google_transaction_id_ &&
514         default_instrument_id_ == other.default_instrument_id_ &&
515         default_address_id_ == other.default_address_id_ &&
516         required_actions_ == other.required_actions_ &&
517         obfuscated_gaia_id_ == other.obfuscated_gaia_id_ &&
518         VectorsAreEqual<MaskedInstrument>(instruments(),
519                                           other.instruments()) &&
520         VectorsAreEqual<Address>(addresses(), other.addresses()) &&
521         VectorsAreEqual<LegalDocument>(legal_documents(),
522                                        other.legal_documents());
523}
524
525bool WalletItems::operator!=(const WalletItems& other) const {
526  return !(*this == other);
527}
528
529}  // namespace wallet
530}  // namespace autofill
531