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