Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(620)

Side by Side Diff: components/payments/content/payment_request.cc

Issue 2742813004: [Payments] Refactor into PaymentRequestState and Spec (Closed)
Patch Set: don't stop rebasin' Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/payments/content/payment_request.h" 5 #include "components/payments/content/payment_request.h"
6 6
7 #include <algorithm>
8 #include <set>
9 #include <unordered_map>
10 #include <utility> 7 #include <utility>
11 8
12 #include "base/memory/ptr_util.h" 9 #include "base/memory/ptr_util.h"
13 #include "components/autofill/core/browser/autofill_data_util.h"
14 #include "components/autofill/core/browser/field_types.h"
15 #include "components/autofill/core/browser/personal_data_manager.h" 10 #include "components/autofill/core/browser/personal_data_manager.h"
16 #include "components/payments/content/payment_details_validation.h" 11 #include "components/payments/content/payment_details_validation.h"
17 #include "components/payments/content/payment_request_web_contents_manager.h" 12 #include "components/payments/content/payment_request_web_contents_manager.h"
18 #include "components/payments/core/autofill_payment_instrument.h"
19 #include "components/payments/core/currency_formatter.h" 13 #include "components/payments/core/currency_formatter.h"
20 #include "content/public/browser/browser_thread.h" 14 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/web_contents.h" 15 #include "content/public/browser/web_contents.h"
22 16
23 namespace payments { 17 namespace payments {
24 18
25 namespace {
26 // Identifier for the basic card payment method in the PaymentMethodData.
27 static const char* const kBasicCardMethodName = "basic-card";
28 } // namespace
29
30 PaymentRequest::PaymentRequest( 19 PaymentRequest::PaymentRequest(
31 content::WebContents* web_contents, 20 content::WebContents* web_contents,
32 std::unique_ptr<PaymentRequestDelegate> delegate, 21 std::unique_ptr<PaymentRequestDelegate> delegate,
33 PaymentRequestWebContentsManager* manager, 22 PaymentRequestWebContentsManager* manager,
34 mojo::InterfaceRequest<payments::mojom::PaymentRequest> request) 23 mojo::InterfaceRequest<payments::mojom::PaymentRequest> request)
35 : web_contents_(web_contents), 24 : web_contents_(web_contents),
36 delegate_(std::move(delegate)), 25 delegate_(std::move(delegate)),
37 manager_(manager), 26 manager_(manager),
38 binding_(this, std::move(request)), 27 binding_(this, std::move(request)) {
39 is_ready_to_pay_(false),
40 selected_shipping_profile_(nullptr),
41 selected_contact_profile_(nullptr),
42 selected_credit_card_(nullptr),
43 selected_shipping_option_(nullptr) {
44 // OnConnectionTerminated will be called when the Mojo pipe is closed. This 28 // OnConnectionTerminated will be called when the Mojo pipe is closed. This
45 // will happen as a result of many renderer-side events (both successful and 29 // will happen as a result of many renderer-side events (both successful and
46 // erroneous in nature). 30 // erroneous in nature).
47 // TODO(crbug.com/683636): Investigate using 31 // TODO(crbug.com/683636): Investigate using
48 // set_connection_error_with_reason_handler with Binding::CloseWithReason. 32 // set_connection_error_with_reason_handler with Binding::CloseWithReason.
49 binding_.set_connection_error_handler(base::Bind( 33 binding_.set_connection_error_handler(base::Bind(
50 &PaymentRequest::OnConnectionTerminated, base::Unretained(this))); 34 &PaymentRequest::OnConnectionTerminated, base::Unretained(this)));
51 } 35 }
52 36
53 PaymentRequest::~PaymentRequest() {} 37 PaymentRequest::~PaymentRequest() {}
54 38
55 void PaymentRequest::Init( 39 void PaymentRequest::Init(
56 payments::mojom::PaymentRequestClientPtr client, 40 payments::mojom::PaymentRequestClientPtr client,
57 std::vector<payments::mojom::PaymentMethodDataPtr> method_data, 41 std::vector<payments::mojom::PaymentMethodDataPtr> method_data,
58 payments::mojom::PaymentDetailsPtr details, 42 payments::mojom::PaymentDetailsPtr details,
59 payments::mojom::PaymentOptionsPtr options) { 43 payments::mojom::PaymentOptionsPtr options) {
60 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 44 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
61 std::string error; 45 std::string error;
62 if (!payments::validatePaymentDetails(details, &error)) { 46 if (!payments::validatePaymentDetails(details, &error)) {
63 LOG(ERROR) << error; 47 LOG(ERROR) << error;
64 OnConnectionTerminated(); 48 OnConnectionTerminated();
65 return; 49 return;
66 } 50 }
67 client_ = std::move(client); 51 client_ = std::move(client);
68 details_ = std::move(details); 52 spec_ = base::MakeUnique<PaymentRequestSpec>(
69 options_ = std::move(options); 53 std::move(options), std::move(details), std::move(method_data), this);
70 PopulateValidatedMethodData(method_data); 54 state_ = base::MakeUnique<PaymentRequestState>(spec_.get(), this);
71 PopulateProfileCache();
72 SetDefaultProfileSelections();
73 UpdateSelectedShippingOptionFromDetails();
74 } 55 }
75 56
76 void PaymentRequest::Show() { 57 void PaymentRequest::Show() {
77 if (!client_.is_bound() || !binding_.is_bound()) { 58 if (!client_.is_bound() || !binding_.is_bound()) {
78 LOG(ERROR) << "Attempted Show(), but binding(s) missing."; 59 LOG(ERROR) << "Attempted Show(), but binding(s) missing.";
79 OnConnectionTerminated(); 60 OnConnectionTerminated();
80 return; 61 return;
81 } 62 }
82 delegate_->ShowDialog(this); 63 delegate_->ShowDialog(this);
83 } 64 }
(...skipping 15 matching lines...) Expand all
99 // When the renderer closes the connection, 80 // When the renderer closes the connection,
100 // PaymentRequest::OnConnectionTerminated will be called. 81 // PaymentRequest::OnConnectionTerminated will be called.
101 client_->OnComplete(); 82 client_->OnComplete();
102 } 83 }
103 84
104 void PaymentRequest::CanMakePayment() { 85 void PaymentRequest::CanMakePayment() {
105 // TODO(mathp): Return whether we can make payment. 86 // TODO(mathp): Return whether we can make payment.
106 client_->OnCanMakePayment(mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT); 87 client_->OnCanMakePayment(mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT);
107 } 88 }
108 89
109 void PaymentRequest::OnInstrumentDetailsReady( 90 void PaymentRequest::OnInvalidSpecProvided() {
110 const std::string& method_name, 91 OnConnectionTerminated();
111 const std::string& stringified_details) { 92 }
112 payment_response_->method_name = method_name; 93
113 payment_response_->stringified_details = stringified_details; 94 const std::string& PaymentRequest::GetApplicationLocale() {
114 client_->OnPaymentResponse(std::move(payment_response_)); 95 return delegate_->GetApplicationLocale();
96 }
97
98 autofill::PersonalDataManager* PaymentRequest::GetPersonalDataManager() {
99 return delegate_->GetPersonalDataManager();
100 }
101
102 void PaymentRequest::OnPaymentResponseAvailable(
103 mojom::PaymentResponsePtr response) {
104 client_->OnPaymentResponse(std::move(response));
115 } 105 }
116 106
117 void PaymentRequest::UserCancelled() { 107 void PaymentRequest::UserCancelled() {
118 // If |client_| is not bound, then the object is already being destroyed as 108 // If |client_| is not bound, then the object is already being destroyed as
119 // a result of a renderer event. 109 // a result of a renderer event.
120 if (!client_.is_bound()) 110 if (!client_.is_bound())
121 return; 111 return;
122 112
123 // This sends an error to the renderer, which informs the API user. 113 // This sends an error to the renderer, which informs the API user.
124 client_->OnError(payments::mojom::PaymentErrorReason::USER_CANCEL); 114 client_->OnError(payments::mojom::PaymentErrorReason::USER_CANCEL);
(...skipping 10 matching lines...) Expand all
135 // has decided to close the pipe for various reasons (see all uses of 125 // has decided to close the pipe for various reasons (see all uses of
136 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close 126 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close
137 // the binding and the dialog, and ask to be deleted. 127 // the binding and the dialog, and ask to be deleted.
138 client_.reset(); 128 client_.reset();
139 binding_.Close(); 129 binding_.Close();
140 delegate_->CloseDialog(); 130 delegate_->CloseDialog();
141 manager_->DestroyRequest(this); 131 manager_->DestroyRequest(this);
142 } 132 }
143 133
144 void PaymentRequest::Pay() { 134 void PaymentRequest::Pay() {
145 DCHECK(is_ready_to_pay_); 135 DCHECK(state_->is_ready_to_pay());
146 136
147 // TODO(mathp): Fill other fields in the PaymentResponsePtr object. 137 state_->GeneratePaymentResponse();
148 payment_response_ = mojom::PaymentResponse::New();
149
150 // TODO(mathp): PaymentRequest should know about the currently selected
151 // instrument, and not |selected_credit_card_| which is too specific.
152 // TODO(mathp): The method_name should reflect what the merchant asked, and
153 // not necessarily basic-card.
154 selected_payment_instrument_.reset(new AutofillPaymentInstrument(
155 kBasicCardMethodName, *selected_credit_card_, shipping_profiles_,
156 delegate_->GetApplicationLocale()));
157 // Fetch the instrument details, will call back into
158 // PaymentRequest::OnInstrumentsDetailsReady.
159 selected_payment_instrument_->InvokePaymentApp(this);
160 }
161
162 void PaymentRequest::AddObserver(Observer* observer) {
163 CHECK(observer);
164 observers_.AddObserver(observer);
165 }
166
167 void PaymentRequest::RemoveObserver(Observer* observer) {
168 observers_.RemoveObserver(observer);
169 } 138 }
170 139
171 CurrencyFormatter* PaymentRequest::GetOrCreateCurrencyFormatter( 140 CurrencyFormatter* PaymentRequest::GetOrCreateCurrencyFormatter(
172 const std::string& currency_code, 141 const std::string& currency_code,
173 const std::string& currency_system, 142 const std::string& currency_system,
174 const std::string& locale_name) { 143 const std::string& locale_name) {
175 if (!currency_formatter_) { 144 if (!currency_formatter_) {
176 currency_formatter_.reset( 145 currency_formatter_.reset(
177 new CurrencyFormatter(currency_code, currency_system, locale_name)); 146 new CurrencyFormatter(currency_code, currency_system, locale_name));
178 } 147 }
179 return currency_formatter_.get(); 148 return currency_formatter_.get();
180 } 149 }
181 150
182 base::string16 PaymentRequest::GetFormattedCurrencyAmount( 151 base::string16 PaymentRequest::GetFormattedCurrencyAmount(
183 const std::string& amount) { 152 const std::string& amount) {
184 CurrencyFormatter* formatter = 153 CurrencyFormatter* formatter = GetOrCreateCurrencyFormatter(
185 GetOrCreateCurrencyFormatter(details()->total->amount->currency, 154 spec_->details().total->amount->currency,
186 details()->total->amount->currency_system, 155 spec_->details().total->amount->currency_system, GetApplicationLocale());
187 delegate_->GetApplicationLocale());
188 return formatter->Format(amount); 156 return formatter->Format(amount);
189 } 157 }
190 158
191 std::string PaymentRequest::GetFormattedCurrencyCode() { 159 std::string PaymentRequest::GetFormattedCurrencyCode() {
192 CurrencyFormatter* formatter = 160 CurrencyFormatter* formatter = GetOrCreateCurrencyFormatter(
193 GetOrCreateCurrencyFormatter(details()->total->amount->currency, 161 spec_->details().total->amount->currency,
194 details()->total->amount->currency_system, 162 spec_->details().total->amount->currency_system, GetApplicationLocale());
195 delegate_->GetApplicationLocale());
196 163
197 return formatter->formatted_currency_code(); 164 return formatter->formatted_currency_code();
198 } 165 }
199 166
200 void PaymentRequest::SetSelectedShippingProfile(
201 autofill::AutofillProfile* profile) {
202 selected_shipping_profile_ = profile;
203 UpdateIsReadyToPayAndNotifyObservers();
204 }
205
206 void PaymentRequest::SetSelectedContactProfile(
207 autofill::AutofillProfile* profile) {
208 selected_contact_profile_ = profile;
209 UpdateIsReadyToPayAndNotifyObservers();
210 }
211
212 void PaymentRequest::SetSelectedCreditCard(autofill::CreditCard* card) {
213 selected_credit_card_ = card;
214 UpdateIsReadyToPayAndNotifyObservers();
215 }
216
217 void PaymentRequest::PopulateProfileCache() {
218 std::vector<autofill::AutofillProfile*> profiles =
219 personal_data_manager()->GetProfilesToSuggest();
220
221 // PaymentRequest may outlive the Profiles returned by the Data Manager.
222 // Thus, we store copies, and return a vector of pointers to these copies
223 // whenever Profiles are requested. The same is true for credit cards.
224 for (size_t i = 0; i < profiles.size(); i++) {
225 profile_cache_.push_back(
226 base::MakeUnique<autofill::AutofillProfile>(*profiles[i]));
227
228 // TODO(tmartino): Implement deduplication rules specific to shipping and
229 // contact profiles.
230 shipping_profiles_.push_back(profile_cache_[i].get());
231 contact_profiles_.push_back(profile_cache_[i].get());
232 }
233
234 const std::vector<autofill::CreditCard*>& cards =
235 personal_data_manager()->GetCreditCardsToSuggest();
236 for (autofill::CreditCard* card : cards) {
237 card_cache_.push_back(base::MakeUnique<autofill::CreditCard>(*card));
238 credit_cards_.push_back(card_cache_.back().get());
239 }
240 }
241
242 void PaymentRequest::SetDefaultProfileSelections() {
243 if (!shipping_profiles().empty())
244 selected_shipping_profile_ = shipping_profiles()[0];
245
246 if (!contact_profiles().empty())
247 selected_contact_profile_ = contact_profiles()[0];
248
249 // TODO(anthonyvd): Change this code to prioritize server cards and implement
250 // a way to modify this function's return value.
251 const std::vector<autofill::CreditCard*> cards = credit_cards();
252 auto first_complete_card =
253 std::find_if(cards.begin(), cards.end(),
254 [](autofill::CreditCard* card) { return card->IsValid(); });
255
256 selected_credit_card_ =
257 first_complete_card == cards.end() ? nullptr : *first_complete_card;
258
259 UpdateIsReadyToPayAndNotifyObservers();
260 }
261
262 void PaymentRequest::PopulateValidatedMethodData(
263 const std::vector<payments::mojom::PaymentMethodDataPtr>& method_data) {
264 if (method_data.empty()) {
265 LOG(ERROR) << "Invalid payment methods or data";
266 OnConnectionTerminated();
267 return;
268 }
269
270 std::set<std::string> card_networks{"amex", "diners", "discover",
271 "jcb", "mastercard", "mir",
272 "unionpay", "visa"};
273 for (const payments::mojom::PaymentMethodDataPtr& method_data_entry :
274 method_data) {
275 std::vector<std::string> supported_methods =
276 method_data_entry->supported_methods;
277 if (supported_methods.empty()) {
278 LOG(ERROR) << "Invalid payment methods or data";
279 OnConnectionTerminated();
280 return;
281 }
282
283 for (const std::string& method : supported_methods) {
284 if (method.empty())
285 continue;
286
287 // If a card network is specified right in "supportedMethods", add it.
288 auto card_it = card_networks.find(method);
289 if (card_it != card_networks.end()) {
290 supported_card_networks_.push_back(method);
291 // |method| removed from |card_networks| so that it is not doubly added
292 // to |supported_card_networks_| if "basic-card" is specified with no
293 // supported networks.
294 card_networks.erase(card_it);
295 } else if (method == kBasicCardMethodName) {
296 // For the "basic-card" method, check "supportedNetworks".
297 if (method_data_entry->supported_networks.empty()) {
298 // Empty |supported_networks| means all networks are supported.
299 supported_card_networks_.insert(supported_card_networks_.end(),
300 card_networks.begin(),
301 card_networks.end());
302 // Clear the set so that no further networks are added to
303 // |supported_card_networks_|.
304 card_networks.clear();
305 } else {
306 // The merchant has specified a few basic card supported networks. Use
307 // the mapping to transform to known basic-card types.
308 using ::payments::mojom::BasicCardNetwork;
309 std::unordered_map<BasicCardNetwork, std::string> networks = {
310 {BasicCardNetwork::AMEX, "amex"},
311 {BasicCardNetwork::DINERS, "diners"},
312 {BasicCardNetwork::DISCOVER, "discover"},
313 {BasicCardNetwork::JCB, "jcb"},
314 {BasicCardNetwork::MASTERCARD, "mastercard"},
315 {BasicCardNetwork::MIR, "mir"},
316 {BasicCardNetwork::UNIONPAY, "unionpay"},
317 {BasicCardNetwork::VISA, "visa"}};
318 for (const BasicCardNetwork& supported_network :
319 method_data_entry->supported_networks) {
320 // Make sure that the network was not already added to
321 // |supported_card_networks_|.
322 auto card_it = card_networks.find(networks[supported_network]);
323 if (card_it != card_networks.end()) {
324 supported_card_networks_.push_back(networks[supported_network]);
325 card_networks.erase(card_it);
326 }
327 }
328 }
329 }
330 }
331 }
332 }
333
334 void PaymentRequest::UpdateIsReadyToPayAndNotifyObservers() {
335 is_ready_to_pay_ =
336 ArePaymentDetailsSatisfied() && ArePaymentOptionsSatisfied();
337 NotifyOnSelectedInformationChanged();
338 }
339
340 void PaymentRequest::NotifyOnSelectedInformationChanged() {
341 for (auto& observer : observers_)
342 observer.OnSelectedInformationChanged();
343 }
344
345 bool PaymentRequest::ArePaymentDetailsSatisfied() {
346 // TODO(mathp): A masked card may not satisfy IsValid().
347 if (selected_credit_card_ == nullptr || !selected_credit_card_->IsValid())
348 return false;
349
350 const std::string basic_card_payment_type =
351 autofill::data_util::GetPaymentRequestData(selected_credit_card_->type())
352 .basic_card_payment_type;
353 return !supported_card_networks_.empty() &&
354 std::find(supported_card_networks_.begin(),
355 supported_card_networks_.end(),
356 basic_card_payment_type) != supported_card_networks_.end();
357 }
358
359 bool PaymentRequest::ArePaymentOptionsSatisfied() {
360 // TODO(mathp): Have a measure of shipping address completeness.
361 if (request_shipping() && selected_shipping_profile_ == nullptr)
362 return false;
363
364 // TODO(mathp): Make an encompassing class to validate contact info.
365 const std::string& app_locale = delegate_->GetApplicationLocale();
366 if (request_payer_name() &&
367 (selected_contact_profile_ == nullptr ||
368 selected_contact_profile_
369 ->GetInfo(autofill::AutofillType(autofill::NAME_FULL), app_locale)
370 .empty())) {
371 return false;
372 }
373 if (request_payer_email() &&
374 (selected_contact_profile_ == nullptr ||
375 selected_contact_profile_
376 ->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
377 app_locale)
378 .empty())) {
379 return false;
380 }
381 if (request_payer_phone() &&
382 (selected_contact_profile_ == nullptr ||
383 selected_contact_profile_
384 ->GetInfo(autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
385 app_locale)
386 .empty())) {
387 return false;
388 }
389
390 return true;
391 }
392
393 void PaymentRequest::UpdateSelectedShippingOptionFromDetails() {
394 selected_shipping_option_ = nullptr;
395
396 // As per the spec, the selected shipping option should initially be the last
397 // one in the array that has its selected field set to true.
398 auto selected_shipping_option_it = std::find_if(
399 details()->shipping_options.rbegin(), details()->shipping_options.rend(),
400 [](const payments::mojom::PaymentShippingOptionPtr& element) {
401 return element->selected;
402 });
403 if (selected_shipping_option_it != details()->shipping_options.rend()) {
404 selected_shipping_option_ = selected_shipping_option_it->get();
405 }
406 }
407
408 } // namespace payments 167 } // namespace payments
OLDNEW
« no previous file with comments | « components/payments/content/payment_request.h ('k') | components/payments/content/payment_request_spec.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698