PaymentRequestService.cpp (19377B)
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/. */ 6 7 #include "PaymentRequestService.h" 8 9 #include "BasicCardPayment.h" 10 #include "mozilla/ClearOnShutdown.h" 11 #include "mozilla/dom/BasicCardPaymentBinding.h" 12 #include "mozilla/dom/PaymentRequestParent.h" 13 #include "nsArrayUtils.h" 14 #include "nsCOMPtr.h" 15 #include "nsComponentManagerUtils.h" 16 #include "nsIMutableArray.h" 17 #include "nsServiceManagerUtils.h" 18 #include "nsSimpleEnumerator.h" 19 20 namespace mozilla::dom { 21 22 StaticRefPtr<PaymentRequestService> gPaymentService; 23 24 namespace { 25 26 class PaymentRequestEnumerator final : public nsSimpleEnumerator { 27 public: 28 NS_DECL_NSISIMPLEENUMERATOR 29 30 PaymentRequestEnumerator() : mIndex(0) {} 31 32 const nsID& DefaultInterface() override { 33 return NS_GET_IID(nsIPaymentRequest); 34 } 35 36 private: 37 ~PaymentRequestEnumerator() override = default; 38 uint32_t mIndex; 39 }; 40 41 NS_IMETHODIMP 42 PaymentRequestEnumerator::HasMoreElements(bool* aReturn) { 43 NS_ENSURE_ARG_POINTER(aReturn); 44 *aReturn = false; 45 if (NS_WARN_IF(!gPaymentService)) { 46 return NS_ERROR_FAILURE; 47 } 48 RefPtr<PaymentRequestService> service = gPaymentService; 49 *aReturn = mIndex < service->NumPayments(); 50 return NS_OK; 51 } 52 53 NS_IMETHODIMP 54 PaymentRequestEnumerator::GetNext(nsISupports** aItem) { 55 NS_ENSURE_ARG_POINTER(aItem); 56 if (NS_WARN_IF(!gPaymentService)) { 57 return NS_ERROR_FAILURE; 58 } 59 RefPtr<payments::PaymentRequest> rowRequest = 60 gPaymentService->GetPaymentRequestByIndex(mIndex); 61 if (!rowRequest) { 62 return NS_ERROR_FAILURE; 63 } 64 mIndex++; 65 rowRequest.forget(aItem); 66 return NS_OK; 67 } 68 69 } // end of anonymous namespace 70 71 /* PaymentRequestService */ 72 73 NS_IMPL_ISUPPORTS(PaymentRequestService, nsIPaymentRequestService) 74 75 already_AddRefed<PaymentRequestService> PaymentRequestService::GetSingleton() { 76 MOZ_ASSERT(NS_IsMainThread()); 77 if (!gPaymentService) { 78 gPaymentService = new PaymentRequestService(); 79 ClearOnShutdown(&gPaymentService); 80 } 81 RefPtr<PaymentRequestService> service = gPaymentService; 82 return service.forget(); 83 } 84 85 uint32_t PaymentRequestService::NumPayments() const { 86 return mRequestQueue.Length(); 87 } 88 89 already_AddRefed<payments::PaymentRequest> 90 PaymentRequestService::GetPaymentRequestByIndex(const uint32_t aIndex) { 91 if (aIndex >= mRequestQueue.Length()) { 92 return nullptr; 93 } 94 RefPtr<payments::PaymentRequest> request = mRequestQueue[aIndex]; 95 MOZ_ASSERT(request); 96 return request.forget(); 97 } 98 99 NS_IMETHODIMP 100 PaymentRequestService::GetPaymentRequestById(const nsAString& aRequestId, 101 nsIPaymentRequest** aRequest) { 102 NS_ENSURE_ARG_POINTER(aRequest); 103 *aRequest = nullptr; 104 RefPtr<payments::PaymentRequest> rowRequest; 105 nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(rowRequest)); 106 if (NS_WARN_IF(NS_FAILED(rv))) { 107 return rv; 108 } 109 rowRequest.forget(aRequest); 110 return NS_OK; 111 } 112 113 nsresult PaymentRequestService::GetPaymentRequestById( 114 const nsAString& aRequestId, payments::PaymentRequest** aRequest) { 115 NS_ENSURE_ARG_POINTER(aRequest); 116 *aRequest = nullptr; 117 uint32_t numRequests = mRequestQueue.Length(); 118 for (uint32_t index = 0; index < numRequests; ++index) { 119 RefPtr<payments::PaymentRequest> request = mRequestQueue[index]; 120 MOZ_ASSERT(request); 121 nsAutoString requestId; 122 nsresult rv = request->GetRequestId(requestId); 123 NS_ENSURE_SUCCESS(rv, rv); 124 if (requestId == aRequestId) { 125 request.forget(aRequest); 126 break; 127 } 128 } 129 return NS_OK; 130 } 131 132 NS_IMETHODIMP 133 PaymentRequestService::Enumerate(nsISimpleEnumerator** aEnumerator) { 134 NS_ENSURE_ARG_POINTER(aEnumerator); 135 nsCOMPtr<nsISimpleEnumerator> enumerator = new PaymentRequestEnumerator(); 136 enumerator.forget(aEnumerator); 137 return NS_OK; 138 } 139 140 NS_IMETHODIMP 141 PaymentRequestService::Cleanup() { 142 mRequestQueue.Clear(); 143 return NS_OK; 144 } 145 146 NS_IMETHODIMP 147 PaymentRequestService::SetTestingUIService(nsIPaymentUIService* aUIService) { 148 // aUIService can be nullptr 149 mTestingUIService = aUIService; 150 return NS_OK; 151 } 152 153 nsresult PaymentRequestService::LaunchUIAction(const nsAString& aRequestId, 154 uint32_t aActionType) { 155 nsCOMPtr<nsIPaymentUIService> uiService; 156 nsresult rv; 157 if (mTestingUIService) { 158 uiService = mTestingUIService; 159 } else { 160 uiService = do_GetService(NS_PAYMENT_UI_SERVICE_CONTRACT_ID, &rv); 161 if (NS_WARN_IF(NS_FAILED(rv))) { 162 return rv; 163 } 164 } 165 switch (aActionType) { 166 case IPCPaymentActionRequest::TIPCPaymentShowActionRequest: { 167 rv = uiService->ShowPayment(aRequestId); 168 break; 169 } 170 case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest: { 171 rv = uiService->AbortPayment(aRequestId); 172 break; 173 } 174 case IPCPaymentActionRequest::TIPCPaymentCompleteActionRequest: { 175 rv = uiService->CompletePayment(aRequestId); 176 break; 177 } 178 case IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest: { 179 rv = uiService->UpdatePayment(aRequestId); 180 break; 181 } 182 case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest: { 183 rv = uiService->ClosePayment(aRequestId); 184 break; 185 } 186 default: { 187 return NS_ERROR_FAILURE; 188 } 189 } 190 if (NS_WARN_IF(NS_FAILED(rv))) { 191 return rv; 192 } 193 return NS_OK; 194 } 195 196 nsresult PaymentRequestService::RequestPayment( 197 const nsAString& aRequestId, const IPCPaymentActionRequest& aAction, 198 PaymentRequestParent* aIPC) { 199 NS_ENSURE_ARG_POINTER(aIPC); 200 201 nsresult rv = NS_OK; 202 uint32_t type = aAction.type(); 203 204 RefPtr<payments::PaymentRequest> request; 205 if (type != IPCPaymentActionRequest::TIPCPaymentCreateActionRequest) { 206 rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request)); 207 if (NS_WARN_IF(NS_FAILED(rv))) { 208 return rv; 209 } 210 if (!request && 211 type != IPCPaymentActionRequest::TIPCPaymentCloseActionRequest) { 212 return NS_ERROR_FAILURE; 213 } 214 if (request) { 215 request->SetIPC(aIPC); 216 } 217 } 218 219 switch (type) { 220 case IPCPaymentActionRequest::TIPCPaymentCreateActionRequest: { 221 MOZ_ASSERT(!request); 222 const IPCPaymentCreateActionRequest& action = aAction; 223 nsCOMPtr<nsIMutableArray> methodData = 224 do_CreateInstance(NS_ARRAY_CONTRACTID); 225 MOZ_ASSERT(methodData); 226 for (IPCPaymentMethodData data : action.methodData()) { 227 nsCOMPtr<nsIPaymentMethodData> method; 228 rv = payments::PaymentMethodData::Create(data, getter_AddRefs(method)); 229 NS_ENSURE_SUCCESS(rv, rv); 230 rv = methodData->AppendElement(method); 231 NS_ENSURE_SUCCESS(rv, rv); 232 } 233 nsCOMPtr<nsIPaymentDetails> details; 234 rv = payments::PaymentDetails::Create(action.details(), 235 getter_AddRefs(details)); 236 NS_ENSURE_SUCCESS(rv, rv); 237 nsCOMPtr<nsIPaymentOptions> options; 238 rv = payments::PaymentOptions::Create(action.options(), 239 getter_AddRefs(options)); 240 NS_ENSURE_SUCCESS(rv, rv); 241 RefPtr<payments::PaymentRequest> request = new payments::PaymentRequest( 242 action.topOuterWindowId(), aRequestId, action.topLevelPrincipal(), 243 methodData, details, options, action.shippingOption()); 244 245 if (!mRequestQueue.AppendElement(request, mozilla::fallible)) { 246 return NS_ERROR_OUT_OF_MEMORY; 247 } 248 break; 249 } 250 case IPCPaymentActionRequest::TIPCPaymentCanMakeActionRequest: { 251 nsCOMPtr<nsIPaymentCanMakeActionResponse> canMakeResponse = 252 do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID); 253 MOZ_ASSERT(canMakeResponse); 254 rv = canMakeResponse->Init(aRequestId, CanMakePayment(aRequestId)); 255 if (NS_WARN_IF(NS_FAILED(rv))) { 256 return rv; 257 } 258 rv = RespondPayment(canMakeResponse.get()); 259 if (NS_WARN_IF(NS_FAILED(rv))) { 260 return rv; 261 } 262 break; 263 } 264 case IPCPaymentActionRequest::TIPCPaymentShowActionRequest: { 265 const IPCPaymentShowActionRequest& action = aAction; 266 rv = ShowPayment(aRequestId, action.isUpdating()); 267 if (NS_WARN_IF(NS_FAILED(rv))) { 268 return rv; 269 } 270 break; 271 } 272 case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest: { 273 MOZ_ASSERT(request); 274 request->SetState(payments::PaymentRequest::eInteractive); 275 rv = LaunchUIAction(aRequestId, type); 276 if (NS_WARN_IF(NS_FAILED(rv))) { 277 return rv; 278 } 279 break; 280 } 281 case IPCPaymentActionRequest::TIPCPaymentCompleteActionRequest: { 282 MOZ_ASSERT(request); 283 const IPCPaymentCompleteActionRequest& action = aAction; 284 request->SetCompleteStatus(action.completeStatus()); 285 rv = LaunchUIAction(aRequestId, type); 286 if (NS_WARN_IF(NS_FAILED(rv))) { 287 return rv; 288 } 289 break; 290 } 291 case IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest: { 292 const IPCPaymentUpdateActionRequest& action = aAction; 293 nsCOMPtr<nsIPaymentDetails> details; 294 rv = payments::PaymentDetails::Create(action.details(), 295 getter_AddRefs(details)); 296 if (NS_WARN_IF(NS_FAILED(rv))) { 297 return rv; 298 } 299 MOZ_ASSERT(request); 300 rv = request->UpdatePaymentDetails(details, action.shippingOption()); 301 if (NS_WARN_IF(NS_FAILED(rv))) { 302 return rv; 303 } 304 nsAutoString completeStatus; 305 rv = request->GetCompleteStatus(completeStatus); 306 if (NS_WARN_IF(NS_FAILED(rv))) { 307 return rv; 308 } 309 if (completeStatus.Equals(u"initial"_ns)) { 310 request->SetCompleteStatus(u""_ns); 311 } 312 MOZ_ASSERT(mShowingRequest && mShowingRequest == request); 313 rv = LaunchUIAction(aRequestId, type); 314 if (NS_WARN_IF(NS_FAILED(rv))) { 315 return rv; 316 } 317 break; 318 } 319 case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest: { 320 rv = LaunchUIAction(aRequestId, type); 321 if (NS_WARN_IF(NS_FAILED(rv))) { 322 return rv; 323 } 324 if (mShowingRequest == request) { 325 mShowingRequest = nullptr; 326 } 327 mRequestQueue.RemoveElement(request); 328 break; 329 } 330 case IPCPaymentActionRequest::TIPCPaymentRetryActionRequest: { 331 const IPCPaymentRetryActionRequest& action = aAction; 332 MOZ_ASSERT(request); 333 request->UpdateErrors(action.error(), action.payerErrors(), 334 action.paymentMethodErrors(), 335 action.shippingAddressErrors()); 336 request->SetState(payments::PaymentRequest::eInteractive); 337 MOZ_ASSERT(mShowingRequest == request); 338 rv = LaunchUIAction( 339 aRequestId, IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest); 340 break; 341 } 342 default: { 343 return NS_ERROR_FAILURE; 344 } 345 } 346 return NS_OK; 347 } 348 349 NS_IMETHODIMP 350 PaymentRequestService::RespondPayment(nsIPaymentActionResponse* aResponse) { 351 NS_ENSURE_ARG_POINTER(aResponse); 352 nsAutoString requestId; 353 nsresult rv = aResponse->GetRequestId(requestId); 354 NS_ENSURE_SUCCESS(rv, rv); 355 356 RefPtr<payments::PaymentRequest> request; 357 rv = GetPaymentRequestById(requestId, getter_AddRefs(request)); 358 if (NS_WARN_IF(NS_FAILED(rv))) { 359 return rv; 360 } 361 if (!request) { 362 return NS_ERROR_FAILURE; 363 } 364 uint32_t type; 365 rv = aResponse->GetType(&type); 366 NS_ENSURE_SUCCESS(rv, rv); 367 368 // PaymentRequest can only be responded when 369 // 1. the state is eInteractive 370 // 2. the state is eClosed and response type is COMPLETE_ACTION 371 // 3. the state is eCreated and response type is CANMAKE_ACTION 372 payments::PaymentRequest::eState state = request->GetState(); 373 bool canBeResponded = (state == payments::PaymentRequest::eInteractive) || 374 (state == payments::PaymentRequest::eClosed && 375 type == nsIPaymentActionResponse::COMPLETE_ACTION) || 376 (state == payments::PaymentRequest::eCreated && 377 type == nsIPaymentActionResponse::CANMAKE_ACTION); 378 if (!canBeResponded) { 379 return NS_ERROR_FAILURE; 380 } 381 382 if (!request->GetIPC()) { 383 return NS_ERROR_FAILURE; 384 } 385 rv = request->GetIPC()->RespondPayment(aResponse); 386 if (NS_WARN_IF(NS_FAILED(rv))) { 387 return rv; 388 } 389 390 // Remove PaymentRequest from mRequestQueue while receive succeeded abort 391 // response or complete response 392 switch (type) { 393 case nsIPaymentActionResponse::ABORT_ACTION: { 394 nsCOMPtr<nsIPaymentAbortActionResponse> response = 395 do_QueryInterface(aResponse); 396 MOZ_ASSERT(response); 397 bool isSucceeded; 398 rv = response->IsSucceeded(&isSucceeded); 399 NS_ENSURE_SUCCESS(rv, rv); 400 mShowingRequest = nullptr; 401 if (isSucceeded) { 402 mRequestQueue.RemoveElement(request); 403 request->SetState(payments::PaymentRequest::eClosed); 404 } 405 break; 406 } 407 case nsIPaymentActionResponse::SHOW_ACTION: { 408 request->SetState(payments::PaymentRequest::eClosed); 409 nsCOMPtr<nsIPaymentShowActionResponse> response = 410 do_QueryInterface(aResponse); 411 MOZ_ASSERT(response); 412 uint32_t acceptStatus; 413 rv = response->GetAcceptStatus(&acceptStatus); 414 NS_ENSURE_SUCCESS(rv, rv); 415 if (acceptStatus != nsIPaymentActionResponse::PAYMENT_ACCEPTED) { 416 // Check if rejecting the showing PaymentRequest. 417 // If yes, set mShowingRequest as nullptr. 418 if (mShowingRequest == request) { 419 mShowingRequest = nullptr; 420 } 421 mRequestQueue.RemoveElement(request); 422 } 423 break; 424 } 425 case nsIPaymentActionResponse::COMPLETE_ACTION: { 426 mShowingRequest = nullptr; 427 mRequestQueue.RemoveElement(request); 428 break; 429 } 430 default: { 431 break; 432 } 433 } 434 return NS_OK; 435 } 436 437 NS_IMETHODIMP 438 PaymentRequestService::ChangeShippingAddress(const nsAString& aRequestId, 439 nsIPaymentAddress* aAddress) { 440 RefPtr<payments::PaymentRequest> request; 441 nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request)); 442 if (NS_WARN_IF(NS_FAILED(rv))) { 443 return rv; 444 } 445 if (!request) { 446 return NS_ERROR_FAILURE; 447 } 448 if (request->GetState() != payments::PaymentRequest::eInteractive) { 449 return NS_ERROR_FAILURE; 450 } 451 if (!request->GetIPC()) { 452 return NS_ERROR_FAILURE; 453 } 454 rv = request->GetIPC()->ChangeShippingAddress(aRequestId, aAddress); 455 if (NS_WARN_IF(NS_FAILED(rv))) { 456 return rv; 457 } 458 return NS_OK; 459 } 460 461 NS_IMETHODIMP 462 PaymentRequestService::ChangeShippingOption(const nsAString& aRequestId, 463 const nsAString& aOption) { 464 RefPtr<payments::PaymentRequest> request; 465 nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request)); 466 if (NS_WARN_IF(NS_FAILED(rv))) { 467 return rv; 468 } 469 if (!request) { 470 return NS_ERROR_FAILURE; 471 } 472 if (request->GetState() != payments::PaymentRequest::eInteractive) { 473 return NS_ERROR_FAILURE; 474 } 475 if (!request->GetIPC()) { 476 return NS_ERROR_FAILURE; 477 } 478 rv = request->GetIPC()->ChangeShippingOption(aRequestId, aOption); 479 if (NS_WARN_IF(NS_FAILED(rv))) { 480 return rv; 481 } 482 return NS_OK; 483 } 484 485 NS_IMETHODIMP 486 PaymentRequestService::ChangePayerDetail(const nsAString& aRequestId, 487 const nsAString& aPayerName, 488 const nsAString& aPayerEmail, 489 const nsAString& aPayerPhone) { 490 RefPtr<payments::PaymentRequest> request; 491 nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request)); 492 if (NS_WARN_IF(NS_FAILED(rv))) { 493 return rv; 494 } 495 MOZ_ASSERT(request); 496 if (!request->GetIPC()) { 497 return NS_ERROR_FAILURE; 498 } 499 rv = request->GetIPC()->ChangePayerDetail(aRequestId, aPayerName, aPayerEmail, 500 aPayerPhone); 501 if (NS_WARN_IF(NS_FAILED(rv))) { 502 return rv; 503 } 504 return NS_OK; 505 } 506 507 NS_IMETHODIMP 508 PaymentRequestService::ChangePaymentMethod( 509 const nsAString& aRequestId, const nsAString& aMethodName, 510 nsIMethodChangeDetails* aMethodDetails) { 511 RefPtr<payments::PaymentRequest> request; 512 nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request)); 513 if (NS_WARN_IF(NS_FAILED(rv))) { 514 return rv; 515 } 516 if (!request) { 517 return NS_ERROR_FAILURE; 518 } 519 if (request->GetState() != payments::PaymentRequest::eInteractive) { 520 return NS_ERROR_FAILURE; 521 } 522 if (!request->GetIPC()) { 523 return NS_ERROR_FAILURE; 524 } 525 rv = request->GetIPC()->ChangePaymentMethod(aRequestId, aMethodName, 526 aMethodDetails); 527 if (NS_WARN_IF(NS_FAILED(rv))) { 528 return rv; 529 } 530 return NS_OK; 531 } 532 533 bool PaymentRequestService::CanMakePayment(const nsAString& aRequestId) { 534 /* 535 * TODO: Check third party payment app support by traversing all 536 * registered third party payment apps. 537 */ 538 return IsBasicCardPayment(aRequestId); 539 } 540 541 nsresult PaymentRequestService::ShowPayment(const nsAString& aRequestId, 542 bool aIsUpdating) { 543 nsresult rv; 544 RefPtr<payments::PaymentRequest> request; 545 rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request)); 546 if (NS_WARN_IF(NS_FAILED(rv))) { 547 return rv; 548 } 549 MOZ_ASSERT(request); 550 request->SetState(payments::PaymentRequest::eInteractive); 551 if (aIsUpdating) { 552 request->SetCompleteStatus(u"initial"_ns); 553 } 554 555 if (mShowingRequest || !CanMakePayment(aRequestId)) { 556 uint32_t responseStatus; 557 if (mShowingRequest) { 558 responseStatus = nsIPaymentActionResponse::PAYMENT_REJECTED; 559 } else { 560 responseStatus = nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED; 561 } 562 nsCOMPtr<nsIPaymentShowActionResponse> showResponse = 563 do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID); 564 MOZ_ASSERT(showResponse); 565 rv = showResponse->Init(aRequestId, responseStatus, u""_ns, nullptr, u""_ns, 566 u""_ns, u""_ns); 567 rv = RespondPayment(showResponse.get()); 568 if (NS_WARN_IF(NS_FAILED(rv))) { 569 return rv; 570 } 571 } else { 572 mShowingRequest = request; 573 rv = LaunchUIAction(aRequestId, 574 IPCPaymentActionRequest::TIPCPaymentShowActionRequest); 575 if (NS_WARN_IF(NS_FAILED(rv))) { 576 return rv; 577 } 578 } 579 return NS_OK; 580 } 581 582 bool PaymentRequestService::IsBasicCardPayment(const nsAString& aRequestId) { 583 RefPtr<payments::PaymentRequest> request; 584 nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request)); 585 NS_ENSURE_SUCCESS(rv, false); 586 nsCOMPtr<nsIArray> methods; 587 rv = request->GetPaymentMethods(getter_AddRefs(methods)); 588 NS_ENSURE_SUCCESS(rv, false); 589 uint32_t length; 590 rv = methods->GetLength(&length); 591 NS_ENSURE_SUCCESS(rv, false); 592 RefPtr<BasicCardService> service = BasicCardService::GetService(); 593 MOZ_ASSERT(service); 594 for (uint32_t index = 0; index < length; ++index) { 595 nsCOMPtr<nsIPaymentMethodData> method = do_QueryElementAt(methods, index); 596 MOZ_ASSERT(method); 597 nsAutoString supportedMethods; 598 rv = method->GetSupportedMethods(supportedMethods); 599 NS_ENSURE_SUCCESS(rv, false); 600 if (service->IsBasicCardPayment(supportedMethods)) { 601 return true; 602 } 603 } 604 return false; 605 } 606 607 } // namespace mozilla::dom