blob: 2f6bc47798872572c56d91bfe157b1087f16b86f [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ios/chrome/browser/payments/payment_request.h"
#include "base/containers/adapters.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/region_data_loader_impl.h"
#include "components/payments/core/currency_formatter.h"
#include "components/payments/core/payment_request_data_util.h"
#include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/autofill/validation_rules_storage_factory.h"
#include "ios/web/public/payments/payment_request.h"
#include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
std::unique_ptr<::i18n::addressinput::Source> GetAddressInputSource(
net::URLRequestContextGetter* url_context_getter) {
return std::unique_ptr<::i18n::addressinput::Source>(
new autofill::ChromeMetadataSource(I18N_ADDRESS_VALIDATION_DATA_URL,
url_context_getter));
}
std::unique_ptr<::i18n::addressinput::Storage> GetAddressInputStorage() {
return autofill::ValidationRulesStorageFactory::CreateStorage();
}
} // namespace
PaymentRequest::PaymentRequest(
const web::PaymentRequest& web_payment_request,
autofill::PersonalDataManager* personal_data_manager)
: web_payment_request_(web_payment_request),
personal_data_manager_(personal_data_manager),
selected_shipping_profile_(nullptr),
selected_contact_profile_(nullptr),
selected_credit_card_(nullptr),
selected_shipping_option_(nullptr),
profile_comparator_(GetApplicationContext()->GetApplicationLocale(),
*this) {
PopulateAvailableShippingOptions();
PopulateProfileCache();
PopulateAvailableProfiles();
PopulateCreditCardCache();
PopulateAvailableCreditCards();
SetSelectedShippingOption();
// If the merchant provided a default shipping option, and the highest-ranking
// shipping profile is usable, select it.
if (selected_shipping_option_ && !shipping_profiles_.empty() &&
profile_comparator_.IsShippingComplete(shipping_profiles_[0])) {
selected_shipping_profile_ = shipping_profiles_[0];
}
// If the highest-ranking contact profile is usable, select it. Otherwise,
// select none.
if (!contact_profiles_.empty() &&
profile_comparator_.IsContactInfoComplete(contact_profiles_[0])) {
selected_contact_profile_ = contact_profiles_[0];
}
// Select the highest ranking credit card.
if (!credit_cards_.empty())
selected_credit_card_ = credit_cards_[0];
}
PaymentRequest::~PaymentRequest() {}
void PaymentRequest::UpdatePaymentDetails(const web::PaymentDetails& details) {
web_payment_request_.details = details;
PopulateAvailableShippingOptions();
SetSelectedShippingOption();
}
bool PaymentRequest::request_shipping() const {
return web_payment_request_.options.request_shipping;
}
bool PaymentRequest::request_payer_name() const {
return web_payment_request_.options.request_payer_name;
}
bool PaymentRequest::request_payer_phone() const {
return web_payment_request_.options.request_payer_phone;
}
bool PaymentRequest::request_payer_email() const {
return web_payment_request_.options.request_payer_email;
}
payments::PaymentShippingType PaymentRequest::shipping_type() const {
return web_payment_request_.options.shipping_type;
}
payments::CurrencyFormatter* PaymentRequest::GetOrCreateCurrencyFormatter() {
if (!currency_formatter_) {
currency_formatter_.reset(new payments::CurrencyFormatter(
base::UTF16ToASCII(web_payment_request_.details.total.amount.currency),
base::UTF16ToASCII(
web_payment_request_.details.total.amount.currency_system),
GetApplicationContext()->GetApplicationLocale()));
}
return currency_formatter_.get();
}
autofill::RegionDataLoader* PaymentRequest::GetRegionDataLoader() {
return new autofill::RegionDataLoaderImpl(
GetAddressInputSource(
personal_data_manager_->GetURLRequestContextGetter())
.release(),
GetAddressInputStorage().release(),
GetApplicationContext()->GetApplicationLocale());
}
autofill::AutofillProfile* PaymentRequest::AddAutofillProfile(
const autofill::AutofillProfile& profile) {
profile_cache_.push_back(
base::MakeUnique<autofill::AutofillProfile>(profile));
PopulateAvailableProfiles();
return profile_cache_.back().get();
}
void PaymentRequest::PopulateProfileCache() {
const std::vector<autofill::AutofillProfile*>& profiles_to_suggest =
personal_data_manager_->GetProfilesToSuggest();
// Return early if the user has no stored Autofill profiles.
if (profiles_to_suggest.empty())
return;
profile_cache_.reserve(profiles_to_suggest.size());
for (const auto* profile : profiles_to_suggest) {
profile_cache_.push_back(
base::MakeUnique<autofill::AutofillProfile>(*profile));
}
}
void PaymentRequest::PopulateAvailableProfiles() {
if (profile_cache_.empty())
return;
std::vector<autofill::AutofillProfile*> raw_profiles_for_filtering;
raw_profiles_for_filtering.reserve(profile_cache_.size());
for (auto const& profile : profile_cache_) {
raw_profiles_for_filtering.push_back(profile.get());
}
// Contact profiles are deduped and ordered by completeness.
contact_profiles_ =
profile_comparator_.FilterProfilesForContact(raw_profiles_for_filtering);
// Shipping profiles are ordered by completeness.
shipping_profiles_ =
profile_comparator_.FilterProfilesForShipping(raw_profiles_for_filtering);
}
autofill::CreditCard* PaymentRequest::AddCreditCard(
const autofill::CreditCard& credit_card) {
credit_card_cache_.push_back(
base::MakeUnique<autofill::CreditCard>(credit_card));
PopulateAvailableCreditCards();
return credit_card_cache_.back().get();
}
payments::PaymentsProfileComparator* PaymentRequest::profile_comparator() {
return &profile_comparator_;
}
bool PaymentRequest::CanMakePayment() const {
return !credit_cards_.empty();
}
void PaymentRequest::PopulateCreditCardCache() {
// TODO(crbug.com/709036): Validate method data.
payments::data_util::ParseBasicCardSupportedNetworks(
web_payment_request_.method_data, &supported_card_networks_,
&basic_card_specified_networks_);
const std::vector<autofill::CreditCard*>& credit_cards_to_suggest =
personal_data_manager_->GetCreditCardsToSuggest();
// Return early if the user has no stored credit cards.
if (credit_cards_to_suggest.empty())
return;
credit_card_cache_.reserve(credit_cards_to_suggest.size());
for (const auto* credit_card : credit_cards_to_suggest) {
std::string spec_issuer_network =
autofill::data_util::GetPaymentRequestData(credit_card->network())
.basic_card_issuer_network;
if (std::find(supported_card_networks_.begin(),
supported_card_networks_.end(),
spec_issuer_network) != supported_card_networks_.end()) {
credit_card_cache_.push_back(
base::MakeUnique<autofill::CreditCard>(*credit_card));
}
}
}
void PaymentRequest::PopulateAvailableCreditCards() {
if (credit_card_cache_.empty())
return;
credit_cards_.clear();
credit_cards_.reserve(credit_card_cache_.size());
// TODO(crbug.com/602666): Implement prioritization rules for credit cards.
for (auto const& credit_card : credit_card_cache_) {
credit_cards_.push_back(credit_card.get());
}
}
void PaymentRequest::PopulateAvailableShippingOptions() {
shipping_options_.clear();
selected_shipping_option_ = nullptr;
if (web_payment_request_.details.shipping_options.empty())
return;
shipping_options_.reserve(
web_payment_request_.details.shipping_options.size());
std::transform(std::begin(web_payment_request_.details.shipping_options),
std::end(web_payment_request_.details.shipping_options),
std::back_inserter(shipping_options_),
[](web::PaymentShippingOption& option) { return &option; });
}
void PaymentRequest::SetSelectedShippingOption() {
// If more than one option has |selected| set, the last one in the sequence
// should be treated as the selected item.
for (auto* shipping_option : base::Reversed(shipping_options_)) {
if (shipping_option->selected) {
selected_shipping_option_ = shipping_option;
break;
}
}
}