blob: 0ac1e5f947f944adb6dd122ac1d2fa81c8dc75a4 [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 <algorithm>
#include <memory>
#include "base/containers/adapters.h"
#include "base/feature_list.h"
#include "base/logging.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/personal_data_manager.h"
#include "components/autofill/core/browser/region_data_loader_impl.h"
#include "components/autofill/core/browser/validation.h"
#include "components/payments/core/autofill_payment_instrument.h"
#include "components/payments/core/currency_formatter.h"
#include "components/payments/core/features.h"
#include "components/payments/core/payment_details.h"
#include "components/payments/core/payment_item.h"
#include "components/payments/core/payment_request_data_util.h"
#include "components/payments/core/payment_shipping_option.h"
#include "components/payments/core/web_payment_request.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_manager.h"
#include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/autofill/validation_rules_storage_factory.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/payments/ios_payment_instrument.h"
#import "ios/chrome/browser/payments/payment_request_util.h"
#include "ios/chrome/browser/signin/signin_manager_factory.h"
#include "ios/web/public/web_state/web_state.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 payments {
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();
}
// Validates the |method_data| and fills the output parameters.
void PopulateValidatedMethodData(
const std::vector<PaymentMethodData>& method_data,
std::vector<std::string>* supported_card_networks,
std::set<std::string>* basic_card_specified_networks,
std::set<std::string>* supported_card_networks_set,
std::set<autofill::CreditCard::CardType>* supported_card_types,
std::vector<GURL>* url_payment_method_identifiers,
std::set<std::string>* payment_method_identifiers) {
data_util::ParseSupportedMethods(
method_data, supported_card_networks, basic_card_specified_networks,
url_payment_method_identifiers, payment_method_identifiers);
supported_card_networks_set->insert(supported_card_networks->begin(),
supported_card_networks->end());
data_util::ParseSupportedCardTypes(method_data, supported_card_types);
}
} // namespace
PaymentRequest::PaymentRequest(
const payments::WebPaymentRequest& web_payment_request,
ios::ChromeBrowserState* browser_state,
web::WebState* web_state,
autofill::PersonalDataManager* personal_data_manager,
id<PaymentRequestUIDelegate> payment_request_ui_delegate)
: state_(State::CREATED),
updating_(false),
web_payment_request_(web_payment_request),
browser_state_(browser_state),
web_state_(web_state),
personal_data_manager_(personal_data_manager),
payment_request_ui_delegate_(payment_request_ui_delegate),
address_normalizer_(
GetAddressInputSource(
personal_data_manager_->GetURLRequestContextGetter()),
GetAddressInputStorage()),
address_normalization_manager_(
&address_normalizer_,
GetApplicationContext()->GetApplicationLocale()),
selected_shipping_profile_(nullptr),
selected_contact_profile_(nullptr),
selected_payment_method_(nullptr),
selected_shipping_option_(nullptr),
profile_comparator_(GetApplicationLocale(), *this),
journey_logger_(IsIncognito(), GetLastCommittedURL(), GetUkmRecorder()),
payment_instruments_ready_(false),
ios_instrument_finder_(
personal_data_manager_->GetURLRequestContextGetter(),
payment_request_ui_delegate_) {
PopulateAvailableShippingOptions();
PopulateProfileCache();
PopulateAvailableProfiles();
ParsePaymentMethodData();
CreateNativeAppPaymentMethods();
SetSelectedShippingOption();
if (request_shipping()) {
// 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 (request_payer_name() || request_payer_email() || request_payer_phone()) {
// 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];
}
}
// Kickoff the process of loading the rules (which is asynchronous) for each
// profile's country, to get faster address normalization later.
for (const autofill::AutofillProfile* profile :
personal_data_manager_->GetProfilesToSuggest()) {
std::string countryCode =
base::UTF16ToUTF8(profile->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
if (autofill::data_util::IsValidCountryCode(countryCode)) {
address_normalizer_.LoadRulesForRegion(countryCode);
}
}
RecordNumberOfSuggestionsShown();
RecordRequestedInformation();
}
PaymentRequest::~PaymentRequest() {}
bool PaymentRequest::Compare::operator()(
const std::unique_ptr<PaymentRequest>& lhs,
const std::unique_ptr<PaymentRequest>& rhs) const {
return lhs->web_payment_request().payment_request_id !=
rhs->web_payment_request().payment_request_id;
}
autofill::PersonalDataManager* PaymentRequest::GetPersonalDataManager() {
return personal_data_manager_;
}
const std::string& PaymentRequest::GetApplicationLocale() const {
return GetApplicationContext()->GetApplicationLocale();
}
bool PaymentRequest::IsIncognito() const {
return browser_state_->IsOffTheRecord();
}
bool PaymentRequest::IsSslCertificateValid() {
NOTREACHED() << "Implementation is never used";
return false;
}
const GURL& PaymentRequest::GetLastCommittedURL() const {
return web_state_->GetLastCommittedURL();
}
void PaymentRequest::DoFullCardRequest(
const autofill::CreditCard& credit_card,
base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
result_delegate) {
[payment_request_ui_delegate_ paymentRequest:this
requestFullCreditCard:credit_card
resultDelegate:result_delegate];
}
autofill::AddressNormalizer* PaymentRequest::GetAddressNormalizer() {
return &address_normalizer_;
}
autofill::RegionDataLoader* PaymentRequest::GetRegionDataLoader() {
return new autofill::RegionDataLoaderImpl(
GetAddressInputSource(
personal_data_manager_->GetURLRequestContextGetter())
.release(),
GetAddressInputStorage().release(), GetApplicationLocale());
}
ukm::UkmRecorder* PaymentRequest::GetUkmRecorder() {
return GetApplicationContext()->GetUkmRecorder();
}
std::string PaymentRequest::GetAuthenticatedEmail() const {
const SigninManager* signin_manager =
ios::SigninManagerFactory::GetForBrowserStateIfExists(browser_state_);
if (signin_manager && signin_manager->IsAuthenticated())
return signin_manager->GetAuthenticatedAccountInfo().email;
else
return std::string();
}
PrefService* PaymentRequest::GetPrefService() {
return browser_state_->GetPrefs();
}
const PaymentItem& PaymentRequest::GetTotal(
PaymentInstrument* selected_instrument) const {
const PaymentDetailsModifier* modifier =
GetApplicableModifier(selected_instrument);
if (modifier && modifier->total) {
return *modifier->total;
} else {
DCHECK(web_payment_request_.details.total);
return *web_payment_request_.details.total;
}
}
std::vector<PaymentItem> PaymentRequest::GetDisplayItems(
PaymentInstrument* selected_instrument) const {
std::vector<PaymentItem> display_items =
web_payment_request_.details.display_items;
const PaymentDetailsModifier* modifier =
GetApplicableModifier(selected_instrument);
if (modifier) {
display_items.insert(display_items.end(),
modifier->additional_display_items.begin(),
modifier->additional_display_items.end());
}
return display_items;
}
void PaymentRequest::UpdatePaymentDetails(const PaymentDetails& details) {
DCHECK(web_payment_request_.details.total);
std::unique_ptr<PaymentItem> old_total =
std::move(web_payment_request_.details.total);
web_payment_request_.details = details;
// Restore the old total amount if the PaymentDetails passed to updateWith()
// is missing a total value.
if (!web_payment_request_.details.total)
web_payment_request_.details.total = std::move(old_total);
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;
}
PaymentShippingType PaymentRequest::shipping_type() const {
return web_payment_request_.options.shipping_type;
}
CurrencyFormatter* PaymentRequest::GetOrCreateCurrencyFormatter() {
if (!currency_formatter_) {
DCHECK(web_payment_request_.details.total);
currency_formatter_ = base::MakeUnique<CurrencyFormatter>(
web_payment_request_.details.total->amount.currency,
web_payment_request_.details.total->amount.currency_system,
GetApplicationLocale());
}
return currency_formatter_.get();
}
autofill::AddressNormalizationManager*
PaymentRequest::GetAddressNormalizationManager() {
return &address_normalization_manager_;
}
autofill::AutofillProfile* PaymentRequest::AddAutofillProfile(
const autofill::AutofillProfile& profile) {
profile_cache_.push_back(
base::MakeUnique<autofill::AutofillProfile>(profile));
contact_profiles_.push_back(profile_cache_.back().get());
shipping_profiles_.push_back(profile_cache_.back().get());
return profile_cache_.back().get();
}
const PaymentDetailsModifier* PaymentRequest::GetApplicableModifier(
PaymentInstrument* selected_instrument) const {
if (!selected_instrument ||
!base::FeatureList::IsEnabled(features::kWebPaymentsModifiers)) {
return nullptr;
}
for (const auto& modifier : web_payment_request_.details.modifiers) {
std::vector<std::string> supported_networks;
std::set<autofill::CreditCard::CardType> supported_types;
// The following 4 variables are unused.
std::set<std::string> unused_basic_card_specified_networks;
std::set<std::string> unused_supported_card_networks_set;
std::vector<GURL> unused_url_payment_method_identifiers;
std::set<std::string> unused_payment_method_identifiers;
PopulateValidatedMethodData({modifier.method_data}, &supported_networks,
&unused_basic_card_specified_networks,
&unused_supported_card_networks_set,
&supported_types,
&unused_url_payment_method_identifiers,
&unused_payment_method_identifiers);
if (selected_instrument->IsValidForModifier(
modifier.method_data.supported_methods, supported_networks,
supported_types, !modifier.method_data.supported_types.empty())) {
return &modifier;
}
}
return nullptr;
}
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_.clear();
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);
}
AutofillPaymentInstrument* PaymentRequest::AddAutofillPaymentInstrument(
const autofill::CreditCard& credit_card) {
std::string basic_card_issuer_network =
autofill::data_util::GetPaymentRequestData(credit_card.network())
.basic_card_issuer_network;
if (!supported_card_networks_set_.count(basic_card_issuer_network) ||
!supported_card_types_set_.count(credit_card.card_type())) {
return nullptr;
}
// If the merchant specified the card network as part of the "basic-card"
// payment method, use "basic-card" as the method_name. Otherwise, use
// the name of the network directly.
std::string method_name = basic_card_issuer_network;
if (basic_card_specified_networks_.count(basic_card_issuer_network)) {
method_name = "basic_card";
}
// The total number of card types: credit, debit, prepaid, unknown.
constexpr size_t kTotalNumberOfCardTypes = 4U;
// Whether the card type (credit, debit, prepaid) matches the type that the
// merchant has requested exactly. This should be false for unknown card
// types, if the merchant cannot accept some card types.
bool matches_merchant_card_type_exactly =
credit_card.card_type() != autofill::CreditCard::CARD_TYPE_UNKNOWN ||
supported_card_types_set_.size() == kTotalNumberOfCardTypes;
// AutofillPaymentInstrument makes a copy of |credit_card| so it is
// effectively owned by this object.
payment_method_cache_.push_back(base::MakeUnique<AutofillPaymentInstrument>(
method_name, credit_card, matches_merchant_card_type_exactly,
billing_profiles(), GetApplicationLocale(), this));
payment_methods_.push_back(payment_method_cache_.back().get());
return static_cast<AutofillPaymentInstrument*>(
payment_method_cache_.back().get());
}
PaymentsProfileComparator* PaymentRequest::profile_comparator() {
return &profile_comparator_;
}
const PaymentsProfileComparator* PaymentRequest::profile_comparator() const {
// Return a const version of what the non-const |profile_comparator| method
// returns.
return const_cast<PaymentRequest*>(this)->profile_comparator();
}
bool PaymentRequest::CanMakePayment() const {
for (PaymentInstrument* payment_method : payment_methods_) {
if (payment_method->IsValidForCanMakePayment()) {
return true;
}
}
return false;
}
void PaymentRequest::InvokePaymentApp(
id<PaymentResponseHelperConsumer> consumer) {
DCHECK(selected_payment_method());
response_helper_ = base::MakeUnique<PaymentResponseHelper>(consumer, this);
selected_payment_method()->InvokePaymentApp(response_helper_.get());
}
bool PaymentRequest::IsPaymentAppInvoked() const {
return !!response_helper_;
}
void PaymentRequest::RecordUseStats() {
if (request_shipping()) {
DCHECK(selected_shipping_profile_);
personal_data_manager_->RecordUseOf(*selected_shipping_profile_);
}
if (request_payer_name() || request_payer_email() || request_payer_phone()) {
DCHECK(selected_contact_profile_);
// If the same address was used for both contact and shipping, the stats
// should be updated only once.
if (!request_shipping() || (selected_shipping_profile_->guid() !=
selected_contact_profile_->guid())) {
personal_data_manager_->RecordUseOf(*selected_contact_profile_);
}
}
selected_payment_method_->RecordUse();
}
void PaymentRequest::ParsePaymentMethodData() {
for (const PaymentMethodData& method_data_entry :
web_payment_request_.method_data) {
for (const std::string& method : method_data_entry.supported_methods) {
stringified_method_data_[method].insert(method_data_entry.data);
}
}
std::set<std::string> unused_payment_method_identifiers;
PopulateValidatedMethodData(
web_payment_request_.method_data, &supported_card_networks_,
&basic_card_specified_networks_, &supported_card_networks_set_,
&supported_card_types_set_, &url_payment_method_identifiers_,
&unused_payment_method_identifiers);
}
void PaymentRequest::CreateNativeAppPaymentMethods() {
if (!base::FeatureList::IsEnabled(
payments::features::kWebPaymentsNativeApps)) {
PopulatePaymentMethodCache(
std::vector<std::unique_ptr<IOSPaymentInstrument>>());
return;
}
url_payment_method_identifiers_ =
ios_instrument_finder_.CreateIOSPaymentInstrumentsForMethods(
url_payment_method_identifiers_,
base::BindOnce(&PaymentRequest::PopulatePaymentMethodCache,
base::Unretained(this)));
}
void PaymentRequest::PopulatePaymentMethodCache(
std::vector<std::unique_ptr<IOSPaymentInstrument>> native_app_instruments) {
const std::vector<autofill::CreditCard*>& credit_cards_to_suggest =
personal_data_manager_->GetCreditCardsToSuggest();
// Return early if the user has no stored credit cards or installed payment
// apps.
if (native_app_instruments.empty() && credit_cards_to_suggest.empty()) {
payment_instruments_ready_ = true;
[payment_request_ui_delegate_ paymentRequestDidFetchPaymentMethods:this];
return;
}
payment_method_cache_.clear();
payment_method_cache_.reserve(native_app_instruments.size() +
credit_cards_to_suggest.size());
for (auto& instrument : native_app_instruments)
payment_method_cache_.push_back(std::move(instrument));
for (const auto* credit_card : credit_cards_to_suggest)
AddAutofillPaymentInstrument(*credit_card);
PopulateAvailablePaymentMethods();
const auto first_complete_payment_method =
std::find_if(payment_methods_.begin(), payment_methods_.end(),
[this](PaymentInstrument* payment_method) {
return payment_method->IsCompleteForPayment() &&
payment_method->IsExactlyMatchingMerchantRequest();
});
if (first_complete_payment_method != payment_methods_.end())
selected_payment_method_ = *first_complete_payment_method;
payment_instruments_ready_ = true;
[payment_request_ui_delegate_ paymentRequestDidFetchPaymentMethods:this];
}
void PaymentRequest::PopulateAvailablePaymentMethods() {
if (payment_method_cache_.empty())
return;
payment_methods_.clear();
payment_methods_.reserve(payment_method_cache_.size());
for (auto const& payment_method : payment_method_cache_)
payment_methods_.push_back(payment_method.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_),
[](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;
}
}
}
void PaymentRequest::RecordNumberOfSuggestionsShown() {
if (request_payer_name() || request_payer_phone() || request_payer_email()) {
const bool has_complete_contact = (selected_contact_profile_ != nullptr);
journey_logger().SetNumberOfSuggestionsShown(
payments::JourneyLogger::Section::SECTION_CONTACT_INFO,
contact_profiles().size(), has_complete_contact);
}
if (request_shipping()) {
const bool has_complete_shipping = (selected_shipping_profile_ != nullptr);
journey_logger().SetNumberOfSuggestionsShown(
payments::JourneyLogger::Section::SECTION_SHIPPING_ADDRESS,
shipping_profiles().size(), has_complete_shipping);
}
const bool has_complete_instrument = (selected_payment_method_ != nullptr);
journey_logger().SetNumberOfSuggestionsShown(
payments::JourneyLogger::Section::SECTION_PAYMENT_METHOD,
payment_methods().size(), has_complete_instrument);
}
void PaymentRequest::RecordRequestedInformation() {
journey_logger().SetRequestedInformation(
request_shipping(), request_payer_email(), request_payer_phone(),
request_payer_name());
// Log metrics around which payment methods are requested by the merchant.
const GURL kGooglePayUrl("https://google.com/pay");
const GURL kAndroidPayUrl("https://android.com/pay");
// Looking for payment methods that are NOT Google-related as well as the
// Google-related ones.
bool requestedMethodGoogle = false;
bool requestedMethodOther = false;
for (const GURL& url_payment_method : url_payment_method_identifiers()) {
if (url_payment_method == kGooglePayUrl ||
url_payment_method == kAndroidPayUrl) {
requestedMethodGoogle = true;
} else {
requestedMethodOther = true;
}
}
journey_logger().SetRequestedPaymentMethodTypes(
/*requested_basic_card=*/!supported_card_networks().empty(),
/*requested_method_google=*/requestedMethodGoogle,
/*requested_method_other=*/requestedMethodOther);
}
} // namespace payments