blob: 785517d5db21a1e8cb380394b80a636fdbebfe5f [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
sebsgad86bd002017-03-29 16:39:129#include "base/strings/utf_string_conversions.h"
mathpf1a7a3752017-03-15 11:23:3710#include "components/autofill/core/browser/autofill_data_util.h"
11#include "components/autofill/core/browser/autofill_profile.h"
12#include "components/autofill/core/browser/credit_card.h"
mathpc0d616a2017-03-15 14:09:3313#include "components/autofill/core/browser/personal_data_manager.h"
mathpf1a7a3752017-03-15 11:23:3714#include "components/payments/content/payment_request_spec.h"
sebsgad86bd002017-03-29 16:39:1215#include "components/payments/content/payment_response_helper.h"
mathpf1a7a3752017-03-15 11:23:3716#include "components/payments/core/autofill_payment_instrument.h"
17
18namespace payments {
19
mathpc0d616a2017-03-15 14:09:3320PaymentRequestState::PaymentRequestState(
21 PaymentRequestSpec* spec,
22 Delegate* delegate,
23 const std::string& app_locale,
24 autofill::PersonalDataManager* personal_data_manager)
mathpf1a7a3752017-03-15 11:23:3725 : is_ready_to_pay_(false),
mathpc0d616a2017-03-15 14:09:3326 app_locale_(app_locale),
mathpf1a7a3752017-03-15 11:23:3727 spec_(spec),
28 delegate_(delegate),
mathpc0d616a2017-03-15 14:09:3329 personal_data_manager_(personal_data_manager),
mathpf1a7a3752017-03-15 11:23:3730 selected_shipping_profile_(nullptr),
31 selected_contact_profile_(nullptr),
mathp363735b2017-03-16 18:08:0532 selected_instrument_(nullptr),
mathpf1a7a3752017-03-15 11:23:3733 selected_shipping_option_(nullptr) {
34 PopulateProfileCache();
35 UpdateSelectedShippingOption();
36 SetDefaultProfileSelections();
37}
38
mathp1a5be4f2017-03-24 18:09:1939bool PaymentRequestState::CanMakePayment() const {
mathp1a5be4f2017-03-24 18:09:1940 for (const std::unique_ptr<PaymentInstrument>& instrument :
41 available_instruments_) {
42 if (instrument.get()->IsValid() &&
43 spec_->supported_card_networks_set().count(
44 instrument.get()->method_name())) {
45 return true;
46 }
47 }
48 return false;
49}
50
mathpf1a7a3752017-03-15 11:23:3751void PaymentRequestState::AddObserver(Observer* observer) {
52 CHECK(observer);
53 observers_.AddObserver(observer);
54}
55PaymentRequestState::~PaymentRequestState() {}
56
57void PaymentRequestState::RemoveObserver(Observer* observer) {
58 observers_.RemoveObserver(observer);
59}
60
sebsgad86bd002017-03-29 16:39:1261// TODO(sebsg): Move this to the PaymentResponseHelper.
mathpf1a7a3752017-03-15 11:23:3762void PaymentRequestState::OnInstrumentDetailsReady(
63 const std::string& method_name,
64 const std::string& stringified_details) {
mathpf1a7a3752017-03-15 11:23:3765 mojom::PaymentResponsePtr payment_response = mojom::PaymentResponse::New();
66
mathp600bab52017-03-26 03:47:5967 // Make sure that we return the method name that the merchant specified for
68 // this instrument: cards can be either specified through their name (e.g.,
69 // "visa") or through basic-card's supportedNetworks.
70 payment_response->method_name =
71 spec_->IsMethodSupportedThroughBasicCard(method_name)
72 ? kBasicCardMethodName
73 : method_name;
mathpf1a7a3752017-03-15 11:23:3774 payment_response->stringified_details = stringified_details;
sebsgad86bd002017-03-29 16:39:1275
76 // Shipping Address section
77 if (spec_->request_shipping()) {
78 DCHECK(selected_shipping_profile_);
79 payment_response->shipping_address =
80 PaymentResponseHelper::GetMojomPaymentAddressFromAutofillProfile(
81 selected_shipping_profile_, app_locale_);
82
83 DCHECK(selected_shipping_option_);
84 payment_response->shipping_option = selected_shipping_option_->id;
85 }
86
87 // Contact Details section.
88 if (spec_->request_payer_name()) {
89 DCHECK(selected_contact_profile_);
90 payment_response->payer_name =
91 base::UTF16ToUTF8(selected_contact_profile_->GetInfo(
92 autofill::AutofillType(autofill::NAME_FULL), app_locale_));
93 }
94 if (spec_->request_payer_email()) {
95 DCHECK(selected_contact_profile_);
96 payment_response->payer_email = base::UTF16ToUTF8(
97 selected_contact_profile_->GetRawInfo(autofill::EMAIL_ADDRESS));
98 }
99 if (spec_->request_payer_phone()) {
100 DCHECK(selected_contact_profile_);
101 // TODO(crbug.com/705945): Format phone number according to spec.
102 payment_response->payer_phone =
103 base::UTF16ToUTF8(selected_contact_profile_->GetRawInfo(
104 autofill::PHONE_HOME_WHOLE_NUMBER));
105 }
106
mathpf1a7a3752017-03-15 11:23:37107 delegate_->OnPaymentResponseAvailable(std::move(payment_response));
108}
109
110void PaymentRequestState::GeneratePaymentResponse() {
mathp363735b2017-03-16 18:08:05111 DCHECK(is_ready_to_pay());
mathpf1a7a3752017-03-15 11:23:37112 // Fetch the instrument details, will call back into
sebsgad86bd002017-03-29 16:39:12113 // PaymentRequest::OnInstrumentDetailsReady.
mathp363735b2017-03-16 18:08:05114 selected_instrument_->InvokePaymentApp(this);
mathpf1a7a3752017-03-15 11:23:37115}
116
anthonyvd0116ce332017-03-21 21:29:01117void PaymentRequestState::SetSelectedShippingOption(
118 mojom::PaymentShippingOption* option) {
119 selected_shipping_option_ = option;
120 UpdateIsReadyToPayAndNotifyObservers();
121}
122
mathpf1a7a3752017-03-15 11:23:37123void PaymentRequestState::SetSelectedShippingProfile(
124 autofill::AutofillProfile* profile) {
125 selected_shipping_profile_ = profile;
126 UpdateIsReadyToPayAndNotifyObservers();
127}
128
129void PaymentRequestState::SetSelectedContactProfile(
130 autofill::AutofillProfile* profile) {
131 selected_contact_profile_ = profile;
132 UpdateIsReadyToPayAndNotifyObservers();
133}
134
mathp363735b2017-03-16 18:08:05135void PaymentRequestState::SetSelectedInstrument(PaymentInstrument* instrument) {
136 selected_instrument_ = instrument;
mathpf1a7a3752017-03-15 11:23:37137 UpdateIsReadyToPayAndNotifyObservers();
138}
139
mathpc0d616a2017-03-15 14:09:33140const std::string& PaymentRequestState::GetApplicationLocale() {
141 return app_locale_;
142}
143
144autofill::PersonalDataManager* PaymentRequestState::GetPersonalDataManager() {
145 return personal_data_manager_;
146}
147
mathpf1a7a3752017-03-15 11:23:37148void PaymentRequestState::PopulateProfileCache() {
mathpf1a7a3752017-03-15 11:23:37149 std::vector<autofill::AutofillProfile*> profiles =
mathpc0d616a2017-03-15 14:09:33150 personal_data_manager_->GetProfilesToSuggest();
mathpf1a7a3752017-03-15 11:23:37151
152 // PaymentRequest may outlive the Profiles returned by the Data Manager.
153 // Thus, we store copies, and return a vector of pointers to these copies
154 // whenever Profiles are requested. The same is true for credit cards.
155 for (size_t i = 0; i < profiles.size(); i++) {
156 profile_cache_.push_back(
157 base::MakeUnique<autofill::AutofillProfile>(*profiles[i]));
158
159 // TODO(tmartino): Implement deduplication rules specific to shipping and
160 // contact profiles.
161 shipping_profiles_.push_back(profile_cache_[i].get());
162 contact_profiles_.push_back(profile_cache_[i].get());
163 }
164
mathp363735b2017-03-16 18:08:05165 // Create the list of available instruments.
mathpf1a7a3752017-03-15 11:23:37166 const std::vector<autofill::CreditCard*>& cards =
mathpc0d616a2017-03-15 14:09:33167 personal_data_manager_->GetCreditCardsToSuggest();
mathp363735b2017-03-16 18:08:05168 const std::set<std::string>& supported_card_networks =
169 spec_->supported_card_networks_set();
mathpf1a7a3752017-03-15 11:23:37170 for (autofill::CreditCard* card : cards) {
mathp363735b2017-03-16 18:08:05171 std::string basic_card_network =
172 autofill::data_util::GetPaymentRequestData(card->type())
173 .basic_card_payment_type;
174 if (!supported_card_networks.count(basic_card_network))
175 continue;
176
177 // TODO(crbug.com/701952): Should use the method name preferred by the
178 // merchant (either "basic-card" or the basic card network e.g. "visa").
179
180 // Copy the credit cards as part of AutofillPaymentInstrument so they are
181 // indirectly owned by this object.
182 std::unique_ptr<PaymentInstrument> instrument =
183 base::MakeUnique<AutofillPaymentInstrument>(
184 basic_card_network, *card, shipping_profiles_, app_locale_);
185 available_instruments_.push_back(std::move(instrument));
mathpf1a7a3752017-03-15 11:23:37186 }
187}
188
189void PaymentRequestState::SetDefaultProfileSelections() {
190 if (!shipping_profiles().empty())
191 selected_shipping_profile_ = shipping_profiles()[0];
192
193 if (!contact_profiles().empty())
194 selected_contact_profile_ = contact_profiles()[0];
195
mathp363735b2017-03-16 18:08:05196 // TODO(crbug.com/702063): Change this code to prioritize instruments by use
197 // count and other means, and implement a way to modify this function's return
198 // value.
199 const std::vector<std::unique_ptr<PaymentInstrument>>& instruments =
200 available_instruments();
201 auto first_complete_instrument =
202 std::find_if(instruments.begin(), instruments.end(),
203 [](const std::unique_ptr<PaymentInstrument>& instrument) {
204 return instrument->IsValid();
205 });
mathpf1a7a3752017-03-15 11:23:37206
mathp363735b2017-03-16 18:08:05207 selected_instrument_ = first_complete_instrument == instruments.end()
208 ? nullptr
209 : first_complete_instrument->get();
mathpf1a7a3752017-03-15 11:23:37210
211 UpdateIsReadyToPayAndNotifyObservers();
212}
213
214void PaymentRequestState::UpdateIsReadyToPayAndNotifyObservers() {
215 is_ready_to_pay_ =
216 ArePaymentDetailsSatisfied() && ArePaymentOptionsSatisfied();
217 NotifyOnSelectedInformationChanged();
218}
219
220void PaymentRequestState::NotifyOnSelectedInformationChanged() {
221 for (auto& observer : observers_)
222 observer.OnSelectedInformationChanged();
223}
224
225bool PaymentRequestState::ArePaymentDetailsSatisfied() {
mathp363735b2017-03-16 18:08:05226 // There is no need to check for supported networks, because only supported
227 // instruments are listed/created in the flow.
228 // TODO(crbug.com/702063): A masked card may not satisfy IsValid().
229 return selected_instrument_ != nullptr && selected_instrument_->IsValid();
mathpf1a7a3752017-03-15 11:23:37230}
231
232bool PaymentRequestState::ArePaymentOptionsSatisfied() {
233 // TODO(mathp): Have a measure of shipping address completeness.
234 if (spec_->request_shipping() && selected_shipping_profile_ == nullptr)
235 return false;
236
237 // TODO(mathp): Make an encompassing class to validate contact info.
mathpf1a7a3752017-03-15 11:23:37238 if (spec_->request_payer_name() &&
239 (selected_contact_profile_ == nullptr ||
240 selected_contact_profile_
mathpc0d616a2017-03-15 14:09:33241 ->GetInfo(autofill::AutofillType(autofill::NAME_FULL), app_locale_)
mathpf1a7a3752017-03-15 11:23:37242 .empty())) {
243 return false;
244 }
245 if (spec_->request_payer_email() &&
246 (selected_contact_profile_ == nullptr ||
247 selected_contact_profile_
248 ->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
mathpc0d616a2017-03-15 14:09:33249 app_locale_)
mathpf1a7a3752017-03-15 11:23:37250 .empty())) {
251 return false;
252 }
253 if (spec_->request_payer_phone() &&
254 (selected_contact_profile_ == nullptr ||
255 selected_contact_profile_
256 ->GetInfo(autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
mathpc0d616a2017-03-15 14:09:33257 app_locale_)
mathpf1a7a3752017-03-15 11:23:37258 .empty())) {
259 return false;
260 }
261
262 return true;
263}
264
265void PaymentRequestState::UpdateSelectedShippingOption() {
266 selected_shipping_option_ = nullptr;
267
268 // As per the spec, the selected shipping option should initially be the last
269 // one in the array that has its selected field set to true.
270 auto selected_shipping_option_it = std::find_if(
271 spec_->details().shipping_options.rbegin(),
272 spec_->details().shipping_options.rend(),
273 [](const payments::mojom::PaymentShippingOptionPtr& element) {
274 return element->selected;
275 });
276 if (selected_shipping_option_it != spec_->details().shipping_options.rend()) {
277 selected_shipping_option_ = selected_shipping_option_it->get();
278 }
279}
280
281} // namespace payments