blob: 636146ea5a61da7efcfd32e3d6e6fa7b7035f528 [file] [log] [blame]
mathpf709499d2017-01-09 20:48:361// 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
rouslan908248c2017-02-27 21:30:245#include "components/payments/content/payment_request.h"
6
Rouslan Solomakhin9c83b8a2019-06-24 20:50:037#include <algorithm>
anthonyvdd23ed702017-04-05 15:29:008#include <string>
rouslan908248c2017-02-27 21:30:249#include <utility>
mathpf709499d2017-01-09 20:48:3610
Sebastien Marchand53801a32019-01-25 16:26:1111#include "base/bind.h"
Rouslan Solomakhin1804ee42017-10-03 14:27:4312#include "base/feature_list.h"
Mathieu Perreault627b97c2017-08-12 00:44:2213#include "base/stl_util.h"
Rouslan Solomakhin9c83b8a2019-06-24 20:50:0314#include "base/strings/string_util.h"
rouslan690997682017-05-09 18:07:3915#include "components/payments/content/can_make_payment_query_factory.h"
Rouslan Solomakhin4eea9bc22017-10-10 15:18:5116#include "components/payments/content/content_payment_request_delegate.h"
Rouslan Solomakhin2039a342020-05-21 19:21:0417#include "components/payments/content/payment_app.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2218#include "components/payments/content/payment_request_converter.h"
rouslan908248c2017-02-27 21:30:2419#include "components/payments/content/payment_request_web_contents_manager.h"
rouslan690997682017-05-09 18:07:3920#include "components/payments/core/can_make_payment_query.h"
Rouslan Solomakhin2dc99c32020-03-24 21:25:0321#include "components/payments/core/error_message_util.h"
Rouslan Solomakhina480efa2019-05-06 15:37:2222#include "components/payments/core/error_strings.h"
Anthony Vallee-Dubois968ae4d2018-03-15 16:56:3623#include "components/payments/core/features.h"
Rouslan Solomakhin85b10da2019-11-05 20:03:1724#include "components/payments/core/method_strings.h"
Rouslan Solomakhineb06b272019-07-10 16:23:3225#include "components/payments/core/native_error_strings.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2226#include "components/payments/core/payment_details.h"
27#include "components/payments/core/payment_details_validation.h"
anthonyvd6a43b932017-05-11 18:39:2728#include "components/payments/core/payment_prefs.h"
Danyao Wang50ccb9f2019-05-09 23:28:0329#include "components/payments/core/payments_experimental_features.h"
Jinho Bangbe463a22018-08-02 10:26:5030#include "components/payments/core/payments_validators.h"
Rouslan Solomakhin77a7e1a2019-05-23 17:37:5831#include "components/payments/core/url_util.h"
anthonyvd6a43b932017-05-11 18:39:2732#include "components/prefs/pref_service.h"
Steven Holte2083e8bc2018-07-16 23:50:3633#include "components/ukm/content/source_url_recorder.h"
Rouslan Solomakhin6e979ab2017-08-30 17:30:3934#include "components/url_formatter/elide_url.h"
mathpf709499d2017-01-09 20:48:3635#include "content/public/browser/browser_thread.h"
rouslan690997682017-05-09 18:07:3936#include "content/public/browser/render_frame_host.h"
Danyao Wang15840792020-05-07 23:52:1737#include "content/public/browser/render_process_host.h"
mathpf709499d2017-01-09 20:48:3638#include "content/public/browser/web_contents.h"
Rouslan Solomakhin1804ee42017-10-03 14:27:4339#include "content/public/common/content_features.h"
Rouslan Solomakhin77a7e1a2019-05-23 17:37:5840#include "content/public/common/origin_util.h"
Sahel Sharify54f5e862020-03-26 20:02:4141#include "services/metrics/public/cpp/ukm_source_id.h"
mathpf709499d2017-01-09 20:48:3642
Rouslan Solomakhina480efa2019-05-06 15:37:2243namespace payments {
Danyao Wangce175bf2018-12-21 22:35:5844namespace {
45
Danyao Wang57aa0442019-01-31 04:06:4146using ::payments::mojom::CanMakePaymentQueryResult;
Danyao Wangce175bf2018-12-21 22:35:5847using ::payments::mojom::HasEnrolledInstrumentQueryResult;
48
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:3949bool IsGooglePaymentMethod(const std::string& method_name) {
Rouslan Solomakhin85b10da2019-11-05 20:03:1750 return method_name == methods::kGooglePay ||
51 method_name == methods::kAndroidPay;
Sahel Sharify26884382019-05-07 16:23:5152}
53
Sahel Sharify9d98a502019-09-30 19:58:3954// Redact shipping address before exposing it in ShippingAddressChangeEvent.
55// https://w3c.github.io/payment-request/#shipping-address-changed-algorithm
56mojom::PaymentAddressPtr RedactShippingAddress(
57 mojom::PaymentAddressPtr address) {
58 DCHECK(address);
59 if (!PaymentsExperimentalFeatures::IsEnabled(
60 features::kWebPaymentsRedactShippingAddress)) {
61 return address;
62 }
63 address->organization.clear();
64 address->phone.clear();
65 address->recipient.clear();
66 address->address_line.clear();
67 return address;
68}
69
Rouslan Solomakhina480efa2019-05-06 15:37:2270} // namespace
mathpf709499d2017-01-09 20:48:3671
72PaymentRequest::PaymentRequest(
rouslan690997682017-05-09 18:07:3973 content::RenderFrameHost* render_frame_host,
mathpf709499d2017-01-09 20:48:3674 content::WebContents* web_contents,
Rouslan Solomakhin4eea9bc22017-10-10 15:18:5175 std::unique_ptr<ContentPaymentRequestDelegate> delegate,
mathpf709499d2017-01-09 20:48:3676 PaymentRequestWebContentsManager* manager,
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0777 PaymentRequestDisplayManager* display_manager,
Gyuyoung Kim9cfbda32019-08-27 02:15:1878 mojo::PendingReceiver<mojom::PaymentRequest> receiver,
mathp300fa542017-03-27 19:29:3779 ObserverForTest* observer_for_testing)
mathpf709499d2017-01-09 20:48:3680 : web_contents_(web_contents),
Danyao Wang15840792020-05-07 23:52:1781 // TODO(crbug.com/1058840): change to WeakPtr<RenderFrameHost> once
82 // RenderFrameHost provides a WeakPtr API.
83 initiator_frame_routing_id_(content::GlobalFrameRoutingId(
84 render_frame_host->GetProcess()->GetID(),
85 render_frame_host->GetRoutingID())),
Rouslan Solomakhin27064702018-12-14 21:15:3386 log_(web_contents_),
mathpf709499d2017-01-09 20:48:3687 delegate_(std::move(delegate)),
88 manager_(manager),
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0789 display_manager_(display_manager),
90 display_handle_(nullptr),
Rouslan Solomakhin1f95f092019-08-09 12:28:5191 payment_handler_host_(web_contents_, this),
Rouslan Solomakhin6e979ab2017-08-30 17:30:3992 top_level_origin_(url_formatter::FormatUrlForSecurityDisplay(
93 web_contents_->GetLastCommittedURL())),
94 frame_origin_(url_formatter::FormatUrlForSecurityDisplay(
95 render_frame_host->GetLastCommittedURL())),
Rouslan Solomakhinc71cca622020-01-31 22:15:3596 frame_security_origin_(render_frame_host->GetLastCommittedOrigin()),
sebsg20b49d7b2017-05-04 20:23:1797 observer_for_testing_(observer_for_testing),
Ramin Halavatifbfcef672020-05-21 16:33:3498 journey_logger_(delegate_->IsOffTheRecord(),
Jeremy Roman5c341f6d2019-07-15 15:56:1099 ukm::GetSourceIdForWebContentsDocument(web_contents)) {
Gyuyoung Kim9cfbda32019-08-27 02:15:18100 receiver_.Bind(std::move(receiver));
mathpf4bc50e2017-01-24 05:17:50101 // OnConnectionTerminated will be called when the Mojo pipe is closed. This
102 // will happen as a result of many renderer-side events (both successful and
103 // erroneous in nature).
104 // TODO(crbug.com/683636): Investigate using
105 // set_connection_error_with_reason_handler with Binding::CloseWithReason.
Gyuyoung Kim9cfbda32019-08-27 02:15:18106 receiver_.set_disconnect_handler(base::BindOnce(
Anthony Vallee-Duboisdc1dbf1a2017-07-17 15:01:13107 &PaymentRequest::OnConnectionTerminated, weak_ptr_factory_.GetWeakPtr()));
mathpf709499d2017-01-09 20:48:36108}
109
110PaymentRequest::~PaymentRequest() {}
111
Gyuyoung Kim9cfbda32019-08-27 02:15:18112void PaymentRequest::Init(
113 mojo::PendingRemote<mojom::PaymentRequestClient> client,
114 std::vector<mojom::PaymentMethodDataPtr> method_data,
115 mojom::PaymentDetailsPtr details,
116 mojom::PaymentOptionsPtr options) {
mathpf709499d2017-01-09 20:48:36117 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin27064702018-12-14 21:15:33118
119 if (is_initialized_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22120 log_.Error(errors::kAttemptedInitializationTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33121 OnConnectionTerminated();
122 return;
123 }
124
125 is_initialized_ = true;
Gyuyoung Kim9cfbda32019-08-27 02:15:18126 client_.Bind(std::move(client));
rouslan6e3cf7c62017-04-17 21:23:28127
rouslanb28f4532017-05-08 15:41:47128 const GURL last_committed_url = delegate_->GetLastCommittedURL();
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58129 if (!content::IsOriginSecure(last_committed_url)) {
Rouslan Solomakhina480efa2019-05-06 15:37:22130 log_.Error(errors::kNotInASecureOrigin);
rouslan6e3cf7c62017-04-17 21:23:28131 OnConnectionTerminated();
132 return;
133 }
134
Rouslan Solomakhin55db8272019-06-25 18:16:28135 // TODO(crbug.com/978471): Improve architecture for handling prohibited
136 // origins and invalid SSL certificates.
rouslanb28f4532017-05-08 15:41:47137 bool allowed_origin =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58138 UrlUtil::IsOriginAllowedToUseWebPaymentApis(last_committed_url);
rouslanb28f4532017-05-08 15:41:47139 if (!allowed_origin) {
Rouslan Solomakhin68429b72019-06-27 15:12:39140 reject_show_error_message_ = errors::kProhibitedOrigin;
rouslanb28f4532017-05-08 15:41:47141 }
142
Rouslan Solomakhin55db8272019-06-25 18:16:28143 bool invalid_ssl = false;
144 if (last_committed_url.SchemeIsCryptographic()) {
Rouslan Solomakhin68429b72019-06-27 15:12:39145 DCHECK(reject_show_error_message_.empty());
146 reject_show_error_message_ =
Rouslan Solomakhin55db8272019-06-25 18:16:28147 delegate_->GetInvalidSslCertificateErrorMessage();
Rouslan Solomakhin68429b72019-06-27 15:12:39148 invalid_ssl = !reject_show_error_message_.empty();
Rouslan Solomakhin27064702018-12-14 21:15:33149 }
rouslanb28f4532017-05-08 15:41:47150
151 if (!allowed_origin || invalid_ssl) {
Rouslan Solomakhin27064702018-12-14 21:15:33152 // Intentionally don't set |spec_| and |state_|, so the UI is never shown.
Rouslan Solomakhin68429b72019-06-27 15:12:39153 log_.Error(reject_show_error_message_);
Rouslan Solomakhina480efa2019-05-06 15:37:22154 log_.Error(errors::kProhibitedOriginOrInvalidSslExplanation);
rouslan6e3cf7c62017-04-17 21:23:28155 return;
156 }
157
mathpf709499d2017-01-09 20:48:36158 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22159 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33160 log_.Error(error);
mathpf4bc50e2017-01-24 05:17:50161 OnConnectionTerminated();
mathpf709499d2017-01-09 20:48:36162 return;
163 }
rouslan6e3cf7c62017-04-17 21:23:28164
jinho.bangfcb5ec92017-03-29 08:08:02165 if (!details->total) {
Rouslan Solomakhina480efa2019-05-06 15:37:22166 log_.Error(errors::kTotalRequired);
jinho.bangfcb5ec92017-03-29 08:08:02167 OnConnectionTerminated();
168 return;
169 }
rouslan6e3cf7c62017-04-17 21:23:28170
Danyao Wang15840792020-05-07 23:52:17171 // TODO(crbug.com/1058840): change to WeakPtr<RenderFrameHost> once
172 // RenderFrameHost provides a WeakPtr API.
173 content::RenderFrameHost* initiator_frame =
174 content::RenderFrameHost::FromID(initiator_frame_routing_id_);
175 if (!initiator_frame) {
176 log_.Error(errors::kInvalidInitiatorFrame);
177 OnConnectionTerminated();
178 return;
179 }
180
sebsga70a6da2017-12-21 22:27:02181 spec_ = std::make_unique<PaymentRequestSpec>(
Rouslan Solomakhin1f95f092019-08-09 12:28:51182 std::move(options), std::move(details), std::move(method_data),
183 /*observer=*/this, delegate_->GetApplicationLocale());
sebsga70a6da2017-12-21 22:27:02184 state_ = std::make_unique<PaymentRequestState>(
Danyao Wang15840792020-05-07 23:52:17185 web_contents_, initiator_frame, top_level_origin_, frame_origin_,
186 frame_security_origin_, spec_.get(),
Rouslan Solomakhin1f95f092019-08-09 12:28:51187 /*delegate=*/this, delegate_->GetApplicationLocale(),
Rouslan Solomakhin13947ae32020-05-22 15:34:29188 delegate_->GetPersonalDataManager(), delegate_.get(), &journey_logger_);
Mathieu Perreault627b97c2017-08-12 00:44:22189
190 journey_logger_.SetRequestedInformation(
191 spec_->request_shipping(), spec_->request_payer_email(),
192 spec_->request_payer_phone(), spec_->request_payer_name());
193
194 // Log metrics around which payment methods are requested by the merchant.
Rouslan Solomakhin85b10da2019-11-05 20:03:17195 GURL google_pay_url(methods::kGooglePay);
196 GURL android_pay_url(methods::kAndroidPay);
Mathieu Perreault627b97c2017-08-12 00:44:22197 // Looking for payment methods that are NOT google-related payment methods.
198 auto non_google_it =
199 std::find_if(spec_->url_payment_method_identifiers().begin(),
200 spec_->url_payment_method_identifiers().end(),
201 [google_pay_url, android_pay_url](const GURL& url) {
202 return url != google_pay_url && url != android_pay_url;
203 });
204 journey_logger_.SetRequestedPaymentMethodTypes(
205 /*requested_basic_card=*/!spec_->supported_card_networks().empty(),
206 /*requested_method_google=*/
Jan Wilken Dörrie45d34f42019-06-08 09:40:54207 base::Contains(spec_->url_payment_method_identifiers(), google_pay_url) ||
208 base::Contains(spec_->url_payment_method_identifiers(),
209 android_pay_url),
Mathieu Perreault627b97c2017-08-12 00:44:22210 /*requested_method_other=*/non_google_it !=
211 spec_->url_payment_method_identifiers().end());
Rouslan Solomakhin1f95f092019-08-09 12:28:51212
213 payment_handler_host_.set_payment_request_id_for_logs(*spec_->details().id);
mathpf709499d2017-01-09 20:48:36214}
215
Rouslan Solomakhin9788d4b2019-04-09 13:10:23216void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
Rouslan Solomakhin27064702018-12-14 21:15:33217 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22218 log_.Error(errors::kCannotShowWithoutInit);
mathpf4bc50e2017-01-24 05:17:50219 OnConnectionTerminated();
tmartino8ce922852017-01-09 22:23:10220 return;
221 }
rouslan6e3cf7c62017-04-17 21:23:28222
Rouslan Solomakhin27064702018-12-14 21:15:33223 if (is_show_called_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22224 log_.Error(errors::kCannotShowTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33225 OnConnectionTerminated();
226 return;
227 }
228
229 is_show_called_ = true;
Sahel Sharify0fadf4da2019-08-09 14:55:58230 journey_logger_.SetTriggerTime();
Rouslan Solomakhin27064702018-12-14 21:15:33231
rouslan7d433cc22017-05-08 15:18:07232 // A tab can display only one PaymentRequest UI at a time.
Anthony Vallee-Dubois8f5e7e12018-01-12 16:14:06233 display_handle_ = display_manager_->TryShow(delegate_.get());
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07234 if (!display_handle_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22235 log_.Error(errors::kAnotherUiShowing);
Sahel Sharifya50fc4c2019-05-28 14:53:22236 DCHECK(!has_recorded_completion_);
237 has_recorded_completion_ = true;
sebsg828269bc2017-06-09 19:11:12238 journey_logger_.SetNotShown(
239 JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03240 client_->OnError(mojom::PaymentErrorReason::ALREADY_SHOWING,
241 errors::kAnotherUiShowing);
rouslan7d433cc22017-05-08 15:18:07242 OnConnectionTerminated();
243 return;
244 }
245
Rouslan Solomakhin5b510432017-09-26 16:59:32246 if (!delegate_->IsBrowserWindowActive()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22247 log_.Error(errors::kCannotShowInBackgroundTab);
Sahel Sharifya50fc4c2019-05-28 14:53:22248 DCHECK(!has_recorded_completion_);
249 has_recorded_completion_ = true;
Rouslan Solomakhin5b510432017-09-26 16:59:32250 journey_logger_.SetNotShown(JourneyLogger::NOT_SHOWN_REASON_OTHER);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03251 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
252 errors::kCannotShowInBackgroundTab);
Rouslan Solomakhin5b510432017-09-26 16:59:32253 OnConnectionTerminated();
254 return;
255 }
256
Rouslan Solomakhind2cae95a2018-08-09 00:16:10257 if (!state_) {
Rouslan Solomakhin27064702018-12-14 21:15:33258 // SSL is not valid. Reject show with NotSupportedError, disconnect the
259 // mojo pipe, and destroy this object.
Rouslan Solomakhind5dcc322019-07-11 21:47:20260 AreRequestedMethodsSupportedCallback(false, reject_show_error_message_);
Rouslan Solomakhind2cae95a2018-08-09 00:16:10261 return;
262 }
263
Rouslan Solomakhin833f8512018-04-03 23:19:25264 is_show_user_gesture_ = is_user_gesture;
265
Rouslan Solomakhin9788d4b2019-04-09 13:10:23266 if (wait_for_updated_details) {
267 // Put |spec_| into uninitialized state, so the UI knows to show a spinner.
268 // This method does not block.
269 spec_->StartWaitingForUpdateWith(
270 PaymentRequestSpec::UpdateReason::INITIAL_PAYMENT_DETAILS);
Sahel Sharify98a2c2a2019-07-12 18:57:40271 } else {
272 DCHECK(spec_->details().total);
273 journey_logger_.RecordTransactionAmount(
274 spec_->details().total->amount->currency,
275 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23276 }
Takashi Sakamoto48a29702019-04-08 05:06:32277
Rouslan Solomakhin9788d4b2019-04-09 13:10:23278 display_handle_->Show(this);
Rouslan Solomakhin1dca2a922019-09-06 22:25:07279
280 state_->set_is_show_user_gesture(is_show_user_gesture_);
gogerald0a7ee6c2017-11-13 18:23:19281 state_->AreRequestedMethodsSupported(
282 base::BindOnce(&PaymentRequest::AreRequestedMethodsSupportedCallback,
283 weak_ptr_factory_.GetWeakPtr()));
284}
285
Jinho Bangbe463a22018-08-02 10:26:50286void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
Rouslan Solomakhin27064702018-12-14 21:15:33287 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22288 log_.Error(errors::kCannotRetryWithoutInit);
Jinho Bangcac8d9a02018-08-23 19:47:22289 OnConnectionTerminated();
290 return;
291 }
292
Rouslan Solomakhin27064702018-12-14 21:15:33293 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22294 log_.Error(errors::kCannotRetryWithoutShow);
Jinho Bangcac8d9a02018-08-23 19:47:22295 OnConnectionTerminated();
296 return;
297 }
298
Jinho Bangbe463a22018-08-02 10:26:50299 std::string error;
300 if (!PaymentsValidators::IsValidPaymentValidationErrorsFormat(errors,
301 &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33302 log_.Error(error);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03303 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL, error);
Jinho Bangbe463a22018-08-02 10:26:50304 OnConnectionTerminated();
305 return;
306 }
307
Sahel Sharifycf4e2132020-01-24 23:59:08308 state()->SetAvailablePaymentAppForRetry();
Jinho Bang092e7162018-09-06 23:41:19309 spec()->Retry(std::move(errors));
Jinho Bangcac8d9a02018-08-23 19:47:22310 display_handle_->Retry();
Jinho Bangbe463a22018-08-02 10:26:50311}
312
mathp151bd312017-04-03 21:07:24313void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
Rouslan Solomakhin27064702018-12-14 21:15:33314 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22315 log_.Error(errors::kCannotUpdateWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33316 OnConnectionTerminated();
317 return;
318 }
319
320 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22321 log_.Error(errors::kCannotUpdateWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33322 OnConnectionTerminated();
323 return;
324 }
325
mathp151bd312017-04-03 21:07:24326 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22327 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33328 log_.Error(error);
mathp151bd312017-04-03 21:07:24329 OnConnectionTerminated();
330 return;
331 }
Rouslan Solomakhin4cbda822017-08-23 18:50:39332
Jinho Bang092e7162018-09-06 23:41:19333 if (details->shipping_address_errors &&
334 !PaymentsValidators::IsValidAddressErrorsFormat(
335 details->shipping_address_errors, &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33336 log_.Error(error);
Jinho Bang092e7162018-09-06 23:41:19337 OnConnectionTerminated();
338 return;
339 }
340
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39341 if (state()->selected_app() && state()->IsPaymentAppInvoked() &&
Rouslan Solomakhin218f7072020-05-21 14:26:47342 state()->selected_app()->IsWaitingForPaymentDetailsUpdate()) {
Rouslan Solomakhin2039a342020-05-21 19:21:04343 state()->selected_app()->UpdateWith(details);
Rouslan Solomakhina480efa2019-05-06 15:37:22344 }
345
Rouslan Solomakhin6ba46fd2019-04-11 23:44:01346 bool is_resolving_promise_passed_into_show_method = !spec_->IsInitialized();
347
mathp151bd312017-04-03 21:07:24348 spec_->UpdateWith(std::move(details));
Rouslan Solomakhin9788d4b2019-04-09 13:10:23349
350 if (is_resolving_promise_passed_into_show_method) {
Sahel Sharify98a2c2a2019-07-12 18:57:40351 DCHECK(spec_->details().total);
352 journey_logger_.RecordTransactionAmount(
353 spec_->details().total->amount->currency,
354 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23355 if (SatisfiesSkipUIConstraints()) {
Rouslan Solomakhin9788d4b2019-04-09 13:10:23356 Pay();
357 } else if (spec_->request_shipping()) {
358 state_->SelectDefaultShippingAddressAndNotifyObservers();
359 }
360 }
mathp151bd312017-04-03 21:07:24361}
362
Sahel Sharifye2f889542019-12-02 22:17:48363void PaymentRequest::OnPaymentDetailsNotUpdated() {
Rouslan Solomakhin27064702018-12-14 21:15:33364 // This Mojo call is triggered by the user of the API doing nothing in
365 // response to a shipping address update event, so the error messages cannot
366 // be more verbose.
367 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22368 log_.Error(errors::kNotInitialized);
Rouslan Solomakhin27064702018-12-14 21:15:33369 OnConnectionTerminated();
370 return;
371 }
372
373 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22374 log_.Error(errors::kNotShown);
Rouslan Solomakhin27064702018-12-14 21:15:33375 OnConnectionTerminated();
376 return;
377 }
378
Rouslan Solomakhina9ff9282017-10-31 21:58:05379 spec_->RecomputeSpecForDetails();
Rouslan Solomakhina480efa2019-05-06 15:37:22380
Rouslan Solomakhin218f7072020-05-21 14:26:47381 if (state()->IsPaymentAppInvoked() && state()->selected_app() &&
382 state()->selected_app()->IsWaitingForPaymentDetailsUpdate()) {
Rouslan Solomakhin2039a342020-05-21 19:21:04383 state()->selected_app()->OnPaymentDetailsNotUpdated();
Rouslan Solomakhincf9093f2019-05-20 15:32:17384 }
Rouslan Solomakhina9ff9282017-10-31 21:58:05385}
386
mathpf4bc50e2017-01-24 05:17:50387void PaymentRequest::Abort() {
Rouslan Solomakhin27064702018-12-14 21:15:33388 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22389 log_.Error(errors::kCannotAbortWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33390 OnConnectionTerminated();
391 return;
392 }
393
394 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22395 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33396 OnConnectionTerminated();
397 return;
398 }
399
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56400 // The API user has decided to abort. If a successful abort message is
401 // returned to the renderer, the Mojo message pipe is closed, which triggers
mathpf4bc50e2017-01-24 05:17:50402 // PaymentRequest::OnConnectionTerminated, which destroys this object.
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56403 // Otherwise, the abort promise is rejected and the pipe is not closed.
404 // The abort is only successful if the payment app wasn't yet invoked.
405 // TODO(crbug.com/716546): Add a merchant abort metric
406
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56407 if (observer_for_testing_)
408 observer_for_testing_->OnAbortCalled();
Sahel Sharifyd3418222020-02-19 21:50:02409
Rouslan Solomakhin13947ae32020-05-22 15:34:29410 if (!state_->IsPaymentAppInvoked() || !state_->selected_app()) {
411 OnAbortResult(/*aborted=*/true);
412 return;
413 }
414
415 state_->selected_app()->AbortPaymentApp(base::BindOnce(
416 &PaymentRequest::OnAbortResult, weak_ptr_factory_.GetWeakPtr()));
mathpf4bc50e2017-01-24 05:17:50417}
418
mathp218795892017-03-29 15:15:34419void PaymentRequest::Complete(mojom::PaymentComplete result) {
Rouslan Solomakhin27064702018-12-14 21:15:33420 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22421 log_.Error(errors::kCannotCompleteWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33422 OnConnectionTerminated();
mathp4b85b582017-03-08 21:07:16423 return;
Rouslan Solomakhin27064702018-12-14 21:15:33424 }
425
426 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22427 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33428 OnConnectionTerminated();
429 return;
430 }
mathp4b85b582017-03-08 21:07:16431
Sahel Sharify2a56b5f2019-11-28 19:27:19432 if (observer_for_testing_) {
433 observer_for_testing_->OnCompleteCalled();
434 }
435
Rouslan Solomakhine3473192017-06-16 14:54:57436 // Failed transactions show an error. Successful and unknown-state
437 // transactions don't show an error.
438 if (result == mojom::PaymentComplete::FAIL) {
mathp218795892017-03-29 15:15:34439 delegate_->ShowErrorMessage();
440 } else {
sebsgfcdd13c2017-06-08 15:49:33441 DCHECK(!has_recorded_completion_);
sebsgf8272a22017-05-26 14:32:58442 journey_logger_.SetCompleted();
sebsgfcdd13c2017-06-08 15:49:33443 has_recorded_completion_ = true;
Sahel Sharify98a2c2a2019-07-12 18:57:40444 DCHECK(spec_->details().total);
445 journey_logger_.RecordTransactionAmount(
446 spec_->details().total->amount->currency,
447 spec_->details().total->amount->value, true /*completed*/);
sebsgfcdd13c2017-06-08 15:49:33448
anthonyvd6a43b932017-05-11 18:39:27449 delegate_->GetPrefService()->SetBoolean(kPaymentsFirstTransactionCompleted,
450 true);
mathp218795892017-03-29 15:15:34451 // When the renderer closes the connection,
452 // PaymentRequest::OnConnectionTerminated will be called.
453 client_->OnComplete();
sebsg8a93b272017-05-11 19:30:22454 state_->RecordUseStats();
mathp218795892017-03-29 15:15:34455 }
mathp4b85b582017-03-08 21:07:16456}
457
Danyao Wang03a4cbd2019-08-15 23:47:11458void PaymentRequest::CanMakePayment() {
Rouslan Solomakhin27064702018-12-14 21:15:33459 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22460 log_.Error(errors::kCannotCallCanMakePaymentWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33461 OnConnectionTerminated();
462 return;
463 }
464
465 // It's valid to call canMakePayment() without calling show() first.
466
gogerald8189d522017-09-15 17:52:18467 if (observer_for_testing_)
468 observer_for_testing_->OnCanMakePaymentCalled();
Mathieu Perreaultcacb85e2018-06-06 20:40:13469
Rouslan Solomakhind2cae95a2018-08-09 00:16:10470 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
471 !state_) {
Danyao Wang03a4cbd2019-08-15 23:47:11472 CanMakePaymentCallback(/*can_make_payment=*/false);
Mathieu Perreaultcacb85e2018-06-06 20:40:13473 } else {
Rouslan Solomakhind2cae95a2018-08-09 00:16:10474 state_->CanMakePayment(
Mathieu Perreaultcacb85e2018-06-06 20:40:13475 base::BindOnce(&PaymentRequest::CanMakePaymentCallback,
Danyao Wang03a4cbd2019-08-15 23:47:11476 weak_ptr_factory_.GetWeakPtr()));
Mathieu Perreaultcacb85e2018-06-06 20:40:13477 }
gogerald8189d522017-09-15 17:52:18478}
479
Rouslan Solomakhin5683eb282019-01-29 18:06:03480void PaymentRequest::HasEnrolledInstrument(bool per_method_quota) {
Danyao Wangce175bf2018-12-21 22:35:58481 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22482 log_.Error(errors::kCannotCallHasEnrolledInstrumentWithoutInit);
Danyao Wangce175bf2018-12-21 22:35:58483 OnConnectionTerminated();
484 return;
485 }
486
487 // It's valid to call hasEnrolledInstrument() without calling show() first.
488
489 if (observer_for_testing_)
490 observer_for_testing_->OnHasEnrolledInstrumentCalled();
491
492 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
493 !state_) {
Rouslan Solomakhin5683eb282019-01-29 18:06:03494 HasEnrolledInstrumentCallback(per_method_quota,
495 /*has_enrolled_instrument=*/false);
Danyao Wangce175bf2018-12-21 22:35:58496 } else {
497 state_->HasEnrolledInstrument(
498 base::BindOnce(&PaymentRequest::HasEnrolledInstrumentCallback,
Rouslan Solomakhin5683eb282019-01-29 18:06:03499 weak_ptr_factory_.GetWeakPtr(), per_method_quota));
Danyao Wangce175bf2018-12-21 22:35:58500 }
501}
502
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02503bool PaymentRequest::ChangePaymentMethod(const std::string& method_name,
504 const std::string& stringified_data) {
Rouslan Solomakhina480efa2019-05-06 15:37:22505 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02506 DCHECK(!method_name.empty());
Rouslan Solomakhina480efa2019-05-06 15:37:22507
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02508 if (!state_ || !state_->IsPaymentAppInvoked() || !client_)
509 return false;
Rouslan Solomakhina480efa2019-05-06 15:37:22510
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02511 client_->OnPaymentMethodChange(method_name, stringified_data);
512 return true;
Rouslan Solomakhina480efa2019-05-06 15:37:22513}
514
Sahel Sharify9d98a502019-09-30 19:58:39515bool PaymentRequest::ChangeShippingOption(
516 const std::string& shipping_option_id) {
517 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
518 DCHECK(!shipping_option_id.empty());
519
520 bool is_valid_id = false;
521 if (spec_->details().shipping_options) {
522 for (const auto& option : spec_->GetShippingOptions()) {
523 if (option->id == shipping_option_id) {
524 is_valid_id = true;
525 break;
526 }
527 }
528 }
529
530 if (!state_ || !state_->IsPaymentAppInvoked() || !client_ || !spec_ ||
531 !spec_->request_shipping() || !is_valid_id) {
532 return false;
533 }
534
535 client_->OnShippingOptionChange(shipping_option_id);
536 return true;
537}
538
539bool PaymentRequest::ChangeShippingAddress(
540 mojom::PaymentAddressPtr shipping_address) {
541 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
542 DCHECK(shipping_address);
543
544 if (!state_ || !state_->IsPaymentAppInvoked() || !client_ || !spec_ ||
545 !spec_->request_shipping()) {
546 return false;
547 }
548
549 client_->OnShippingAddressChange(
550 RedactShippingAddress(std::move(shipping_address)));
551 return true;
552}
553
Rouslan Solomakhin27064702018-12-14 21:15:33554void PaymentRequest::AreRequestedMethodsSupportedCallback(
Rouslan Solomakhind5dcc322019-07-11 21:47:20555 bool methods_supported,
556 const std::string& error_message) {
Danyao Wang25f72dc2019-10-18 05:11:32557 if (is_show_called_ && observer_for_testing_)
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39558 observer_for_testing_->OnShowAppsReady();
Danyao Wang25f72dc2019-10-18 05:11:32559
Rouslan Solomakhin27064702018-12-14 21:15:33560 if (methods_supported) {
Sahel Sharifyd3f1bc82019-05-21 18:48:46561 if (SatisfiesSkipUIConstraints())
Rouslan Solomakhin27064702018-12-14 21:15:33562 Pay();
Rouslan Solomakhin27064702018-12-14 21:15:33563 } else {
Sahel Sharifya50fc4c2019-05-28 14:53:22564 DCHECK(!has_recorded_completion_);
565 has_recorded_completion_ = true;
Rouslan Solomakhin27064702018-12-14 21:15:33566 journey_logger_.SetNotShown(
567 JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03568 client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED,
Rouslan Solomakhin2dc99c32020-03-24 21:25:03569 GetNotSupportedErrorMessage(
570 spec_ ? spec_->payment_method_identifiers_set()
571 : std::set<std::string>()) +
Rouslan Solomakhind5dcc322019-07-11 21:47:20572 (error_message.empty() ? "" : " " + error_message));
Rouslan Solomakhin27064702018-12-14 21:15:33573 if (observer_for_testing_)
574 observer_for_testing_->OnNotSupportedError();
575 OnConnectionTerminated();
576 }
577}
578
579bool PaymentRequest::IsInitialized() const {
580 return is_initialized_ && client_ && client_.is_bound() &&
Gyuyoung Kim9cfbda32019-08-27 02:15:18581 receiver_.is_bound();
Rouslan Solomakhin27064702018-12-14 21:15:33582}
583
584bool PaymentRequest::IsThisPaymentRequestShowing() const {
585 return is_show_called_ && display_handle_ && spec_ && state_;
586}
587
Sahel Sharify9120fd32019-12-05 17:16:11588bool PaymentRequest::OnlySingleAppCanProvideAllRequiredInformation() const {
589 DCHECK(state()->IsInitialized());
590 DCHECK(spec()->IsInitialized());
591
592 if (!spec()->request_shipping() && !spec()->request_payer_name() &&
593 !spec()->request_payer_phone() && !spec()->request_payer_email()) {
594 return state()->available_apps().size() == 1 &&
595 state()->available_apps().at(0)->type() !=
596 PaymentApp::Type::AUTOFILL;
597 }
598
599 bool an_app_can_provide_all_info = false;
600 for (const auto& app : state()->available_apps()) {
601 if ((!spec()->request_shipping() || app->HandlesShippingAddress()) &&
602 (!spec()->request_payer_name() || app->HandlesPayerName()) &&
603 (!spec()->request_payer_phone() || app->HandlesPayerPhone()) &&
604 (!spec()->request_payer_email() || app->HandlesPayerEmail())) {
605 // There is another available app that can provide all merchant requested
606 // information information.
607 if (an_app_can_provide_all_info)
608 return false;
609
610 an_app_can_provide_all_info = true;
611 }
612 }
613 return an_app_can_provide_all_info;
614}
615
Sahel Sharifyd3f1bc82019-05-21 18:48:46616bool PaymentRequest::SatisfiesSkipUIConstraints() {
Rouslan Solomakhin9788d4b2019-04-09 13:10:23617 // Only allowing URL base payment apps to skip the payment sheet.
Sahel Sharifyd3f1bc82019-05-21 18:48:46618 skipped_payment_request_ui_ =
Sahel Sharify9120fd32019-12-05 17:16:11619 (spec()->url_payment_method_identifiers().size() > 0 ||
danakjdca06902019-06-27 21:41:41620 delegate_->SkipUiForBasicCard()) &&
Sahel Sharifyd3f1bc82019-05-21 18:48:46621 base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
622 base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
623 is_show_user_gesture_ && state()->IsInitialized() &&
Sahel Sharify9120fd32019-12-05 17:16:11624 spec()->IsInitialized() &&
625 OnlySingleAppCanProvideAllRequiredInformation() &&
Sahel Sharify90307872019-12-02 17:28:14626 // The available app should be preselectable.
Sahel Sharify9120fd32019-12-05 17:16:11627 state()->selected_app() != nullptr;
Sahel Sharifyd3f1bc82019-05-21 18:48:46628 if (skipped_payment_request_ui_) {
629 DCHECK(state()->IsInitialized() && spec()->IsInitialized());
630 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SKIPPED_SHOW);
631 } else if (state()->IsInitialized() && spec()->IsInitialized()) {
632 // Set EVENT_SHOWN only after state() and spec() initialization.
633 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
634 }
635 return skipped_payment_request_ui_;
Rouslan Solomakhin27064702018-12-14 21:15:33636}
637
mathpf1a7a3752017-03-15 11:23:37638void PaymentRequest::OnPaymentResponseAvailable(
639 mojom::PaymentResponsePtr response) {
Rouslan Solomakhin68429b72019-06-27 15:12:39640 DCHECK(!response->method_name.empty());
641 DCHECK(!response->stringified_details.empty());
642
mathp57c8c862017-06-16 20:15:45643 journey_logger_.SetEventOccurred(
644 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
gogerald7a2b761e2017-11-09 18:30:19645
Sahel Sharify26884382019-05-07 16:23:51646 // Log the correct "selected instrument" metric according to its type and
647 // the method name in response.
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39648 DCHECK(state_->selected_app());
Sahel Sharify26884382019-05-07 16:23:51649 JourneyLogger::Event selected_event =
650 JourneyLogger::Event::EVENT_SELECTED_OTHER;
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39651 switch (state_->selected_app()->type()) {
652 case PaymentApp::Type::AUTOFILL:
Sahel Sharify26884382019-05-07 16:23:51653 selected_event = JourneyLogger::Event::EVENT_SELECTED_CREDIT_CARD;
654 break;
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39655 case PaymentApp::Type::SERVICE_WORKER_APP: {
656 selected_event = IsGooglePaymentMethod(response->method_name)
657 ? JourneyLogger::Event::EVENT_SELECTED_GOOGLE
658 : JourneyLogger::Event::EVENT_SELECTED_OTHER;
Sahel Sharify26884382019-05-07 16:23:51659 break;
660 }
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39661 case PaymentApp::Type::NATIVE_MOBILE_APP:
Sahel Sharify26884382019-05-07 16:23:51662 NOTREACHED();
663 break;
664 }
665 journey_logger_.SetEventOccurred(selected_event);
666
Rouslan Solomakhin02d086ec2019-01-31 23:10:39667 // If currently interactive, show the processing spinner. Autofill payment
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39668 // apps request a CVC, so they are always interactive at this point. A payment
669 // handler may elect to be non-interactive by not showing a confirmation page
670 // to the user.
Rouslan Solomakhin02d086ec2019-01-31 23:10:39671 if (delegate_->IsInteractive())
672 delegate_->ShowProcessingSpinner();
673
mathpf1a7a3752017-03-15 11:23:37674 client_->OnPaymentResponse(std::move(response));
mathp4b85b582017-03-08 21:07:16675}
676
Rouslan Solomakhin68429b72019-06-27 15:12:39677void PaymentRequest::OnPaymentResponseError(const std::string& error_message) {
678 journey_logger_.SetEventOccurred(
679 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
680 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_INSTRUMENT_DETAILS_ERROR);
681
682 reject_show_error_message_ = error_message;
683 delegate_->ShowErrorMessage();
684 // When the user dismisses the error message, UserCancelled() will reject
685 // PaymentRequest.show() with |reject_show_error_message_|.
686}
687
mathp151bd312017-04-03 21:07:24688void PaymentRequest::OnShippingOptionIdSelected(
689 std::string shipping_option_id) {
690 client_->OnShippingOptionChange(shipping_option_id);
691}
692
693void PaymentRequest::OnShippingAddressSelected(
694 mojom::PaymentAddressPtr address) {
Sahel Sharify9d98a502019-09-30 19:58:39695 client_->OnShippingAddressChange(RedactShippingAddress(std::move(address)));
mathp151bd312017-04-03 21:07:24696}
697
Jinho Bangbb178152018-09-13 09:44:43698void PaymentRequest::OnPayerInfoSelected(mojom::PayerDetailPtr payer_info) {
699 client_->OnPayerDetailChange(std::move(payer_info));
700}
701
mathpf4bc50e2017-01-24 05:17:50702void PaymentRequest::UserCancelled() {
703 // If |client_| is not bound, then the object is already being destroyed as
704 // a result of a renderer event.
705 if (!client_.is_bound())
706 return;
707
sebsgfcdd13c2017-06-08 15:49:33708 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
sebsg20b49d7b2017-05-04 20:23:17709
mathpf4bc50e2017-01-24 05:17:50710 // This sends an error to the renderer, which informs the API user.
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03711 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
Rouslan Solomakhin68429b72019-06-27 15:12:39712 !reject_show_error_message_.empty()
713 ? reject_show_error_message_
714 : errors::kUserCancelled);
mathpf4bc50e2017-01-24 05:17:50715
716 // We close all bindings and ask to be destroyed.
717 client_.reset();
Gyuyoung Kim9cfbda32019-08-27 02:15:18718 receiver_.reset();
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02719 payment_handler_host_.Disconnect();
rouslanb28f4532017-05-08 15:41:47720 if (observer_for_testing_)
721 observer_for_testing_->OnConnectionTerminated();
mathpf4bc50e2017-01-24 05:17:50722 manager_->DestroyRequest(this);
mathpf709499d2017-01-09 20:48:36723}
724
sebsgd56b3e422017-10-20 18:08:08725void PaymentRequest::DidStartMainFrameNavigationToDifferentDocument(
726 bool is_user_initiated) {
sebsgfcdd13c2017-06-08 15:49:33727 RecordFirstAbortReason(is_user_initiated
728 ? JourneyLogger::ABORT_REASON_USER_NAVIGATION
729 : JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION);
sebsg2c8558a2017-05-17 18:54:10730}
731
mathpf4bc50e2017-01-24 05:17:50732void PaymentRequest::OnConnectionTerminated() {
733 // We are here because of a browser-side error, or likely as a result of the
Gyuyoung Kim9cfbda32019-08-27 02:15:18734 // disconnect_handler on |receiver_|, which can mean that the renderer
mathpf4bc50e2017-01-24 05:17:50735 // has decided to close the pipe for various reasons (see all uses of
736 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close
737 // the binding and the dialog, and ask to be deleted.
738 client_.reset();
Gyuyoung Kim9cfbda32019-08-27 02:15:18739 receiver_.reset();
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02740 payment_handler_host_.Disconnect();
mathpf4bc50e2017-01-24 05:17:50741 delegate_->CloseDialog();
rouslanb28f4532017-05-08 15:41:47742 if (observer_for_testing_)
743 observer_for_testing_->OnConnectionTerminated();
sebsgfcdd13c2017-06-08 15:49:33744
745 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_MOJO_CONNECTION_ERROR);
mathpf709499d2017-01-09 20:48:36746 manager_->DestroyRequest(this);
747}
748
mathpd4be8de82017-03-01 00:51:48749void PaymentRequest::Pay() {
mathp57c8c862017-06-16 20:15:45750 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39751 DCHECK(state_->selected_app());
Rouslan Solomakhin47f109e2020-05-20 15:45:36752 state_->selected_app()->SetPaymentHandlerHost(
753 payment_handler_host_.AsWeakPtr());
mathpf1a7a3752017-03-15 11:23:37754 state_->GeneratePaymentResponse();
mathpd4be8de82017-03-01 00:51:48755}
756
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07757void PaymentRequest::HideIfNecessary() {
758 display_handle_.reset();
759}
760
Ramin Halavatifbfcef672020-05-21 16:33:34761bool PaymentRequest::IsOffTheRecord() const {
762 return delegate_->IsOffTheRecord();
Anthony Vallee-Dubois10d131a2018-02-22 15:41:04763}
764
Sahel Sharify54f5e862020-03-26 20:02:41765void PaymentRequest::OnPaymentHandlerOpenWindowCalled() {
766 DCHECK(state_->selected_app());
767 // UKM for payment app origin should get recorded only when the origin of the
768 // invoked payment app is shown to the user.
769 journey_logger_.SetPaymentAppUkmSourceId(
770 state_->selected_app()->UkmSourceId());
771}
772
sebsgfcdd13c2017-06-08 15:49:33773void PaymentRequest::RecordFirstAbortReason(
774 JourneyLogger::AbortReason abort_reason) {
775 if (!has_recorded_completion_) {
776 has_recorded_completion_ = true;
777 journey_logger_.SetAborted(abort_reason);
sebsg2c8558a2017-05-17 18:54:10778 }
779}
780
Danyao Wang03a4cbd2019-08-15 23:47:11781void PaymentRequest::CanMakePaymentCallback(bool can_make_payment) {
782 client_->OnCanMakePayment(
783 can_make_payment ? mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT
784 : mojom::CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT);
Danyao Wang4bc0606a2018-12-27 16:54:53785
Danyao Wang57aa0442019-01-31 04:06:41786 journey_logger_.SetCanMakePaymentValue(can_make_payment);
Rouslan Solomakhin1804ee42017-10-03 14:27:43787
788 if (observer_for_testing_)
789 observer_for_testing_->OnCanMakePaymentReturned();
790}
791
Danyao Wangce175bf2018-12-21 22:35:58792void PaymentRequest::HasEnrolledInstrumentCallback(
Rouslan Solomakhin5683eb282019-01-29 18:06:03793 bool per_method_quota,
Danyao Wangce175bf2018-12-21 22:35:58794 bool has_enrolled_instrument) {
Rouslan Solomakhinb26faa072019-08-19 14:42:28795 if (!spec_ || CanMakePaymentQueryFactory::GetInstance()
796 ->GetForContext(web_contents_->GetBrowserContext())
797 ->CanQuery(top_level_origin_, frame_origin_,
798 spec_->query_for_quota(), per_method_quota)) {
Danyao Wangce175bf2018-12-21 22:35:58799 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58800 /*warn_local_development=*/false);
801 } else if (UrlUtil::IsLocalDevelopmentUrl(frame_origin_)) {
Danyao Wangce175bf2018-12-21 22:35:58802 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58803 /*warn_local_development=*/true);
Danyao Wangce175bf2018-12-21 22:35:58804 } else {
805 client_->OnHasEnrolledInstrument(
806 HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED);
807 }
808
809 if (observer_for_testing_)
810 observer_for_testing_->OnHasEnrolledInstrumentReturned();
811}
812
Danyao Wangce175bf2018-12-21 22:35:58813void PaymentRequest::RespondToHasEnrolledInstrumentQuery(
814 bool has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58815 bool warn_local_development) {
Danyao Wangce175bf2018-12-21 22:35:58816 HasEnrolledInstrumentQueryResult positive =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58817 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58818 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_ENROLLED_INSTRUMENT
819 : HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT;
820 HasEnrolledInstrumentQueryResult negative =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58821 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58822 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_NO_ENROLLED_INSTRUMENT
823 : HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT;
824
825 client_->OnHasEnrolledInstrument(has_enrolled_instrument ? positive
826 : negative);
Danyao Wang57aa0442019-01-31 04:06:41827 journey_logger_.SetHasEnrolledInstrumentValue(has_enrolled_instrument);
Danyao Wangce175bf2018-12-21 22:35:58828}
829
Rouslan Solomakhin13947ae32020-05-22 15:34:29830void PaymentRequest::OnAbortResult(bool aborted) {
831 if (client_.is_bound())
832 client_->OnAbort(aborted);
833
834 if (aborted) {
835 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_MERCHANT);
836 state_->OnAbort();
837 }
838}
839
mathpf709499d2017-01-09 20:48:36840} // namespace payments