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

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

Issue 2742813004: [Payments] Refactor into PaymentRequestState and Spec (Closed)
Patch Set: deps fix 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> 7 #include <algorithm>
8 #include <set> 8 #include <set>
9 #include <unordered_map> 9 #include <unordered_map>
10 #include <utility> 10 #include <utility>
11 11
12 #include "base/memory/ptr_util.h" 12 #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" 13 #include "components/autofill/core/browser/field_types.h"
15 #include "components/autofill/core/browser/personal_data_manager.h" 14 #include "components/autofill/core/browser/personal_data_manager.h"
16 #include "components/payments/content/payment_details_validation.h" 15 #include "components/payments/content/payment_details_validation.h"
17 #include "components/payments/content/payment_request_web_contents_manager.h" 16 #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" 17 #include "components/payments/core/currency_formatter.h"
20 #include "content/public/browser/browser_thread.h" 18 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/web_contents.h" 19 #include "content/public/browser/web_contents.h"
22 20
23 namespace payments { 21 namespace payments {
24 22
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( 23 PaymentRequest::PaymentRequest(
31 content::WebContents* web_contents, 24 content::WebContents* web_contents,
32 std::unique_ptr<PaymentRequestDelegate> delegate, 25 std::unique_ptr<PaymentRequestDelegate> delegate,
33 PaymentRequestWebContentsManager* manager, 26 PaymentRequestWebContentsManager* manager,
34 mojo::InterfaceRequest<payments::mojom::PaymentRequest> request) 27 mojo::InterfaceRequest<payments::mojom::PaymentRequest> request)
35 : web_contents_(web_contents), 28 : web_contents_(web_contents),
36 delegate_(std::move(delegate)), 29 delegate_(std::move(delegate)),
37 manager_(manager), 30 manager_(manager),
38 binding_(this, std::move(request)), 31 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 32 // 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 33 // will happen as a result of many renderer-side events (both successful and
46 // erroneous in nature). 34 // erroneous in nature).
47 // TODO(crbug.com/683636): Investigate using 35 // TODO(crbug.com/683636): Investigate using
48 // set_connection_error_with_reason_handler with Binding::CloseWithReason. 36 // set_connection_error_with_reason_handler with Binding::CloseWithReason.
49 binding_.set_connection_error_handler(base::Bind( 37 binding_.set_connection_error_handler(base::Bind(
50 &PaymentRequest::OnConnectionTerminated, base::Unretained(this))); 38 &PaymentRequest::OnConnectionTerminated, base::Unretained(this)));
51 } 39 }
52 40
53 PaymentRequest::~PaymentRequest() {} 41 PaymentRequest::~PaymentRequest() {}
54 42
55 void PaymentRequest::Init( 43 void PaymentRequest::Init(
56 payments::mojom::PaymentRequestClientPtr client, 44 payments::mojom::PaymentRequestClientPtr client,
57 std::vector<payments::mojom::PaymentMethodDataPtr> method_data, 45 std::vector<payments::mojom::PaymentMethodDataPtr> method_data,
58 payments::mojom::PaymentDetailsPtr details, 46 payments::mojom::PaymentDetailsPtr details,
59 payments::mojom::PaymentOptionsPtr options) { 47 payments::mojom::PaymentOptionsPtr options) {
60 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 48 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
61 std::string error; 49 std::string error;
62 if (!payments::validatePaymentDetails(details, &error)) { 50 if (!payments::validatePaymentDetails(details, &error)) {
63 LOG(ERROR) << error; 51 LOG(ERROR) << error;
64 OnConnectionTerminated(); 52 OnConnectionTerminated();
65 return; 53 return;
66 } 54 }
67 client_ = std::move(client); 55 client_ = std::move(client);
68 details_ = std::move(details);
69 options_ = std::move(options);
70 PopulateValidatedMethodData(method_data);
71 PopulateProfileCache(); 56 PopulateProfileCache();
57 spec_ = base::MakeUnique<PaymentRequestSpec>(
58 std::move(options), std::move(details), method_data, this);
59 state_ = base::MakeUnique<PaymentRequestState>(spec_.get(), this);
72 SetDefaultProfileSelections(); 60 SetDefaultProfileSelections();
73 UpdateSelectedShippingOptionFromDetails();
74 } 61 }
75 62
76 void PaymentRequest::Show() { 63 void PaymentRequest::Show() {
77 if (!client_.is_bound() || !binding_.is_bound()) { 64 if (!client_.is_bound() || !binding_.is_bound()) {
78 LOG(ERROR) << "Attempted Show(), but binding(s) missing."; 65 LOG(ERROR) << "Attempted Show(), but binding(s) missing.";
79 OnConnectionTerminated(); 66 OnConnectionTerminated();
80 return; 67 return;
81 } 68 }
82 delegate_->ShowDialog(this); 69 delegate_->ShowDialog(this);
83 } 70 }
(...skipping 15 matching lines...) Expand all
99 // When the renderer closes the connection, 86 // When the renderer closes the connection,
100 // PaymentRequest::OnConnectionTerminated will be called. 87 // PaymentRequest::OnConnectionTerminated will be called.
101 client_->OnComplete(); 88 client_->OnComplete();
102 } 89 }
103 90
104 void PaymentRequest::CanMakePayment() { 91 void PaymentRequest::CanMakePayment() {
105 // TODO(mathp): Return whether we can make payment. 92 // TODO(mathp): Return whether we can make payment.
106 client_->OnCanMakePayment(mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT); 93 client_->OnCanMakePayment(mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT);
107 } 94 }
108 95
109 void PaymentRequest::OnInstrumentDetailsReady( 96 void PaymentRequest::OnInvalidSpecProvided() {
110 const std::string& method_name, 97 OnConnectionTerminated();
111 const std::string& stringified_details) { 98 }
112 payment_response_->method_name = method_name; 99
113 payment_response_->stringified_details = stringified_details; 100 const std::string& PaymentRequest::GetApplicationLocale() {
114 client_->OnPaymentResponse(std::move(payment_response_)); 101 return delegate_->GetApplicationLocale();
102 }
103
104 const std::vector<autofill::AutofillProfile*>&
105 PaymentRequest::GetBillingProfiles() {
106 return shipping_profiles_;
107 }
108
109 void PaymentRequest::OnPaymentResponseAvailable(
110 mojom::PaymentResponsePtr response) {
111 client_->OnPaymentResponse(std::move(response));
115 } 112 }
116 113
117 void PaymentRequest::UserCancelled() { 114 void PaymentRequest::UserCancelled() {
118 // If |client_| is not bound, then the object is already being destroyed as 115 // If |client_| is not bound, then the object is already being destroyed as
119 // a result of a renderer event. 116 // a result of a renderer event.
120 if (!client_.is_bound()) 117 if (!client_.is_bound())
121 return; 118 return;
122 119
123 // This sends an error to the renderer, which informs the API user. 120 // This sends an error to the renderer, which informs the API user.
124 client_->OnError(payments::mojom::PaymentErrorReason::USER_CANCEL); 121 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 132 // has decided to close the pipe for various reasons (see all uses of
136 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close 133 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close
137 // the binding and the dialog, and ask to be deleted. 134 // the binding and the dialog, and ask to be deleted.
138 client_.reset(); 135 client_.reset();
139 binding_.Close(); 136 binding_.Close();
140 delegate_->CloseDialog(); 137 delegate_->CloseDialog();
141 manager_->DestroyRequest(this); 138 manager_->DestroyRequest(this);
142 } 139 }
143 140
144 void PaymentRequest::Pay() { 141 void PaymentRequest::Pay() {
145 DCHECK(is_ready_to_pay_); 142 DCHECK(state_->is_ready_to_pay());
146 143
147 // TODO(mathp): Fill other fields in the PaymentResponsePtr object. 144 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 } 145 }
170 146
171 CurrencyFormatter* PaymentRequest::GetOrCreateCurrencyFormatter( 147 CurrencyFormatter* PaymentRequest::GetOrCreateCurrencyFormatter(
172 const std::string& currency_code, 148 const std::string& currency_code,
173 const std::string& currency_system, 149 const std::string& currency_system,
174 const std::string& locale_name) { 150 const std::string& locale_name) {
175 if (!currency_formatter_) { 151 if (!currency_formatter_) {
176 currency_formatter_.reset( 152 currency_formatter_.reset(
177 new CurrencyFormatter(currency_code, currency_system, locale_name)); 153 new CurrencyFormatter(currency_code, currency_system, locale_name));
178 } 154 }
179 return currency_formatter_.get(); 155 return currency_formatter_.get();
180 } 156 }
181 157
182 base::string16 PaymentRequest::GetFormattedCurrencyAmount( 158 base::string16 PaymentRequest::GetFormattedCurrencyAmount(
183 const std::string& amount) { 159 const std::string& amount) {
184 CurrencyFormatter* formatter = 160 CurrencyFormatter* formatter = GetOrCreateCurrencyFormatter(
185 GetOrCreateCurrencyFormatter(details()->total->amount->currency, 161 spec_->details()->total->amount->currency,
186 details()->total->amount->currency_system, 162 spec_->details()->total->amount->currency_system, GetApplicationLocale());
187 delegate_->GetApplicationLocale());
188 return formatter->Format(amount); 163 return formatter->Format(amount);
189 } 164 }
190 165
191 std::string PaymentRequest::GetFormattedCurrencyCode() { 166 std::string PaymentRequest::GetFormattedCurrencyCode() {
192 CurrencyFormatter* formatter = 167 CurrencyFormatter* formatter = GetOrCreateCurrencyFormatter(
193 GetOrCreateCurrencyFormatter(details()->total->amount->currency, 168 spec_->details()->total->amount->currency,
194 details()->total->amount->currency_system, 169 spec_->details()->total->amount->currency_system, GetApplicationLocale());
195 delegate_->GetApplicationLocale());
196 170
197 return formatter->formatted_currency_code(); 171 return formatter->formatted_currency_code();
198 } 172 }
199 173
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() { 174 void PaymentRequest::PopulateProfileCache() {
218 std::vector<autofill::AutofillProfile*> profiles = 175 std::vector<autofill::AutofillProfile*> profiles =
219 personal_data_manager()->GetProfilesToSuggest(); 176 personal_data_manager()->GetProfilesToSuggest();
220 177
221 // PaymentRequest may outlive the Profiles returned by the Data Manager. 178 // PaymentRequest may outlive the Profiles returned by the Data Manager.
222 // Thus, we store copies, and return a vector of pointers to these copies 179 // 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. 180 // whenever Profiles are requested. The same is true for credit cards.
224 for (size_t i = 0; i < profiles.size(); i++) { 181 for (size_t i = 0; i < profiles.size(); i++) {
225 profile_cache_.push_back( 182 profile_cache_.push_back(
226 base::MakeUnique<autofill::AutofillProfile>(*profiles[i])); 183 base::MakeUnique<autofill::AutofillProfile>(*profiles[i]));
227 184
228 // TODO(tmartino): Implement deduplication rules specific to shipping and 185 // TODO(tmartino): Implement deduplication rules specific to shipping and
229 // contact profiles. 186 // contact profiles.
230 shipping_profiles_.push_back(profile_cache_[i].get()); 187 shipping_profiles_.push_back(profile_cache_[i].get());
231 contact_profiles_.push_back(profile_cache_[i].get()); 188 contact_profiles_.push_back(profile_cache_[i].get());
232 } 189 }
233 190
234 const std::vector<autofill::CreditCard*>& cards = 191 const std::vector<autofill::CreditCard*>& cards =
235 personal_data_manager()->GetCreditCardsToSuggest(); 192 personal_data_manager()->GetCreditCardsToSuggest();
236 for (autofill::CreditCard* card : cards) { 193 for (autofill::CreditCard* card : cards) {
237 card_cache_.push_back(base::MakeUnique<autofill::CreditCard>(*card)); 194 card_cache_.push_back(base::MakeUnique<autofill::CreditCard>(*card));
238 credit_cards_.push_back(card_cache_.back().get()); 195 credit_cards_.push_back(card_cache_.back().get());
239 } 196 }
240 } 197 }
241 198
242 void PaymentRequest::SetDefaultProfileSelections() { 199 void PaymentRequest::SetDefaultProfileSelections() {
200 autofill::AutofillProfile* selected_shipping = nullptr;
201 autofill::AutofillProfile* selected_contact = nullptr;
202 autofill::CreditCard* selected_card = nullptr;
243 if (!shipping_profiles().empty()) 203 if (!shipping_profiles().empty())
244 selected_shipping_profile_ = shipping_profiles()[0]; 204 selected_shipping = shipping_profiles()[0];
245 205
246 if (!contact_profiles().empty()) 206 if (!contact_profiles().empty())
247 selected_contact_profile_ = contact_profiles()[0]; 207 selected_contact = contact_profiles()[0];
248 208
249 // TODO(anthonyvd): Change this code to prioritize server cards and implement 209 // TODO(anthonyvd): Change this code to prioritize server cards and implement
250 // a way to modify this function's return value. 210 // a way to modify this function's return value.
251 const std::vector<autofill::CreditCard*> cards = credit_cards(); 211 const std::vector<autofill::CreditCard*> cards = credit_cards();
252 auto first_complete_card = 212 auto first_complete_card =
253 std::find_if(cards.begin(), cards.end(), 213 std::find_if(cards.begin(), cards.end(),
254 [](autofill::CreditCard* card) { return card->IsValid(); }); 214 [](autofill::CreditCard* card) { return card->IsValid(); });
255 215
256 selected_credit_card_ = 216 selected_card =
257 first_complete_card == cards.end() ? nullptr : *first_complete_card; 217 first_complete_card == cards.end() ? nullptr : *first_complete_card;
258 218
259 UpdateIsReadyToPayAndNotifyObservers(); 219 state_->SetInitialSelections(selected_shipping, selected_contact,
260 } 220 selected_card);
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 } 221 }
407 222
408 } // namespace payments 223 } // namespace payments
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698