1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "BasicCardPayment.h"
8 #include "mozilla/ClearOnShutdown.h"
9 #include "mozilla/dom/BasicCardPaymentBinding.h"
10 #include "mozilla/dom/PaymentRequestParent.h"
11 #include "nsArrayUtils.h"
12 #include "nsComponentManagerUtils.h"
14 #include "nsIMutableArray.h"
15 #include "nsServiceManagerUtils.h"
16 #include "nsSimpleEnumerator.h"
17 #include "PaymentRequestService.h"
19 namespace mozilla::dom
{
21 StaticRefPtr
<PaymentRequestService
> gPaymentService
;
25 class PaymentRequestEnumerator final
: public nsSimpleEnumerator
{
27 NS_DECL_NSISIMPLEENUMERATOR
29 PaymentRequestEnumerator() : mIndex(0) {}
31 const nsID
& DefaultInterface() override
{
32 return NS_GET_IID(nsIPaymentRequest
);
36 ~PaymentRequestEnumerator() override
= default;
41 PaymentRequestEnumerator::HasMoreElements(bool* aReturn
) {
42 NS_ENSURE_ARG_POINTER(aReturn
);
44 if (NS_WARN_IF(!gPaymentService
)) {
45 return NS_ERROR_FAILURE
;
47 RefPtr
<PaymentRequestService
> service
= gPaymentService
;
48 *aReturn
= mIndex
< service
->NumPayments();
53 PaymentRequestEnumerator::GetNext(nsISupports
** aItem
) {
54 NS_ENSURE_ARG_POINTER(aItem
);
55 if (NS_WARN_IF(!gPaymentService
)) {
56 return NS_ERROR_FAILURE
;
58 RefPtr
<payments::PaymentRequest
> rowRequest
=
59 gPaymentService
->GetPaymentRequestByIndex(mIndex
);
61 return NS_ERROR_FAILURE
;
64 rowRequest
.forget(aItem
);
68 } // end of anonymous namespace
70 /* PaymentRequestService */
72 NS_IMPL_ISUPPORTS(PaymentRequestService
, nsIPaymentRequestService
)
74 already_AddRefed
<PaymentRequestService
> PaymentRequestService::GetSingleton() {
75 MOZ_ASSERT(NS_IsMainThread());
76 if (!gPaymentService
) {
77 gPaymentService
= new PaymentRequestService();
78 ClearOnShutdown(&gPaymentService
);
80 RefPtr
<PaymentRequestService
> service
= gPaymentService
;
81 return service
.forget();
84 uint32_t PaymentRequestService::NumPayments() const {
85 return mRequestQueue
.Length();
88 already_AddRefed
<payments::PaymentRequest
>
89 PaymentRequestService::GetPaymentRequestByIndex(const uint32_t aIndex
) {
90 if (aIndex
>= mRequestQueue
.Length()) {
93 RefPtr
<payments::PaymentRequest
> request
= mRequestQueue
[aIndex
];
95 return request
.forget();
99 PaymentRequestService::GetPaymentRequestById(const nsAString
& aRequestId
,
100 nsIPaymentRequest
** aRequest
) {
101 NS_ENSURE_ARG_POINTER(aRequest
);
103 RefPtr
<payments::PaymentRequest
> rowRequest
;
104 nsresult rv
= GetPaymentRequestById(aRequestId
, getter_AddRefs(rowRequest
));
105 if (NS_WARN_IF(NS_FAILED(rv
))) {
108 rowRequest
.forget(aRequest
);
112 nsresult
PaymentRequestService::GetPaymentRequestById(
113 const nsAString
& aRequestId
, payments::PaymentRequest
** aRequest
) {
114 NS_ENSURE_ARG_POINTER(aRequest
);
116 uint32_t numRequests
= mRequestQueue
.Length();
117 for (uint32_t index
= 0; index
< numRequests
; ++index
) {
118 RefPtr
<payments::PaymentRequest
> request
= mRequestQueue
[index
];
120 nsAutoString requestId
;
121 nsresult rv
= request
->GetRequestId(requestId
);
122 NS_ENSURE_SUCCESS(rv
, rv
);
123 if (requestId
== aRequestId
) {
124 request
.forget(aRequest
);
132 PaymentRequestService::Enumerate(nsISimpleEnumerator
** aEnumerator
) {
133 NS_ENSURE_ARG_POINTER(aEnumerator
);
134 nsCOMPtr
<nsISimpleEnumerator
> enumerator
= new PaymentRequestEnumerator();
135 enumerator
.forget(aEnumerator
);
140 PaymentRequestService::Cleanup() {
141 mRequestQueue
.Clear();
146 PaymentRequestService::SetTestingUIService(nsIPaymentUIService
* aUIService
) {
147 // aUIService can be nullptr
148 mTestingUIService
= aUIService
;
152 nsresult
PaymentRequestService::LaunchUIAction(const nsAString
& aRequestId
,
153 uint32_t aActionType
) {
154 nsCOMPtr
<nsIPaymentUIService
> uiService
;
156 if (mTestingUIService
) {
157 uiService
= mTestingUIService
;
159 uiService
= do_GetService(NS_PAYMENT_UI_SERVICE_CONTRACT_ID
, &rv
);
160 if (NS_WARN_IF(NS_FAILED(rv
))) {
164 switch (aActionType
) {
165 case IPCPaymentActionRequest::TIPCPaymentShowActionRequest
: {
166 rv
= uiService
->ShowPayment(aRequestId
);
169 case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest
: {
170 rv
= uiService
->AbortPayment(aRequestId
);
173 case IPCPaymentActionRequest::TIPCPaymentCompleteActionRequest
: {
174 rv
= uiService
->CompletePayment(aRequestId
);
177 case IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest
: {
178 rv
= uiService
->UpdatePayment(aRequestId
);
181 case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest
: {
182 rv
= uiService
->ClosePayment(aRequestId
);
186 return NS_ERROR_FAILURE
;
189 if (NS_WARN_IF(NS_FAILED(rv
))) {
195 nsresult
PaymentRequestService::RequestPayment(
196 const nsAString
& aRequestId
, const IPCPaymentActionRequest
& aAction
,
197 PaymentRequestParent
* aIPC
) {
198 NS_ENSURE_ARG_POINTER(aIPC
);
201 uint32_t type
= aAction
.type();
203 RefPtr
<payments::PaymentRequest
> request
;
204 if (type
!= IPCPaymentActionRequest::TIPCPaymentCreateActionRequest
) {
205 rv
= GetPaymentRequestById(aRequestId
, getter_AddRefs(request
));
206 if (NS_WARN_IF(NS_FAILED(rv
))) {
210 type
!= IPCPaymentActionRequest::TIPCPaymentCloseActionRequest
) {
211 return NS_ERROR_FAILURE
;
214 request
->SetIPC(aIPC
);
219 case IPCPaymentActionRequest::TIPCPaymentCreateActionRequest
: {
220 MOZ_ASSERT(!request
);
221 const IPCPaymentCreateActionRequest
& action
= aAction
;
222 nsCOMPtr
<nsIMutableArray
> methodData
=
223 do_CreateInstance(NS_ARRAY_CONTRACTID
);
224 MOZ_ASSERT(methodData
);
225 for (IPCPaymentMethodData data
: action
.methodData()) {
226 nsCOMPtr
<nsIPaymentMethodData
> method
;
227 rv
= payments::PaymentMethodData::Create(data
, getter_AddRefs(method
));
228 NS_ENSURE_SUCCESS(rv
, rv
);
229 rv
= methodData
->AppendElement(method
);
230 NS_ENSURE_SUCCESS(rv
, rv
);
232 nsCOMPtr
<nsIPaymentDetails
> details
;
233 rv
= payments::PaymentDetails::Create(action
.details(),
234 getter_AddRefs(details
));
235 NS_ENSURE_SUCCESS(rv
, rv
);
236 nsCOMPtr
<nsIPaymentOptions
> options
;
237 rv
= payments::PaymentOptions::Create(action
.options(),
238 getter_AddRefs(options
));
239 NS_ENSURE_SUCCESS(rv
, rv
);
240 RefPtr
<payments::PaymentRequest
> request
= new payments::PaymentRequest(
241 action
.topOuterWindowId(), aRequestId
, action
.topLevelPrincipal(),
242 methodData
, details
, options
, action
.shippingOption());
244 if (!mRequestQueue
.AppendElement(request
, mozilla::fallible
)) {
245 return NS_ERROR_OUT_OF_MEMORY
;
249 case IPCPaymentActionRequest::TIPCPaymentCanMakeActionRequest
: {
250 nsCOMPtr
<nsIPaymentCanMakeActionResponse
> canMakeResponse
=
251 do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID
);
252 MOZ_ASSERT(canMakeResponse
);
253 rv
= canMakeResponse
->Init(aRequestId
, CanMakePayment(aRequestId
));
254 if (NS_WARN_IF(NS_FAILED(rv
))) {
257 rv
= RespondPayment(canMakeResponse
.get());
258 if (NS_WARN_IF(NS_FAILED(rv
))) {
263 case IPCPaymentActionRequest::TIPCPaymentShowActionRequest
: {
264 const IPCPaymentShowActionRequest
& action
= aAction
;
265 rv
= ShowPayment(aRequestId
, action
.isUpdating());
266 if (NS_WARN_IF(NS_FAILED(rv
))) {
271 case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest
: {
273 request
->SetState(payments::PaymentRequest::eInteractive
);
274 rv
= LaunchUIAction(aRequestId
, type
);
275 if (NS_WARN_IF(NS_FAILED(rv
))) {
280 case IPCPaymentActionRequest::TIPCPaymentCompleteActionRequest
: {
282 const IPCPaymentCompleteActionRequest
& action
= aAction
;
283 request
->SetCompleteStatus(action
.completeStatus());
284 rv
= LaunchUIAction(aRequestId
, type
);
285 if (NS_WARN_IF(NS_FAILED(rv
))) {
290 case IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest
: {
291 const IPCPaymentUpdateActionRequest
& action
= aAction
;
292 nsCOMPtr
<nsIPaymentDetails
> details
;
293 rv
= payments::PaymentDetails::Create(action
.details(),
294 getter_AddRefs(details
));
295 if (NS_WARN_IF(NS_FAILED(rv
))) {
299 rv
= request
->UpdatePaymentDetails(details
, action
.shippingOption());
300 if (NS_WARN_IF(NS_FAILED(rv
))) {
303 nsAutoString completeStatus
;
304 rv
= request
->GetCompleteStatus(completeStatus
);
305 if (NS_WARN_IF(NS_FAILED(rv
))) {
308 if (completeStatus
.Equals(u
"initial"_ns
)) {
309 request
->SetCompleteStatus(u
""_ns
);
311 MOZ_ASSERT(mShowingRequest
&& mShowingRequest
== request
);
312 rv
= LaunchUIAction(aRequestId
, type
);
313 if (NS_WARN_IF(NS_FAILED(rv
))) {
318 case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest
: {
319 rv
= LaunchUIAction(aRequestId
, type
);
320 if (NS_WARN_IF(NS_FAILED(rv
))) {
323 if (mShowingRequest
== request
) {
324 mShowingRequest
= nullptr;
326 mRequestQueue
.RemoveElement(request
);
329 case IPCPaymentActionRequest::TIPCPaymentRetryActionRequest
: {
330 const IPCPaymentRetryActionRequest
& action
= aAction
;
332 request
->UpdateErrors(action
.error(), action
.payerErrors(),
333 action
.paymentMethodErrors(),
334 action
.shippingAddressErrors());
335 request
->SetState(payments::PaymentRequest::eInteractive
);
336 MOZ_ASSERT(mShowingRequest
== request
);
338 aRequestId
, IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest
);
342 return NS_ERROR_FAILURE
;
349 PaymentRequestService::RespondPayment(nsIPaymentActionResponse
* aResponse
) {
350 NS_ENSURE_ARG_POINTER(aResponse
);
351 nsAutoString requestId
;
352 nsresult rv
= aResponse
->GetRequestId(requestId
);
353 NS_ENSURE_SUCCESS(rv
, rv
);
355 RefPtr
<payments::PaymentRequest
> request
;
356 rv
= GetPaymentRequestById(requestId
, getter_AddRefs(request
));
357 if (NS_WARN_IF(NS_FAILED(rv
))) {
361 return NS_ERROR_FAILURE
;
364 rv
= aResponse
->GetType(&type
);
365 NS_ENSURE_SUCCESS(rv
, rv
);
367 // PaymentRequest can only be responded when
368 // 1. the state is eInteractive
369 // 2. the state is eClosed and response type is COMPLETE_ACTION
370 // 3. the state is eCreated and response type is CANMAKE_ACTION
371 payments::PaymentRequest::eState state
= request
->GetState();
372 bool canBeResponded
= (state
== payments::PaymentRequest::eInteractive
) ||
373 (state
== payments::PaymentRequest::eClosed
&&
374 type
== nsIPaymentActionResponse::COMPLETE_ACTION
) ||
375 (state
== payments::PaymentRequest::eCreated
&&
376 type
== nsIPaymentActionResponse::CANMAKE_ACTION
);
377 if (!canBeResponded
) {
378 return NS_ERROR_FAILURE
;
381 if (!request
->GetIPC()) {
382 return NS_ERROR_FAILURE
;
384 rv
= request
->GetIPC()->RespondPayment(aResponse
);
385 if (NS_WARN_IF(NS_FAILED(rv
))) {
389 // Remove PaymentRequest from mRequestQueue while receive succeeded abort
390 // response or complete response
392 case nsIPaymentActionResponse::ABORT_ACTION
: {
393 nsCOMPtr
<nsIPaymentAbortActionResponse
> response
=
394 do_QueryInterface(aResponse
);
395 MOZ_ASSERT(response
);
397 rv
= response
->IsSucceeded(&isSucceeded
);
398 NS_ENSURE_SUCCESS(rv
, rv
);
399 mShowingRequest
= nullptr;
401 mRequestQueue
.RemoveElement(request
);
402 request
->SetState(payments::PaymentRequest::eClosed
);
406 case nsIPaymentActionResponse::SHOW_ACTION
: {
407 request
->SetState(payments::PaymentRequest::eClosed
);
408 nsCOMPtr
<nsIPaymentShowActionResponse
> response
=
409 do_QueryInterface(aResponse
);
410 MOZ_ASSERT(response
);
411 uint32_t acceptStatus
;
412 rv
= response
->GetAcceptStatus(&acceptStatus
);
413 NS_ENSURE_SUCCESS(rv
, rv
);
414 if (acceptStatus
!= nsIPaymentActionResponse::PAYMENT_ACCEPTED
) {
415 // Check if rejecting the showing PaymentRequest.
416 // If yes, set mShowingRequest as nullptr.
417 if (mShowingRequest
== request
) {
418 mShowingRequest
= nullptr;
420 mRequestQueue
.RemoveElement(request
);
424 case nsIPaymentActionResponse::COMPLETE_ACTION
: {
425 mShowingRequest
= nullptr;
426 mRequestQueue
.RemoveElement(request
);
437 PaymentRequestService::ChangeShippingAddress(const nsAString
& aRequestId
,
438 nsIPaymentAddress
* aAddress
) {
439 RefPtr
<payments::PaymentRequest
> request
;
440 nsresult rv
= GetPaymentRequestById(aRequestId
, getter_AddRefs(request
));
441 if (NS_WARN_IF(NS_FAILED(rv
))) {
445 return NS_ERROR_FAILURE
;
447 if (request
->GetState() != payments::PaymentRequest::eInteractive
) {
448 return NS_ERROR_FAILURE
;
450 if (!request
->GetIPC()) {
451 return NS_ERROR_FAILURE
;
453 rv
= request
->GetIPC()->ChangeShippingAddress(aRequestId
, aAddress
);
454 if (NS_WARN_IF(NS_FAILED(rv
))) {
461 PaymentRequestService::ChangeShippingOption(const nsAString
& aRequestId
,
462 const nsAString
& aOption
) {
463 RefPtr
<payments::PaymentRequest
> request
;
464 nsresult rv
= GetPaymentRequestById(aRequestId
, getter_AddRefs(request
));
465 if (NS_WARN_IF(NS_FAILED(rv
))) {
469 return NS_ERROR_FAILURE
;
471 if (request
->GetState() != payments::PaymentRequest::eInteractive
) {
472 return NS_ERROR_FAILURE
;
474 if (!request
->GetIPC()) {
475 return NS_ERROR_FAILURE
;
477 rv
= request
->GetIPC()->ChangeShippingOption(aRequestId
, aOption
);
478 if (NS_WARN_IF(NS_FAILED(rv
))) {
485 PaymentRequestService::ChangePayerDetail(const nsAString
& aRequestId
,
486 const nsAString
& aPayerName
,
487 const nsAString
& aPayerEmail
,
488 const nsAString
& aPayerPhone
) {
489 RefPtr
<payments::PaymentRequest
> request
;
490 nsresult rv
= GetPaymentRequestById(aRequestId
, getter_AddRefs(request
));
491 if (NS_WARN_IF(NS_FAILED(rv
))) {
495 if (!request
->GetIPC()) {
496 return NS_ERROR_FAILURE
;
498 rv
= request
->GetIPC()->ChangePayerDetail(aRequestId
, aPayerName
, aPayerEmail
,
500 if (NS_WARN_IF(NS_FAILED(rv
))) {
507 PaymentRequestService::ChangePaymentMethod(
508 const nsAString
& aRequestId
, const nsAString
& aMethodName
,
509 nsIMethodChangeDetails
* aMethodDetails
) {
510 RefPtr
<payments::PaymentRequest
> request
;
511 nsresult rv
= GetPaymentRequestById(aRequestId
, getter_AddRefs(request
));
512 if (NS_WARN_IF(NS_FAILED(rv
))) {
516 return NS_ERROR_FAILURE
;
518 if (request
->GetState() != payments::PaymentRequest::eInteractive
) {
519 return NS_ERROR_FAILURE
;
521 if (!request
->GetIPC()) {
522 return NS_ERROR_FAILURE
;
524 rv
= request
->GetIPC()->ChangePaymentMethod(aRequestId
, aMethodName
,
526 if (NS_WARN_IF(NS_FAILED(rv
))) {
532 bool PaymentRequestService::CanMakePayment(const nsAString
& aRequestId
) {
534 * TODO: Check third party payment app support by traversing all
535 * registered third party payment apps.
537 return IsBasicCardPayment(aRequestId
);
540 nsresult
PaymentRequestService::ShowPayment(const nsAString
& aRequestId
,
543 RefPtr
<payments::PaymentRequest
> request
;
544 rv
= GetPaymentRequestById(aRequestId
, getter_AddRefs(request
));
545 if (NS_WARN_IF(NS_FAILED(rv
))) {
549 request
->SetState(payments::PaymentRequest::eInteractive
);
551 request
->SetCompleteStatus(u
"initial"_ns
);
554 if (mShowingRequest
|| !CanMakePayment(aRequestId
)) {
555 uint32_t responseStatus
;
556 if (mShowingRequest
) {
557 responseStatus
= nsIPaymentActionResponse::PAYMENT_REJECTED
;
559 responseStatus
= nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED
;
561 nsCOMPtr
<nsIPaymentShowActionResponse
> showResponse
=
562 do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID
);
563 MOZ_ASSERT(showResponse
);
564 rv
= showResponse
->Init(aRequestId
, responseStatus
, u
""_ns
, nullptr, u
""_ns
,
566 rv
= RespondPayment(showResponse
.get());
567 if (NS_WARN_IF(NS_FAILED(rv
))) {
571 mShowingRequest
= request
;
572 rv
= LaunchUIAction(aRequestId
,
573 IPCPaymentActionRequest::TIPCPaymentShowActionRequest
);
574 if (NS_WARN_IF(NS_FAILED(rv
))) {
581 bool PaymentRequestService::IsBasicCardPayment(const nsAString
& aRequestId
) {
582 RefPtr
<payments::PaymentRequest
> request
;
583 nsresult rv
= GetPaymentRequestById(aRequestId
, getter_AddRefs(request
));
584 NS_ENSURE_SUCCESS(rv
, false);
585 nsCOMPtr
<nsIArray
> methods
;
586 rv
= request
->GetPaymentMethods(getter_AddRefs(methods
));
587 NS_ENSURE_SUCCESS(rv
, false);
589 rv
= methods
->GetLength(&length
);
590 NS_ENSURE_SUCCESS(rv
, false);
591 RefPtr
<BasicCardService
> service
= BasicCardService::GetService();
593 for (uint32_t index
= 0; index
< length
; ++index
) {
594 nsCOMPtr
<nsIPaymentMethodData
> method
= do_QueryElementAt(methods
, index
);
596 nsAutoString supportedMethods
;
597 rv
= method
->GetSupportedMethods(supportedMethods
);
598 NS_ENSURE_SUCCESS(rv
, false);
599 if (service
->IsBasicCardPayment(supportedMethods
)) {
606 } // namespace mozilla::dom