blob: 47fe27f1539d65424b442caf421c0ae5f2ff5ab0 [file] [log] [blame]
mathpf1a7a3752017-03-15 11:23:371// Copyright 2016 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/payments/content/payment_request_state.h"
6
anthonyvd0116ce332017-03-21 21:29:017#include <set>
8
mathpf1a7a3752017-03-15 11:23:379#include "components/autofill/core/browser/autofill_data_util.h"
10#include "components/autofill/core/browser/autofill_profile.h"
11#include "components/autofill/core/browser/credit_card.h"
mathpc0d616a2017-03-15 14:09:3312#include "components/autofill/core/browser/personal_data_manager.h"
mathpf1a7a3752017-03-15 11:23:3713#include "components/payments/content/payment_request_spec.h"
14#include "components/payments/core/autofill_payment_instrument.h"
15
16namespace payments {
17
mathpc0d616a2017-03-15 14:09:3318PaymentRequestState::PaymentRequestState(
19 PaymentRequestSpec* spec,
20 Delegate* delegate,
21 const std::string& app_locale,
22 autofill::PersonalDataManager* personal_data_manager)
mathpf1a7a3752017-03-15 11:23:3723 : is_ready_to_pay_(false),
mathpc0d616a2017-03-15 14:09:3324 app_locale_(app_locale),
mathpf1a7a3752017-03-15 11:23:3725 spec_(spec),
26 delegate_(delegate),
mathpc0d616a2017-03-15 14:09:3327 personal_data_manager_(personal_data_manager),
mathpf1a7a3752017-03-15 11:23:3728 selected_shipping_profile_(nullptr),
29 selected_contact_profile_(nullptr),
mathp363735b2017-03-16 18:08:0530 selected_instrument_(nullptr),
mathpf1a7a3752017-03-15 11:23:3731 selected_shipping_option_(nullptr) {
32 PopulateProfileCache();
33 UpdateSelectedShippingOption();
34 SetDefaultProfileSelections();
35}
36
mathp1a5be4f2017-03-24 18:09:1937bool PaymentRequestState::CanMakePayment() const {
mathp1a5be4f2017-03-24 18:09:1938 for (const std::unique_ptr<PaymentInstrument>& instrument :
39 available_instruments_) {
40 if (instrument.get()->IsValid() &&
41 spec_->supported_card_networks_set().count(
42 instrument.get()->method_name())) {
43 return true;
44 }
45 }
46 return false;
47}
48
mathpf1a7a3752017-03-15 11:23:3749void PaymentRequestState::AddObserver(Observer* observer) {
50 CHECK(observer);
51 observers_.AddObserver(observer);
52}
53PaymentRequestState::~PaymentRequestState() {}
54
55void PaymentRequestState::RemoveObserver(Observer* observer) {
56 observers_.RemoveObserver(observer);
57}
58
59void PaymentRequestState::OnInstrumentDetailsReady(
60 const std::string& method_name,
61 const std::string& stringified_details) {
62 // TODO(mathp): Fill other fields in the PaymentResponsePtr object.
63 mojom::PaymentResponsePtr payment_response = mojom::PaymentResponse::New();
64
mathp600bab52017-03-26 03:47:5965 // Make sure that we return the method name that the merchant specified for
66 // this instrument: cards can be either specified through their name (e.g.,
67 // "visa") or through basic-card's supportedNetworks.
68 payment_response->method_name =
69 spec_->IsMethodSupportedThroughBasicCard(method_name)
70 ? kBasicCardMethodName
71 : method_name;
mathpf1a7a3752017-03-15 11:23:3772 payment_response->stringified_details = stringified_details;
73 delegate_->OnPaymentResponseAvailable(std::move(payment_response));
74}
75
76void PaymentRequestState::GeneratePaymentResponse() {
mathp363735b2017-03-16 18:08:0577 DCHECK(is_ready_to_pay());
mathpf1a7a3752017-03-15 11:23:3778 // Fetch the instrument details, will call back into
79 // PaymentRequest::OnInstrumentsDetailsReady.
mathp363735b2017-03-16 18:08:0580 selected_instrument_->InvokePaymentApp(this);
mathpf1a7a3752017-03-15 11:23:3781}
82
anthonyvd0116ce332017-03-21 21:29:0183void PaymentRequestState::SetSelectedShippingOption(
84 mojom::PaymentShippingOption* option) {
85 selected_shipping_option_ = option;
86 UpdateIsReadyToPayAndNotifyObservers();
87}
88
mathpf1a7a3752017-03-15 11:23:3789void PaymentRequestState::SetSelectedShippingProfile(
90 autofill::AutofillProfile* profile) {
91 selected_shipping_profile_ = profile;
92 UpdateIsReadyToPayAndNotifyObservers();
93}
94
95void PaymentRequestState::SetSelectedContactProfile(
96 autofill::AutofillProfile* profile) {
97 selected_contact_profile_ = profile;
98 UpdateIsReadyToPayAndNotifyObservers();
99}
100
mathp363735b2017-03-16 18:08:05101void PaymentRequestState::SetSelectedInstrument(PaymentInstrument* instrument) {
102 selected_instrument_ = instrument;
mathpf1a7a3752017-03-15 11:23:37103 UpdateIsReadyToPayAndNotifyObservers();
104}
105
mathpc0d616a2017-03-15 14:09:33106const std::string& PaymentRequestState::GetApplicationLocale() {
107 return app_locale_;
108}
109
110autofill::PersonalDataManager* PaymentRequestState::GetPersonalDataManager() {
111 return personal_data_manager_;
112}
113
mathpf1a7a3752017-03-15 11:23:37114void PaymentRequestState::PopulateProfileCache() {
mathpf1a7a3752017-03-15 11:23:37115 std::vector<autofill::AutofillProfile*> profiles =
mathpc0d616a2017-03-15 14:09:33116 personal_data_manager_->GetProfilesToSuggest();
mathpf1a7a3752017-03-15 11:23:37117
118 // PaymentRequest may outlive the Profiles returned by the Data Manager.
119 // Thus, we store copies, and return a vector of pointers to these copies
120 // whenever Profiles are requested. The same is true for credit cards.
121 for (size_t i = 0; i < profiles.size(); i++) {
122 profile_cache_.push_back(
123 base::MakeUnique<autofill::AutofillProfile>(*profiles[i]));
124
125 // TODO(tmartino): Implement deduplication rules specific to shipping and
126 // contact profiles.
127 shipping_profiles_.push_back(profile_cache_[i].get());
128 contact_profiles_.push_back(profile_cache_[i].get());
129 }
130
mathp363735b2017-03-16 18:08:05131 // Create the list of available instruments.
mathpf1a7a3752017-03-15 11:23:37132 const std::vector<autofill::CreditCard*>& cards =
mathpc0d616a2017-03-15 14:09:33133 personal_data_manager_->GetCreditCardsToSuggest();
mathp363735b2017-03-16 18:08:05134 const std::set<std::string>& supported_card_networks =
135 spec_->supported_card_networks_set();
mathpf1a7a3752017-03-15 11:23:37136 for (autofill::CreditCard* card : cards) {
mathp363735b2017-03-16 18:08:05137 std::string basic_card_network =
138 autofill::data_util::GetPaymentRequestData(card->type())
139 .basic_card_payment_type;
140 if (!supported_card_networks.count(basic_card_network))
141 continue;
142
143 // TODO(crbug.com/701952): Should use the method name preferred by the
144 // merchant (either "basic-card" or the basic card network e.g. "visa").
145
146 // Copy the credit cards as part of AutofillPaymentInstrument so they are
147 // indirectly owned by this object.
148 std::unique_ptr<PaymentInstrument> instrument =
149 base::MakeUnique<AutofillPaymentInstrument>(
150 basic_card_network, *card, shipping_profiles_, app_locale_);
151 available_instruments_.push_back(std::move(instrument));
mathpf1a7a3752017-03-15 11:23:37152 }
153}
154
155void PaymentRequestState::SetDefaultProfileSelections() {
156 if (!shipping_profiles().empty())
157 selected_shipping_profile_ = shipping_profiles()[0];
158
159 if (!contact_profiles().empty())
160 selected_contact_profile_ = contact_profiles()[0];
161
mathp363735b2017-03-16 18:08:05162 // TODO(crbug.com/702063): Change this code to prioritize instruments by use
163 // count and other means, and implement a way to modify this function's return
164 // value.
165 const std::vector<std::unique_ptr<PaymentInstrument>>& instruments =
166 available_instruments();
167 auto first_complete_instrument =
168 std::find_if(instruments.begin(), instruments.end(),
169 [](const std::unique_ptr<PaymentInstrument>& instrument) {
170 return instrument->IsValid();
171 });
mathpf1a7a3752017-03-15 11:23:37172
mathp363735b2017-03-16 18:08:05173 selected_instrument_ = first_complete_instrument == instruments.end()
174 ? nullptr
175 : first_complete_instrument->get();
mathpf1a7a3752017-03-15 11:23:37176
177 UpdateIsReadyToPayAndNotifyObservers();
178}
179
180void PaymentRequestState::UpdateIsReadyToPayAndNotifyObservers() {
181 is_ready_to_pay_ =
182 ArePaymentDetailsSatisfied() && ArePaymentOptionsSatisfied();
183 NotifyOnSelectedInformationChanged();
184}
185
186void PaymentRequestState::NotifyOnSelectedInformationChanged() {
187 for (auto& observer : observers_)
188 observer.OnSelectedInformationChanged();
189}
190
191bool PaymentRequestState::ArePaymentDetailsSatisfied() {
mathp363735b2017-03-16 18:08:05192 // There is no need to check for supported networks, because only supported
193 // instruments are listed/created in the flow.
194 // TODO(crbug.com/702063): A masked card may not satisfy IsValid().
195 return selected_instrument_ != nullptr && selected_instrument_->IsValid();
mathpf1a7a3752017-03-15 11:23:37196}
197
198bool PaymentRequestState::ArePaymentOptionsSatisfied() {
199 // TODO(mathp): Have a measure of shipping address completeness.
200 if (spec_->request_shipping() && selected_shipping_profile_ == nullptr)
201 return false;
202
203 // TODO(mathp): Make an encompassing class to validate contact info.
mathpf1a7a3752017-03-15 11:23:37204 if (spec_->request_payer_name() &&
205 (selected_contact_profile_ == nullptr ||
206 selected_contact_profile_
mathpc0d616a2017-03-15 14:09:33207 ->GetInfo(autofill::AutofillType(autofill::NAME_FULL), app_locale_)
mathpf1a7a3752017-03-15 11:23:37208 .empty())) {
209 return false;
210 }
211 if (spec_->request_payer_email() &&
212 (selected_contact_profile_ == nullptr ||
213 selected_contact_profile_
214 ->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
mathpc0d616a2017-03-15 14:09:33215 app_locale_)
mathpf1a7a3752017-03-15 11:23:37216 .empty())) {
217 return false;
218 }
219 if (spec_->request_payer_phone() &&
220 (selected_contact_profile_ == nullptr ||
221 selected_contact_profile_
222 ->GetInfo(autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
mathpc0d616a2017-03-15 14:09:33223 app_locale_)
mathpf1a7a3752017-03-15 11:23:37224 .empty())) {
225 return false;
226 }
227
228 return true;
229}
230
231void PaymentRequestState::UpdateSelectedShippingOption() {
232 selected_shipping_option_ = nullptr;
233
234 // As per the spec, the selected shipping option should initially be the last
235 // one in the array that has its selected field set to true.
236 auto selected_shipping_option_it = std::find_if(
237 spec_->details().shipping_options.rbegin(),
238 spec_->details().shipping_options.rend(),
239 [](const payments::mojom::PaymentShippingOptionPtr& element) {
240 return element->selected;
241 });
242 if (selected_shipping_option_it != spec_->details().shipping_options.rend()) {
243 selected_shipping_option_ = selected_shipping_option_it->get();
244 }
245}
246
247} // namespace payments