blob: 1eee7356f53206856029118a1cc88e4dcc78f9b8 [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"
Rouslan Solomakhin97238b22020-05-28 00:28:3518#include "components/payments/content/payment_details_converter.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2219#include "components/payments/content/payment_request_converter.h"
rouslan908248c2017-02-27 21:30:2420#include "components/payments/content/payment_request_web_contents_manager.h"
rouslan690997682017-05-09 18:07:3921#include "components/payments/core/can_make_payment_query.h"
Rouslan Solomakhin2dc99c32020-03-24 21:25:0322#include "components/payments/core/error_message_util.h"
Rouslan Solomakhina480efa2019-05-06 15:37:2223#include "components/payments/core/error_strings.h"
Anthony Vallee-Dubois968ae4d2018-03-15 16:56:3624#include "components/payments/core/features.h"
Rouslan Solomakhin85b10da2019-11-05 20:03:1725#include "components/payments/core/method_strings.h"
Rouslan Solomakhineb06b272019-07-10 16:23:3226#include "components/payments/core/native_error_strings.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2227#include "components/payments/core/payment_details.h"
28#include "components/payments/core/payment_details_validation.h"
anthonyvd6a43b932017-05-11 18:39:2729#include "components/payments/core/payment_prefs.h"
Danyao Wang50ccb9f2019-05-09 23:28:0330#include "components/payments/core/payments_experimental_features.h"
Jinho Bangbe463a22018-08-02 10:26:5031#include "components/payments/core/payments_validators.h"
Rouslan Solomakhin77a7e1a2019-05-23 17:37:5832#include "components/payments/core/url_util.h"
anthonyvd6a43b932017-05-11 18:39:2733#include "components/prefs/pref_service.h"
Steven Holte2083e8bc2018-07-16 23:50:3634#include "components/ukm/content/source_url_recorder.h"
Rouslan Solomakhin6e979ab2017-08-30 17:30:3935#include "components/url_formatter/elide_url.h"
mathpf709499d2017-01-09 20:48:3636#include "content/public/browser/browser_thread.h"
rouslan690997682017-05-09 18:07:3937#include "content/public/browser/render_frame_host.h"
Danyao Wang15840792020-05-07 23:52:1738#include "content/public/browser/render_process_host.h"
mathpf709499d2017-01-09 20:48:3639#include "content/public/browser/web_contents.h"
Rouslan Solomakhin1804ee42017-10-03 14:27:4340#include "content/public/common/content_features.h"
Rouslan Solomakhin77a7e1a2019-05-23 17:37:5841#include "content/public/common/origin_util.h"
Sahel Sharify54f5e862020-03-26 20:02:4142#include "services/metrics/public/cpp/ukm_source_id.h"
mathpf709499d2017-01-09 20:48:3643
Rouslan Solomakhina480efa2019-05-06 15:37:2244namespace payments {
Danyao Wangce175bf2018-12-21 22:35:5845namespace {
46
Danyao Wang57aa0442019-01-31 04:06:4147using ::payments::mojom::CanMakePaymentQueryResult;
Danyao Wangce175bf2018-12-21 22:35:5848using ::payments::mojom::HasEnrolledInstrumentQueryResult;
49
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:3950bool IsGooglePaymentMethod(const std::string& method_name) {
Rouslan Solomakhin85b10da2019-11-05 20:03:1751 return method_name == methods::kGooglePay ||
52 method_name == methods::kAndroidPay;
Sahel Sharify26884382019-05-07 16:23:5153}
54
Sahel Sharify9d98a502019-09-30 19:58:3955// Redact shipping address before exposing it in ShippingAddressChangeEvent.
56// https://w3c.github.io/payment-request/#shipping-address-changed-algorithm
57mojom::PaymentAddressPtr RedactShippingAddress(
58 mojom::PaymentAddressPtr address) {
59 DCHECK(address);
60 if (!PaymentsExperimentalFeatures::IsEnabled(
61 features::kWebPaymentsRedactShippingAddress)) {
62 return address;
63 }
64 address->organization.clear();
65 address->phone.clear();
66 address->recipient.clear();
67 address->address_line.clear();
68 return address;
69}
70
Rouslan Solomakhina480efa2019-05-06 15:37:2271} // namespace
mathpf709499d2017-01-09 20:48:3672
73PaymentRequest::PaymentRequest(
rouslan690997682017-05-09 18:07:3974 content::RenderFrameHost* render_frame_host,
mathpf709499d2017-01-09 20:48:3675 content::WebContents* web_contents,
Rouslan Solomakhin4eea9bc22017-10-10 15:18:5176 std::unique_ptr<ContentPaymentRequestDelegate> delegate,
mathpf709499d2017-01-09 20:48:3677 PaymentRequestWebContentsManager* manager,
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0778 PaymentRequestDisplayManager* display_manager,
Gyuyoung Kim9cfbda32019-08-27 02:15:1879 mojo::PendingReceiver<mojom::PaymentRequest> receiver,
mathp300fa542017-03-27 19:29:3780 ObserverForTest* observer_for_testing)
mathpf709499d2017-01-09 20:48:3681 : web_contents_(web_contents),
Danyao Wang15840792020-05-07 23:52:1782 // TODO(crbug.com/1058840): change to WeakPtr<RenderFrameHost> once
83 // RenderFrameHost provides a WeakPtr API.
84 initiator_frame_routing_id_(content::GlobalFrameRoutingId(
85 render_frame_host->GetProcess()->GetID(),
86 render_frame_host->GetRoutingID())),
Rouslan Solomakhin27064702018-12-14 21:15:3387 log_(web_contents_),
mathpf709499d2017-01-09 20:48:3688 delegate_(std::move(delegate)),
89 manager_(manager),
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0790 display_manager_(display_manager),
91 display_handle_(nullptr),
Rouslan Solomakhin1f95f092019-08-09 12:28:5192 payment_handler_host_(web_contents_, this),
Rouslan Solomakhin6e979ab2017-08-30 17:30:3993 top_level_origin_(url_formatter::FormatUrlForSecurityDisplay(
94 web_contents_->GetLastCommittedURL())),
95 frame_origin_(url_formatter::FormatUrlForSecurityDisplay(
96 render_frame_host->GetLastCommittedURL())),
Rouslan Solomakhinc71cca622020-01-31 22:15:3597 frame_security_origin_(render_frame_host->GetLastCommittedOrigin()),
sebsg20b49d7b2017-05-04 20:23:1798 observer_for_testing_(observer_for_testing),
Ramin Halavatifbfcef672020-05-21 16:33:3499 journey_logger_(delegate_->IsOffTheRecord(),
Jeremy Roman5c341f6d2019-07-15 15:56:10100 ukm::GetSourceIdForWebContentsDocument(web_contents)) {
Gyuyoung Kim9cfbda32019-08-27 02:15:18101 receiver_.Bind(std::move(receiver));
mathpf4bc50e2017-01-24 05:17:50102 // OnConnectionTerminated will be called when the Mojo pipe is closed. This
103 // will happen as a result of many renderer-side events (both successful and
104 // erroneous in nature).
105 // TODO(crbug.com/683636): Investigate using
106 // set_connection_error_with_reason_handler with Binding::CloseWithReason.
Gyuyoung Kim9cfbda32019-08-27 02:15:18107 receiver_.set_disconnect_handler(base::BindOnce(
Anthony Vallee-Duboisdc1dbf1a2017-07-17 15:01:13108 &PaymentRequest::OnConnectionTerminated, weak_ptr_factory_.GetWeakPtr()));
mathpf709499d2017-01-09 20:48:36109}
110
111PaymentRequest::~PaymentRequest() {}
112
Gyuyoung Kim9cfbda32019-08-27 02:15:18113void PaymentRequest::Init(
114 mojo::PendingRemote<mojom::PaymentRequestClient> client,
115 std::vector<mojom::PaymentMethodDataPtr> method_data,
116 mojom::PaymentDetailsPtr details,
117 mojom::PaymentOptionsPtr options) {
mathpf709499d2017-01-09 20:48:36118 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin27064702018-12-14 21:15:33119
120 if (is_initialized_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22121 log_.Error(errors::kAttemptedInitializationTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33122 OnConnectionTerminated();
123 return;
124 }
125
Sahel Sharify9306fe92020-08-18 20:04:58126 journey_logger_.RecordCheckoutStep(
127 JourneyLogger::CheckoutFunnelStep::kInitiated);
Rouslan Solomakhin27064702018-12-14 21:15:33128 is_initialized_ = true;
Gyuyoung Kim9cfbda32019-08-27 02:15:18129 client_.Bind(std::move(client));
rouslan6e3cf7c62017-04-17 21:23:28130
rouslanb28f4532017-05-08 15:41:47131 const GURL last_committed_url = delegate_->GetLastCommittedURL();
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58132 if (!content::IsOriginSecure(last_committed_url)) {
Rouslan Solomakhina480efa2019-05-06 15:37:22133 log_.Error(errors::kNotInASecureOrigin);
rouslan6e3cf7c62017-04-17 21:23:28134 OnConnectionTerminated();
135 return;
136 }
137
Rouslan Solomakhin55db8272019-06-25 18:16:28138 // TODO(crbug.com/978471): Improve architecture for handling prohibited
139 // origins and invalid SSL certificates.
rouslanb28f4532017-05-08 15:41:47140 bool allowed_origin =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58141 UrlUtil::IsOriginAllowedToUseWebPaymentApis(last_committed_url);
rouslanb28f4532017-05-08 15:41:47142 if (!allowed_origin) {
Rouslan Solomakhin68429b72019-06-27 15:12:39143 reject_show_error_message_ = errors::kProhibitedOrigin;
rouslanb28f4532017-05-08 15:41:47144 }
145
Rouslan Solomakhin55db8272019-06-25 18:16:28146 bool invalid_ssl = false;
147 if (last_committed_url.SchemeIsCryptographic()) {
Rouslan Solomakhin68429b72019-06-27 15:12:39148 DCHECK(reject_show_error_message_.empty());
149 reject_show_error_message_ =
Rouslan Solomakhin55db8272019-06-25 18:16:28150 delegate_->GetInvalidSslCertificateErrorMessage();
Rouslan Solomakhin68429b72019-06-27 15:12:39151 invalid_ssl = !reject_show_error_message_.empty();
Rouslan Solomakhin27064702018-12-14 21:15:33152 }
rouslanb28f4532017-05-08 15:41:47153
154 if (!allowed_origin || invalid_ssl) {
Rouslan Solomakhin27064702018-12-14 21:15:33155 // Intentionally don't set |spec_| and |state_|, so the UI is never shown.
Rouslan Solomakhin68429b72019-06-27 15:12:39156 log_.Error(reject_show_error_message_);
Rouslan Solomakhina480efa2019-05-06 15:37:22157 log_.Error(errors::kProhibitedOriginOrInvalidSslExplanation);
rouslan6e3cf7c62017-04-17 21:23:28158 return;
159 }
160
mathpf709499d2017-01-09 20:48:36161 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22162 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33163 log_.Error(error);
mathpf4bc50e2017-01-24 05:17:50164 OnConnectionTerminated();
mathpf709499d2017-01-09 20:48:36165 return;
166 }
rouslan6e3cf7c62017-04-17 21:23:28167
jinho.bangfcb5ec92017-03-29 08:08:02168 if (!details->total) {
Rouslan Solomakhina480efa2019-05-06 15:37:22169 log_.Error(errors::kTotalRequired);
jinho.bangfcb5ec92017-03-29 08:08:02170 OnConnectionTerminated();
171 return;
172 }
rouslan6e3cf7c62017-04-17 21:23:28173
Danyao Wang15840792020-05-07 23:52:17174 // TODO(crbug.com/1058840): change to WeakPtr<RenderFrameHost> once
175 // RenderFrameHost provides a WeakPtr API.
176 content::RenderFrameHost* initiator_frame =
177 content::RenderFrameHost::FromID(initiator_frame_routing_id_);
178 if (!initiator_frame) {
179 log_.Error(errors::kInvalidInitiatorFrame);
180 OnConnectionTerminated();
181 return;
182 }
183
sebsga70a6da2017-12-21 22:27:02184 spec_ = std::make_unique<PaymentRequestSpec>(
Rouslan Solomakhin1f95f092019-08-09 12:28:51185 std::move(options), std::move(details), std::move(method_data),
186 /*observer=*/this, delegate_->GetApplicationLocale());
sebsga70a6da2017-12-21 22:27:02187 state_ = std::make_unique<PaymentRequestState>(
Danyao Wang15840792020-05-07 23:52:17188 web_contents_, initiator_frame, top_level_origin_, frame_origin_,
189 frame_security_origin_, spec_.get(),
Rouslan Solomakhin1f95f092019-08-09 12:28:51190 /*delegate=*/this, delegate_->GetApplicationLocale(),
Rouslan Solomakhin13947ae32020-05-22 15:34:29191 delegate_->GetPersonalDataManager(), delegate_.get(), &journey_logger_);
Mathieu Perreault627b97c2017-08-12 00:44:22192
193 journey_logger_.SetRequestedInformation(
194 spec_->request_shipping(), spec_->request_payer_email(),
195 spec_->request_payer_phone(), spec_->request_payer_name());
196
197 // Log metrics around which payment methods are requested by the merchant.
Rouslan Solomakhin85b10da2019-11-05 20:03:17198 GURL google_pay_url(methods::kGooglePay);
199 GURL android_pay_url(methods::kAndroidPay);
Mathieu Perreault627b97c2017-08-12 00:44:22200 // Looking for payment methods that are NOT google-related payment methods.
201 auto non_google_it =
202 std::find_if(spec_->url_payment_method_identifiers().begin(),
203 spec_->url_payment_method_identifiers().end(),
204 [google_pay_url, android_pay_url](const GURL& url) {
205 return url != google_pay_url && url != android_pay_url;
206 });
207 journey_logger_.SetRequestedPaymentMethodTypes(
208 /*requested_basic_card=*/!spec_->supported_card_networks().empty(),
209 /*requested_method_google=*/
Jan Wilken Dörrie45d34f42019-06-08 09:40:54210 base::Contains(spec_->url_payment_method_identifiers(), google_pay_url) ||
211 base::Contains(spec_->url_payment_method_identifiers(),
212 android_pay_url),
Mathieu Perreault627b97c2017-08-12 00:44:22213 /*requested_method_other=*/non_google_it !=
214 spec_->url_payment_method_identifiers().end());
Rouslan Solomakhin1f95f092019-08-09 12:28:51215
216 payment_handler_host_.set_payment_request_id_for_logs(*spec_->details().id);
mathpf709499d2017-01-09 20:48:36217}
218
Rouslan Solomakhin9788d4b2019-04-09 13:10:23219void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
Rouslan Solomakhin27064702018-12-14 21:15:33220 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22221 log_.Error(errors::kCannotShowWithoutInit);
mathpf4bc50e2017-01-24 05:17:50222 OnConnectionTerminated();
tmartino8ce922852017-01-09 22:23:10223 return;
224 }
rouslan6e3cf7c62017-04-17 21:23:28225
Rouslan Solomakhin27064702018-12-14 21:15:33226 if (is_show_called_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22227 log_.Error(errors::kCannotShowTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33228 OnConnectionTerminated();
229 return;
230 }
231
Sahel Sharify9306fe92020-08-18 20:04:58232 journey_logger_.RecordCheckoutStep(
233 JourneyLogger::CheckoutFunnelStep::kShowCalled);
Rouslan Solomakhin27064702018-12-14 21:15:33234 is_show_called_ = true;
Sahel Sharify0fadf4da2019-08-09 14:55:58235 journey_logger_.SetTriggerTime();
Rouslan Solomakhin27064702018-12-14 21:15:33236
rouslan7d433cc22017-05-08 15:18:07237 // A tab can display only one PaymentRequest UI at a time.
Anthony Vallee-Dubois8f5e7e12018-01-12 16:14:06238 display_handle_ = display_manager_->TryShow(delegate_.get());
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07239 if (!display_handle_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22240 log_.Error(errors::kAnotherUiShowing);
Sahel Sharifya50fc4c2019-05-28 14:53:22241 DCHECK(!has_recorded_completion_);
242 has_recorded_completion_ = true;
sebsg828269bc2017-06-09 19:11:12243 journey_logger_.SetNotShown(
244 JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03245 client_->OnError(mojom::PaymentErrorReason::ALREADY_SHOWING,
246 errors::kAnotherUiShowing);
rouslan7d433cc22017-05-08 15:18:07247 OnConnectionTerminated();
248 return;
249 }
250
Rouslan Solomakhin5b510432017-09-26 16:59:32251 if (!delegate_->IsBrowserWindowActive()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22252 log_.Error(errors::kCannotShowInBackgroundTab);
Sahel Sharifya50fc4c2019-05-28 14:53:22253 DCHECK(!has_recorded_completion_);
254 has_recorded_completion_ = true;
Rouslan Solomakhin5b510432017-09-26 16:59:32255 journey_logger_.SetNotShown(JourneyLogger::NOT_SHOWN_REASON_OTHER);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03256 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
257 errors::kCannotShowInBackgroundTab);
Rouslan Solomakhin5b510432017-09-26 16:59:32258 OnConnectionTerminated();
259 return;
260 }
261
Rouslan Solomakhind2cae95a2018-08-09 00:16:10262 if (!state_) {
Rouslan Solomakhin27064702018-12-14 21:15:33263 // SSL is not valid. Reject show with NotSupportedError, disconnect the
264 // mojo pipe, and destroy this object.
Rouslan Solomakhind5dcc322019-07-11 21:47:20265 AreRequestedMethodsSupportedCallback(false, reject_show_error_message_);
Rouslan Solomakhind2cae95a2018-08-09 00:16:10266 return;
267 }
268
Rouslan Solomakhin833f8512018-04-03 23:19:25269 is_show_user_gesture_ = is_user_gesture;
270
Rouslan Solomakhin9788d4b2019-04-09 13:10:23271 if (wait_for_updated_details) {
272 // Put |spec_| into uninitialized state, so the UI knows to show a spinner.
273 // This method does not block.
274 spec_->StartWaitingForUpdateWith(
275 PaymentRequestSpec::UpdateReason::INITIAL_PAYMENT_DETAILS);
Sahel Sharify98a2c2a2019-07-12 18:57:40276 } else {
277 DCHECK(spec_->details().total);
278 journey_logger_.RecordTransactionAmount(
279 spec_->details().total->amount->currency,
280 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23281 }
Takashi Sakamoto48a29702019-04-08 05:06:32282
Rouslan Solomakhin9788d4b2019-04-09 13:10:23283 display_handle_->Show(this);
Rouslan Solomakhin1dca2a922019-09-06 22:25:07284
285 state_->set_is_show_user_gesture(is_show_user_gesture_);
gogerald0a7ee6c2017-11-13 18:23:19286 state_->AreRequestedMethodsSupported(
287 base::BindOnce(&PaymentRequest::AreRequestedMethodsSupportedCallback,
288 weak_ptr_factory_.GetWeakPtr()));
289}
290
Jinho Bangbe463a22018-08-02 10:26:50291void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
Rouslan Solomakhin27064702018-12-14 21:15:33292 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22293 log_.Error(errors::kCannotRetryWithoutInit);
Jinho Bangcac8d9a02018-08-23 19:47:22294 OnConnectionTerminated();
295 return;
296 }
297
Rouslan Solomakhin27064702018-12-14 21:15:33298 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22299 log_.Error(errors::kCannotRetryWithoutShow);
Jinho Bangcac8d9a02018-08-23 19:47:22300 OnConnectionTerminated();
301 return;
302 }
303
Jinho Bangbe463a22018-08-02 10:26:50304 std::string error;
305 if (!PaymentsValidators::IsValidPaymentValidationErrorsFormat(errors,
306 &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33307 log_.Error(error);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03308 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL, error);
Jinho Bangbe463a22018-08-02 10:26:50309 OnConnectionTerminated();
310 return;
311 }
312
Sahel Sharifycf4e2132020-01-24 23:59:08313 state()->SetAvailablePaymentAppForRetry();
Jinho Bang092e7162018-09-06 23:41:19314 spec()->Retry(std::move(errors));
Jinho Bangcac8d9a02018-08-23 19:47:22315 display_handle_->Retry();
Jinho Bangbe463a22018-08-02 10:26:50316}
317
mathp151bd312017-04-03 21:07:24318void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
Rouslan Solomakhin27064702018-12-14 21:15:33319 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22320 log_.Error(errors::kCannotUpdateWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33321 OnConnectionTerminated();
322 return;
323 }
324
325 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22326 log_.Error(errors::kCannotUpdateWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33327 OnConnectionTerminated();
328 return;
329 }
330
mathp151bd312017-04-03 21:07:24331 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22332 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33333 log_.Error(error);
mathp151bd312017-04-03 21:07:24334 OnConnectionTerminated();
335 return;
336 }
Rouslan Solomakhin4cbda822017-08-23 18:50:39337
Jinho Bang092e7162018-09-06 23:41:19338 if (details->shipping_address_errors &&
339 !PaymentsValidators::IsValidAddressErrorsFormat(
340 details->shipping_address_errors, &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33341 log_.Error(error);
Jinho Bang092e7162018-09-06 23:41:19342 OnConnectionTerminated();
343 return;
344 }
345
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39346 if (state()->selected_app() && state()->IsPaymentAppInvoked() &&
Rouslan Solomakhin218f7072020-05-21 14:26:47347 state()->selected_app()->IsWaitingForPaymentDetailsUpdate()) {
Rouslan Solomakhin97238b22020-05-28 00:28:35348 state()->selected_app()->UpdateWith(
349 PaymentDetailsConverter::ConvertToPaymentRequestDetailsUpdate(
350 details, state()->selected_app()->HandlesShippingAddress(),
351 base::BindRepeating(&PaymentApp::IsValidForPaymentMethodIdentifier,
352 state()->selected_app()->AsWeakPtr())));
Rouslan Solomakhina480efa2019-05-06 15:37:22353 }
354
Rouslan Solomakhin6ba46fd2019-04-11 23:44:01355 bool is_resolving_promise_passed_into_show_method = !spec_->IsInitialized();
356
mathp151bd312017-04-03 21:07:24357 spec_->UpdateWith(std::move(details));
Rouslan Solomakhin9788d4b2019-04-09 13:10:23358
359 if (is_resolving_promise_passed_into_show_method) {
Sahel Sharify98a2c2a2019-07-12 18:57:40360 DCHECK(spec_->details().total);
361 journey_logger_.RecordTransactionAmount(
362 spec_->details().total->amount->currency,
363 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23364 if (SatisfiesSkipUIConstraints()) {
Rouslan Solomakhin9788d4b2019-04-09 13:10:23365 Pay();
366 } else if (spec_->request_shipping()) {
367 state_->SelectDefaultShippingAddressAndNotifyObservers();
368 }
369 }
mathp151bd312017-04-03 21:07:24370}
371
Sahel Sharifye2f889542019-12-02 22:17:48372void PaymentRequest::OnPaymentDetailsNotUpdated() {
Rouslan Solomakhin27064702018-12-14 21:15:33373 // This Mojo call is triggered by the user of the API doing nothing in
374 // response to a shipping address update event, so the error messages cannot
375 // be more verbose.
376 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22377 log_.Error(errors::kNotInitialized);
Rouslan Solomakhin27064702018-12-14 21:15:33378 OnConnectionTerminated();
379 return;
380 }
381
382 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22383 log_.Error(errors::kNotShown);
Rouslan Solomakhin27064702018-12-14 21:15:33384 OnConnectionTerminated();
385 return;
386 }
387
Rouslan Solomakhina9ff9282017-10-31 21:58:05388 spec_->RecomputeSpecForDetails();
Rouslan Solomakhina480efa2019-05-06 15:37:22389
Rouslan Solomakhin218f7072020-05-21 14:26:47390 if (state()->IsPaymentAppInvoked() && state()->selected_app() &&
391 state()->selected_app()->IsWaitingForPaymentDetailsUpdate()) {
Rouslan Solomakhin2039a342020-05-21 19:21:04392 state()->selected_app()->OnPaymentDetailsNotUpdated();
Rouslan Solomakhincf9093f2019-05-20 15:32:17393 }
Rouslan Solomakhina9ff9282017-10-31 21:58:05394}
395
mathpf4bc50e2017-01-24 05:17:50396void PaymentRequest::Abort() {
Rouslan Solomakhin27064702018-12-14 21:15:33397 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22398 log_.Error(errors::kCannotAbortWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33399 OnConnectionTerminated();
400 return;
401 }
402
403 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22404 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33405 OnConnectionTerminated();
406 return;
407 }
408
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56409 // The API user has decided to abort. If a successful abort message is
410 // returned to the renderer, the Mojo message pipe is closed, which triggers
mathpf4bc50e2017-01-24 05:17:50411 // PaymentRequest::OnConnectionTerminated, which destroys this object.
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56412 // Otherwise, the abort promise is rejected and the pipe is not closed.
413 // The abort is only successful if the payment app wasn't yet invoked.
414 // TODO(crbug.com/716546): Add a merchant abort metric
415
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56416 if (observer_for_testing_)
417 observer_for_testing_->OnAbortCalled();
Sahel Sharifyd3418222020-02-19 21:50:02418
Rouslan Solomakhin13947ae32020-05-22 15:34:29419 if (!state_->IsPaymentAppInvoked() || !state_->selected_app()) {
420 OnAbortResult(/*aborted=*/true);
421 return;
422 }
423
424 state_->selected_app()->AbortPaymentApp(base::BindOnce(
425 &PaymentRequest::OnAbortResult, weak_ptr_factory_.GetWeakPtr()));
mathpf4bc50e2017-01-24 05:17:50426}
427
mathp218795892017-03-29 15:15:34428void PaymentRequest::Complete(mojom::PaymentComplete result) {
Rouslan Solomakhin27064702018-12-14 21:15:33429 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22430 log_.Error(errors::kCannotCompleteWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33431 OnConnectionTerminated();
mathp4b85b582017-03-08 21:07:16432 return;
Rouslan Solomakhin27064702018-12-14 21:15:33433 }
434
435 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22436 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33437 OnConnectionTerminated();
438 return;
439 }
mathp4b85b582017-03-08 21:07:16440
Sahel Sharify2a56b5f2019-11-28 19:27:19441 if (observer_for_testing_) {
442 observer_for_testing_->OnCompleteCalled();
443 }
444
Rouslan Solomakhine3473192017-06-16 14:54:57445 // Failed transactions show an error. Successful and unknown-state
446 // transactions don't show an error.
447 if (result == mojom::PaymentComplete::FAIL) {
mathp218795892017-03-29 15:15:34448 delegate_->ShowErrorMessage();
449 } else {
sebsgfcdd13c2017-06-08 15:49:33450 DCHECK(!has_recorded_completion_);
sebsgf8272a22017-05-26 14:32:58451 journey_logger_.SetCompleted();
sebsgfcdd13c2017-06-08 15:49:33452 has_recorded_completion_ = true;
Sahel Sharify98a2c2a2019-07-12 18:57:40453 DCHECK(spec_->details().total);
454 journey_logger_.RecordTransactionAmount(
455 spec_->details().total->amount->currency,
456 spec_->details().total->amount->value, true /*completed*/);
sebsgfcdd13c2017-06-08 15:49:33457
anthonyvd6a43b932017-05-11 18:39:27458 delegate_->GetPrefService()->SetBoolean(kPaymentsFirstTransactionCompleted,
459 true);
mathp218795892017-03-29 15:15:34460 // When the renderer closes the connection,
461 // PaymentRequest::OnConnectionTerminated will be called.
462 client_->OnComplete();
sebsg8a93b272017-05-11 19:30:22463 state_->RecordUseStats();
mathp218795892017-03-29 15:15:34464 }
mathp4b85b582017-03-08 21:07:16465}
466
Danyao Wang03a4cbd2019-08-15 23:47:11467void PaymentRequest::CanMakePayment() {
Rouslan Solomakhin27064702018-12-14 21:15:33468 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22469 log_.Error(errors::kCannotCallCanMakePaymentWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33470 OnConnectionTerminated();
471 return;
472 }
473
474 // It's valid to call canMakePayment() without calling show() first.
475
gogerald8189d522017-09-15 17:52:18476 if (observer_for_testing_)
477 observer_for_testing_->OnCanMakePaymentCalled();
Mathieu Perreaultcacb85e2018-06-06 20:40:13478
Rouslan Solomakhind2cae95a2018-08-09 00:16:10479 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
480 !state_) {
Danyao Wang03a4cbd2019-08-15 23:47:11481 CanMakePaymentCallback(/*can_make_payment=*/false);
Mathieu Perreaultcacb85e2018-06-06 20:40:13482 } else {
Rouslan Solomakhind2cae95a2018-08-09 00:16:10483 state_->CanMakePayment(
Mathieu Perreaultcacb85e2018-06-06 20:40:13484 base::BindOnce(&PaymentRequest::CanMakePaymentCallback,
Danyao Wang03a4cbd2019-08-15 23:47:11485 weak_ptr_factory_.GetWeakPtr()));
Mathieu Perreaultcacb85e2018-06-06 20:40:13486 }
gogerald8189d522017-09-15 17:52:18487}
488
Dongjun Kim0a7565b2020-08-18 09:39:46489void PaymentRequest::HasEnrolledInstrument() {
Danyao Wangce175bf2018-12-21 22:35:58490 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22491 log_.Error(errors::kCannotCallHasEnrolledInstrumentWithoutInit);
Danyao Wangce175bf2018-12-21 22:35:58492 OnConnectionTerminated();
493 return;
494 }
495
496 // It's valid to call hasEnrolledInstrument() without calling show() first.
497
498 if (observer_for_testing_)
499 observer_for_testing_->OnHasEnrolledInstrumentCalled();
500
501 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
502 !state_) {
Dongjun Kim0a7565b2020-08-18 09:39:46503 HasEnrolledInstrumentCallback(/*has_enrolled_instrument=*/false);
Danyao Wangce175bf2018-12-21 22:35:58504 } else {
505 state_->HasEnrolledInstrument(
506 base::BindOnce(&PaymentRequest::HasEnrolledInstrumentCallback,
Dongjun Kim0a7565b2020-08-18 09:39:46507 weak_ptr_factory_.GetWeakPtr()));
Danyao Wangce175bf2018-12-21 22:35:58508 }
509}
510
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02511bool PaymentRequest::ChangePaymentMethod(const std::string& method_name,
512 const std::string& stringified_data) {
Rouslan Solomakhina480efa2019-05-06 15:37:22513 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02514 DCHECK(!method_name.empty());
Rouslan Solomakhina480efa2019-05-06 15:37:22515
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02516 if (!state_ || !state_->IsPaymentAppInvoked() || !client_)
517 return false;
Rouslan Solomakhina480efa2019-05-06 15:37:22518
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02519 client_->OnPaymentMethodChange(method_name, stringified_data);
520 return true;
Rouslan Solomakhina480efa2019-05-06 15:37:22521}
522
Sahel Sharify9d98a502019-09-30 19:58:39523bool PaymentRequest::ChangeShippingOption(
524 const std::string& shipping_option_id) {
525 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
526 DCHECK(!shipping_option_id.empty());
527
528 bool is_valid_id = false;
529 if (spec_->details().shipping_options) {
530 for (const auto& option : spec_->GetShippingOptions()) {
531 if (option->id == shipping_option_id) {
532 is_valid_id = true;
533 break;
534 }
535 }
536 }
537
538 if (!state_ || !state_->IsPaymentAppInvoked() || !client_ || !spec_ ||
539 !spec_->request_shipping() || !is_valid_id) {
540 return false;
541 }
542
543 client_->OnShippingOptionChange(shipping_option_id);
544 return true;
545}
546
547bool PaymentRequest::ChangeShippingAddress(
548 mojom::PaymentAddressPtr shipping_address) {
549 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
550 DCHECK(shipping_address);
551
552 if (!state_ || !state_->IsPaymentAppInvoked() || !client_ || !spec_ ||
553 !spec_->request_shipping()) {
554 return false;
555 }
556
557 client_->OnShippingAddressChange(
558 RedactShippingAddress(std::move(shipping_address)));
559 return true;
560}
561
Rouslan Solomakhin27064702018-12-14 21:15:33562void PaymentRequest::AreRequestedMethodsSupportedCallback(
Rouslan Solomakhind5dcc322019-07-11 21:47:20563 bool methods_supported,
564 const std::string& error_message) {
Danyao Wang25f72dc2019-10-18 05:11:32565 if (is_show_called_ && observer_for_testing_)
Sahel Sharifyfd677ff2020-07-08 19:01:52566 observer_for_testing_->OnAppListReady(weak_ptr_factory_.GetWeakPtr());
Danyao Wang25f72dc2019-10-18 05:11:32567
Rouslan Solomakhin27064702018-12-14 21:15:33568 if (methods_supported) {
Sahel Sharifyd3f1bc82019-05-21 18:48:46569 if (SatisfiesSkipUIConstraints())
Rouslan Solomakhin27064702018-12-14 21:15:33570 Pay();
Rouslan Solomakhin27064702018-12-14 21:15:33571 } else {
Sahel Sharifya50fc4c2019-05-28 14:53:22572 DCHECK(!has_recorded_completion_);
573 has_recorded_completion_ = true;
Rouslan Solomakhin27064702018-12-14 21:15:33574 journey_logger_.SetNotShown(
575 JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03576 client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED,
Rouslan Solomakhin2dc99c32020-03-24 21:25:03577 GetNotSupportedErrorMessage(
578 spec_ ? spec_->payment_method_identifiers_set()
579 : std::set<std::string>()) +
Rouslan Solomakhind5dcc322019-07-11 21:47:20580 (error_message.empty() ? "" : " " + error_message));
Rouslan Solomakhin27064702018-12-14 21:15:33581 if (observer_for_testing_)
582 observer_for_testing_->OnNotSupportedError();
583 OnConnectionTerminated();
584 }
585}
586
Rouslan Solomakhine37ecbd02020-07-29 20:35:42587base::WeakPtr<PaymentRequest> PaymentRequest::GetWeakPtr() {
588 return weak_ptr_factory_.GetWeakPtr();
589}
590
Rouslan Solomakhin27064702018-12-14 21:15:33591bool PaymentRequest::IsInitialized() const {
592 return is_initialized_ && client_ && client_.is_bound() &&
Gyuyoung Kim9cfbda32019-08-27 02:15:18593 receiver_.is_bound();
Rouslan Solomakhin27064702018-12-14 21:15:33594}
595
596bool PaymentRequest::IsThisPaymentRequestShowing() const {
597 return is_show_called_ && display_handle_ && spec_ && state_;
598}
599
Sahel Sharify9120fd32019-12-05 17:16:11600bool PaymentRequest::OnlySingleAppCanProvideAllRequiredInformation() const {
601 DCHECK(state()->IsInitialized());
602 DCHECK(spec()->IsInitialized());
603
604 if (!spec()->request_shipping() && !spec()->request_payer_name() &&
605 !spec()->request_payer_phone() && !spec()->request_payer_email()) {
606 return state()->available_apps().size() == 1 &&
607 state()->available_apps().at(0)->type() !=
608 PaymentApp::Type::AUTOFILL;
609 }
610
611 bool an_app_can_provide_all_info = false;
612 for (const auto& app : state()->available_apps()) {
613 if ((!spec()->request_shipping() || app->HandlesShippingAddress()) &&
614 (!spec()->request_payer_name() || app->HandlesPayerName()) &&
615 (!spec()->request_payer_phone() || app->HandlesPayerPhone()) &&
616 (!spec()->request_payer_email() || app->HandlesPayerEmail())) {
617 // There is another available app that can provide all merchant requested
618 // information information.
619 if (an_app_can_provide_all_info)
620 return false;
621
622 an_app_can_provide_all_info = true;
623 }
624 }
625 return an_app_can_provide_all_info;
626}
627
Sahel Sharifyd3f1bc82019-05-21 18:48:46628bool PaymentRequest::SatisfiesSkipUIConstraints() {
Rouslan Solomakhin9788d4b2019-04-09 13:10:23629 // Only allowing URL base payment apps to skip the payment sheet.
Sahel Sharifyd3f1bc82019-05-21 18:48:46630 skipped_payment_request_ui_ =
Sahel Sharify9120fd32019-12-05 17:16:11631 (spec()->url_payment_method_identifiers().size() > 0 ||
danakjdca06902019-06-27 21:41:41632 delegate_->SkipUiForBasicCard()) &&
Sahel Sharifyd3f1bc82019-05-21 18:48:46633 base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
634 base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
635 is_show_user_gesture_ && state()->IsInitialized() &&
Sahel Sharify9120fd32019-12-05 17:16:11636 spec()->IsInitialized() &&
637 OnlySingleAppCanProvideAllRequiredInformation() &&
Sahel Sharify90307872019-12-02 17:28:14638 // The available app should be preselectable.
Sahel Sharify9120fd32019-12-05 17:16:11639 state()->selected_app() != nullptr;
Sahel Sharifyd3f1bc82019-05-21 18:48:46640 if (skipped_payment_request_ui_) {
641 DCHECK(state()->IsInitialized() && spec()->IsInitialized());
642 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SKIPPED_SHOW);
643 } else if (state()->IsInitialized() && spec()->IsInitialized()) {
644 // Set EVENT_SHOWN only after state() and spec() initialization.
645 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
646 }
647 return skipped_payment_request_ui_;
Rouslan Solomakhin27064702018-12-14 21:15:33648}
649
mathpf1a7a3752017-03-15 11:23:37650void PaymentRequest::OnPaymentResponseAvailable(
651 mojom::PaymentResponsePtr response) {
Rouslan Solomakhin68429b72019-06-27 15:12:39652 DCHECK(!response->method_name.empty());
653 DCHECK(!response->stringified_details.empty());
654
mathp57c8c862017-06-16 20:15:45655 journey_logger_.SetEventOccurred(
656 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
gogerald7a2b761e2017-11-09 18:30:19657
Sahel Sharify26884382019-05-07 16:23:51658 // Log the correct "selected instrument" metric according to its type and
659 // the method name in response.
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39660 DCHECK(state_->selected_app());
Sahel Sharify26884382019-05-07 16:23:51661 JourneyLogger::Event selected_event =
662 JourneyLogger::Event::EVENT_SELECTED_OTHER;
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39663 switch (state_->selected_app()->type()) {
664 case PaymentApp::Type::AUTOFILL:
Sahel Sharify26884382019-05-07 16:23:51665 selected_event = JourneyLogger::Event::EVENT_SELECTED_CREDIT_CARD;
666 break;
Rouslan Solomakhin72fd06d2020-08-17 14:23:40667 case PaymentApp::Type::SERVICE_WORKER_APP:
668 // Intentionally fall through.
669 case PaymentApp::Type::NATIVE_MOBILE_APP: {
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39670 selected_event = IsGooglePaymentMethod(response->method_name)
671 ? JourneyLogger::Event::EVENT_SELECTED_GOOGLE
672 : JourneyLogger::Event::EVENT_SELECTED_OTHER;
Sahel Sharify26884382019-05-07 16:23:51673 break;
674 }
Rouslan Solomakhin1604d0bb2020-05-27 09:59:21675 case PaymentApp::Type::UNDEFINED:
676 // Intentionally fall through.
Rouslan Solomakhin1604d0bb2020-05-27 09:59:21677 case PaymentApp::Type::INTERNAL:
Sahel Sharify26884382019-05-07 16:23:51678 NOTREACHED();
679 break;
680 }
681 journey_logger_.SetEventOccurred(selected_event);
682
Rouslan Solomakhin02d086ec2019-01-31 23:10:39683 // If currently interactive, show the processing spinner. Autofill payment
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39684 // apps request a CVC, so they are always interactive at this point. A payment
685 // handler may elect to be non-interactive by not showing a confirmation page
686 // to the user.
Rouslan Solomakhin02d086ec2019-01-31 23:10:39687 if (delegate_->IsInteractive())
688 delegate_->ShowProcessingSpinner();
689
mathpf1a7a3752017-03-15 11:23:37690 client_->OnPaymentResponse(std::move(response));
mathp4b85b582017-03-08 21:07:16691}
692
Rouslan Solomakhin68429b72019-06-27 15:12:39693void PaymentRequest::OnPaymentResponseError(const std::string& error_message) {
694 journey_logger_.SetEventOccurred(
695 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
696 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_INSTRUMENT_DETAILS_ERROR);
697
698 reject_show_error_message_ = error_message;
699 delegate_->ShowErrorMessage();
700 // When the user dismisses the error message, UserCancelled() will reject
701 // PaymentRequest.show() with |reject_show_error_message_|.
702}
703
mathp151bd312017-04-03 21:07:24704void PaymentRequest::OnShippingOptionIdSelected(
705 std::string shipping_option_id) {
706 client_->OnShippingOptionChange(shipping_option_id);
707}
708
709void PaymentRequest::OnShippingAddressSelected(
710 mojom::PaymentAddressPtr address) {
Sahel Sharify9d98a502019-09-30 19:58:39711 client_->OnShippingAddressChange(RedactShippingAddress(std::move(address)));
mathp151bd312017-04-03 21:07:24712}
713
Jinho Bangbb178152018-09-13 09:44:43714void PaymentRequest::OnPayerInfoSelected(mojom::PayerDetailPtr payer_info) {
715 client_->OnPayerDetailChange(std::move(payer_info));
716}
717
mathpf4bc50e2017-01-24 05:17:50718void PaymentRequest::UserCancelled() {
719 // If |client_| is not bound, then the object is already being destroyed as
720 // a result of a renderer event.
721 if (!client_.is_bound())
722 return;
723
sebsgfcdd13c2017-06-08 15:49:33724 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
sebsg20b49d7b2017-05-04 20:23:17725
mathpf4bc50e2017-01-24 05:17:50726 // This sends an error to the renderer, which informs the API user.
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03727 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
Rouslan Solomakhin68429b72019-06-27 15:12:39728 !reject_show_error_message_.empty()
729 ? reject_show_error_message_
730 : errors::kUserCancelled);
mathpf4bc50e2017-01-24 05:17:50731
732 // We close all bindings and ask to be destroyed.
733 client_.reset();
Gyuyoung Kim9cfbda32019-08-27 02:15:18734 receiver_.reset();
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02735 payment_handler_host_.Disconnect();
rouslanb28f4532017-05-08 15:41:47736 if (observer_for_testing_)
737 observer_for_testing_->OnConnectionTerminated();
mathpf4bc50e2017-01-24 05:17:50738 manager_->DestroyRequest(this);
mathpf709499d2017-01-09 20:48:36739}
740
sebsgd56b3e422017-10-20 18:08:08741void PaymentRequest::DidStartMainFrameNavigationToDifferentDocument(
742 bool is_user_initiated) {
sebsgfcdd13c2017-06-08 15:49:33743 RecordFirstAbortReason(is_user_initiated
744 ? JourneyLogger::ABORT_REASON_USER_NAVIGATION
745 : JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION);
sebsg2c8558a2017-05-17 18:54:10746}
747
mathpf4bc50e2017-01-24 05:17:50748void PaymentRequest::OnConnectionTerminated() {
749 // We are here because of a browser-side error, or likely as a result of the
Gyuyoung Kim9cfbda32019-08-27 02:15:18750 // disconnect_handler on |receiver_|, which can mean that the renderer
mathpf4bc50e2017-01-24 05:17:50751 // has decided to close the pipe for various reasons (see all uses of
752 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close
753 // the binding and the dialog, and ask to be deleted.
754 client_.reset();
Gyuyoung Kim9cfbda32019-08-27 02:15:18755 receiver_.reset();
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02756 payment_handler_host_.Disconnect();
mathpf4bc50e2017-01-24 05:17:50757 delegate_->CloseDialog();
rouslanb28f4532017-05-08 15:41:47758 if (observer_for_testing_)
759 observer_for_testing_->OnConnectionTerminated();
sebsgfcdd13c2017-06-08 15:49:33760
761 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_MOJO_CONNECTION_ERROR);
mathpf709499d2017-01-09 20:48:36762 manager_->DestroyRequest(this);
763}
764
mathpd4be8de82017-03-01 00:51:48765void PaymentRequest::Pay() {
mathp57c8c862017-06-16 20:15:45766 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
Sahel Sharify9306fe92020-08-18 20:04:58767 journey_logger_.RecordCheckoutStep(
768 JourneyLogger::CheckoutFunnelStep::kPaymentHandlerInvoked);
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39769 DCHECK(state_->selected_app());
Rouslan Solomakhin47f109e2020-05-20 15:45:36770 state_->selected_app()->SetPaymentHandlerHost(
771 payment_handler_host_.AsWeakPtr());
mathpf1a7a3752017-03-15 11:23:37772 state_->GeneratePaymentResponse();
mathpd4be8de82017-03-01 00:51:48773}
774
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07775void PaymentRequest::HideIfNecessary() {
776 display_handle_.reset();
777}
778
Ramin Halavatifbfcef672020-05-21 16:33:34779bool PaymentRequest::IsOffTheRecord() const {
780 return delegate_->IsOffTheRecord();
Anthony Vallee-Dubois10d131a2018-02-22 15:41:04781}
782
Sahel Sharify54f5e862020-03-26 20:02:41783void PaymentRequest::OnPaymentHandlerOpenWindowCalled() {
784 DCHECK(state_->selected_app());
785 // UKM for payment app origin should get recorded only when the origin of the
786 // invoked payment app is shown to the user.
787 journey_logger_.SetPaymentAppUkmSourceId(
788 state_->selected_app()->UkmSourceId());
789}
790
sebsgfcdd13c2017-06-08 15:49:33791void PaymentRequest::RecordFirstAbortReason(
792 JourneyLogger::AbortReason abort_reason) {
793 if (!has_recorded_completion_) {
794 has_recorded_completion_ = true;
795 journey_logger_.SetAborted(abort_reason);
sebsg2c8558a2017-05-17 18:54:10796 }
797}
798
Danyao Wang03a4cbd2019-08-15 23:47:11799void PaymentRequest::CanMakePaymentCallback(bool can_make_payment) {
800 client_->OnCanMakePayment(
801 can_make_payment ? mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT
802 : mojom::CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT);
Danyao Wang4bc0606a2018-12-27 16:54:53803
Danyao Wang57aa0442019-01-31 04:06:41804 journey_logger_.SetCanMakePaymentValue(can_make_payment);
Rouslan Solomakhin1804ee42017-10-03 14:27:43805
806 if (observer_for_testing_)
807 observer_for_testing_->OnCanMakePaymentReturned();
808}
809
Danyao Wangce175bf2018-12-21 22:35:58810void PaymentRequest::HasEnrolledInstrumentCallback(
Danyao Wangce175bf2018-12-21 22:35:58811 bool has_enrolled_instrument) {
Rouslan Solomakhinb26faa072019-08-19 14:42:28812 if (!spec_ || CanMakePaymentQueryFactory::GetInstance()
813 ->GetForContext(web_contents_->GetBrowserContext())
814 ->CanQuery(top_level_origin_, frame_origin_,
Dongjun Kim0a7565b2020-08-18 09:39:46815 spec_->query_for_quota())) {
Danyao Wangce175bf2018-12-21 22:35:58816 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58817 /*warn_local_development=*/false);
818 } else if (UrlUtil::IsLocalDevelopmentUrl(frame_origin_)) {
Danyao Wangce175bf2018-12-21 22:35:58819 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58820 /*warn_local_development=*/true);
Danyao Wangce175bf2018-12-21 22:35:58821 } else {
822 client_->OnHasEnrolledInstrument(
823 HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED);
824 }
825
826 if (observer_for_testing_)
827 observer_for_testing_->OnHasEnrolledInstrumentReturned();
828}
829
Danyao Wangce175bf2018-12-21 22:35:58830void PaymentRequest::RespondToHasEnrolledInstrumentQuery(
831 bool has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58832 bool warn_local_development) {
Danyao Wangce175bf2018-12-21 22:35:58833 HasEnrolledInstrumentQueryResult positive =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58834 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58835 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_ENROLLED_INSTRUMENT
836 : HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT;
837 HasEnrolledInstrumentQueryResult negative =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58838 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58839 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_NO_ENROLLED_INSTRUMENT
840 : HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT;
841
842 client_->OnHasEnrolledInstrument(has_enrolled_instrument ? positive
843 : negative);
Danyao Wang57aa0442019-01-31 04:06:41844 journey_logger_.SetHasEnrolledInstrumentValue(has_enrolled_instrument);
Danyao Wangce175bf2018-12-21 22:35:58845}
846
Rouslan Solomakhin13947ae32020-05-22 15:34:29847void PaymentRequest::OnAbortResult(bool aborted) {
848 if (client_.is_bound())
849 client_->OnAbort(aborted);
850
851 if (aborted) {
852 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_MERCHANT);
853 state_->OnAbort();
854 }
855}
856
mathpf709499d2017-01-09 20:48:36857} // namespace payments