PaymentRequestManager.cpp (26838B)
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 "PaymentRequestManager.h" 8 9 #include "PaymentRequestUtils.h" 10 #include "PaymentResponse.h" 11 #include "mozilla/ClearOnShutdown.h" 12 #include "mozilla/Preferences.h" 13 #include "mozilla/dom/BrowserChild.h" 14 #include "mozilla/dom/PaymentRequestChild.h" 15 #include "nsContentUtils.h" 16 #include "nsIPaymentActionResponse.h" 17 #include "nsIPrincipal.h" 18 #include "nsString.h" 19 20 namespace mozilla::dom { 21 namespace { 22 23 /* 24 * Following Convert* functions are used for convert PaymentRequest structs 25 * to transferable structs for IPC. 26 */ 27 void ConvertMethodData(JSContext* aCx, const PaymentMethodData& aMethodData, 28 IPCPaymentMethodData& aIPCMethodData, ErrorResult& aRv) { 29 MOZ_ASSERT(aCx); 30 // Convert JSObject to a serialized string 31 nsAutoString serializedData; 32 if (aMethodData.mData.WasPassed()) { 33 JS::Rooted<JSObject*> object(aCx, aMethodData.mData.Value()); 34 if (NS_WARN_IF( 35 NS_FAILED(SerializeFromJSObject(aCx, object, serializedData)))) { 36 aRv.ThrowTypeError( 37 "The PaymentMethodData.data must be a serializable object"); 38 return; 39 } 40 } 41 aIPCMethodData = 42 IPCPaymentMethodData(aMethodData.mSupportedMethods, serializedData); 43 } 44 45 void ConvertCurrencyAmount(const PaymentCurrencyAmount& aAmount, 46 IPCPaymentCurrencyAmount& aIPCCurrencyAmount) { 47 aIPCCurrencyAmount = 48 IPCPaymentCurrencyAmount(aAmount.mCurrency, aAmount.mValue); 49 } 50 51 void ConvertItem(const PaymentItem& aItem, IPCPaymentItem& aIPCItem) { 52 IPCPaymentCurrencyAmount amount; 53 ConvertCurrencyAmount(aItem.mAmount, amount); 54 aIPCItem = IPCPaymentItem(aItem.mLabel, amount, aItem.mPending); 55 } 56 57 void ConvertModifier(JSContext* aCx, const PaymentDetailsModifier& aModifier, 58 IPCPaymentDetailsModifier& aIPCModifier, 59 ErrorResult& aRv) { 60 MOZ_ASSERT(aCx); 61 // Convert JSObject to a serialized string 62 nsAutoString serializedData; 63 if (aModifier.mData.WasPassed()) { 64 JS::Rooted<JSObject*> object(aCx, aModifier.mData.Value()); 65 if (NS_WARN_IF( 66 NS_FAILED(SerializeFromJSObject(aCx, object, serializedData)))) { 67 aRv.ThrowTypeError("The Modifier.data must be a serializable object"); 68 return; 69 } 70 } 71 72 IPCPaymentItem total; 73 if (aModifier.mTotal.WasPassed()) { 74 ConvertItem(aModifier.mTotal.Value(), total); 75 } 76 77 nsTArray<IPCPaymentItem> additionalDisplayItems; 78 if (aModifier.mAdditionalDisplayItems.WasPassed()) { 79 for (const PaymentItem& item : aModifier.mAdditionalDisplayItems.Value()) { 80 IPCPaymentItem displayItem; 81 ConvertItem(item, displayItem); 82 additionalDisplayItems.AppendElement(displayItem); 83 } 84 } 85 aIPCModifier = IPCPaymentDetailsModifier( 86 aModifier.mSupportedMethods, total, additionalDisplayItems, 87 serializedData, aModifier.mAdditionalDisplayItems.WasPassed()); 88 } 89 90 void ConvertShippingOption(const PaymentShippingOption& aOption, 91 IPCPaymentShippingOption& aIPCOption) { 92 IPCPaymentCurrencyAmount amount; 93 ConvertCurrencyAmount(aOption.mAmount, amount); 94 aIPCOption = IPCPaymentShippingOption(aOption.mId, aOption.mLabel, amount, 95 aOption.mSelected); 96 } 97 98 void ConvertDetailsBase(JSContext* aCx, const PaymentDetailsBase& aDetails, 99 nsTArray<IPCPaymentItem>& aDisplayItems, 100 nsTArray<IPCPaymentShippingOption>& aShippingOptions, 101 nsTArray<IPCPaymentDetailsModifier>& aModifiers, 102 bool aRequestShipping, ErrorResult& aRv) { 103 MOZ_ASSERT(aCx); 104 if (aDetails.mDisplayItems.WasPassed()) { 105 for (const PaymentItem& item : aDetails.mDisplayItems.Value()) { 106 IPCPaymentItem displayItem; 107 ConvertItem(item, displayItem); 108 aDisplayItems.AppendElement(displayItem); 109 } 110 } 111 if (aRequestShipping && aDetails.mShippingOptions.WasPassed()) { 112 for (const PaymentShippingOption& option : 113 aDetails.mShippingOptions.Value()) { 114 IPCPaymentShippingOption shippingOption; 115 ConvertShippingOption(option, shippingOption); 116 aShippingOptions.AppendElement(shippingOption); 117 } 118 } 119 if (aDetails.mModifiers.WasPassed()) { 120 for (const PaymentDetailsModifier& modifier : aDetails.mModifiers.Value()) { 121 IPCPaymentDetailsModifier detailsModifier; 122 ConvertModifier(aCx, modifier, detailsModifier, aRv); 123 if (aRv.Failed()) { 124 return; 125 } 126 aModifiers.AppendElement(detailsModifier); 127 } 128 } 129 } 130 131 void ConvertDetailsInit(JSContext* aCx, const PaymentDetailsInit& aDetails, 132 IPCPaymentDetails& aIPCDetails, bool aRequestShipping, 133 ErrorResult& aRv) { 134 MOZ_ASSERT(aCx); 135 // Convert PaymentDetailsBase members 136 nsTArray<IPCPaymentItem> displayItems; 137 nsTArray<IPCPaymentShippingOption> shippingOptions; 138 nsTArray<IPCPaymentDetailsModifier> modifiers; 139 ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions, modifiers, 140 aRequestShipping, aRv); 141 if (aRv.Failed()) { 142 return; 143 } 144 145 // Convert |id| 146 nsAutoString id; 147 if (aDetails.mId.WasPassed()) { 148 id = aDetails.mId.Value(); 149 } 150 151 // Convert required |total| 152 IPCPaymentItem total; 153 ConvertItem(aDetails.mTotal, total); 154 155 aIPCDetails = 156 IPCPaymentDetails(id, total, displayItems, shippingOptions, modifiers, 157 u""_ns, // error message 158 u""_ns, // shippingAddressErrors 159 u""_ns, // payerErrors 160 u""_ns); // paymentMethodErrors 161 } 162 163 void ConvertDetailsUpdate(JSContext* aCx, const PaymentDetailsUpdate& aDetails, 164 IPCPaymentDetails& aIPCDetails, bool aRequestShipping, 165 ErrorResult& aRv) { 166 MOZ_ASSERT(aCx); 167 // Convert PaymentDetailsBase members 168 nsTArray<IPCPaymentItem> displayItems; 169 nsTArray<IPCPaymentShippingOption> shippingOptions; 170 nsTArray<IPCPaymentDetailsModifier> modifiers; 171 ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions, modifiers, 172 aRequestShipping, aRv); 173 if (aRv.Failed()) { 174 return; 175 } 176 177 // Convert required |total| 178 IPCPaymentItem total; 179 if (aDetails.mTotal.WasPassed()) { 180 ConvertItem(aDetails.mTotal.Value(), total); 181 } 182 183 // Convert |error| 184 nsAutoString error; 185 if (aDetails.mError.WasPassed()) { 186 error = aDetails.mError.Value(); 187 } 188 189 nsAutoString shippingAddressErrors; 190 if (aDetails.mShippingAddressErrors.WasPassed()) { 191 if (!aDetails.mShippingAddressErrors.Value().ToJSON( 192 shippingAddressErrors)) { 193 aRv.ThrowTypeError("The ShippingAddressErrors can not be serailized"); 194 return; 195 } 196 } 197 198 nsAutoString payerErrors; 199 if (aDetails.mPayerErrors.WasPassed()) { 200 if (!aDetails.mPayerErrors.Value().ToJSON(payerErrors)) { 201 aRv.ThrowTypeError("The PayerErrors can not be serialized"); 202 return; 203 } 204 } 205 206 nsAutoString paymentMethodErrors; 207 if (aDetails.mPaymentMethodErrors.WasPassed()) { 208 JS::Rooted<JSObject*> object(aCx, aDetails.mPaymentMethodErrors.Value()); 209 if (NS_WARN_IF(NS_FAILED( 210 SerializeFromJSObject(aCx, object, paymentMethodErrors)))) { 211 aRv.ThrowTypeError("The PaymentMethodErrors can not be serialized"); 212 return; 213 } 214 } 215 216 aIPCDetails = IPCPaymentDetails(u""_ns, // id 217 total, displayItems, shippingOptions, 218 modifiers, error, shippingAddressErrors, 219 payerErrors, paymentMethodErrors); 220 } 221 222 void ConvertOptions(const PaymentOptions& aOptions, 223 IPCPaymentOptions& aIPCOption) { 224 NS_ConvertASCIItoUTF16 shippingType(GetEnumString(aOptions.mShippingType)); 225 aIPCOption = 226 IPCPaymentOptions(aOptions.mRequestPayerName, aOptions.mRequestPayerEmail, 227 aOptions.mRequestPayerPhone, aOptions.mRequestShipping, 228 aOptions.mRequestBillingAddress, shippingType); 229 } 230 231 void ConvertResponseData(const IPCPaymentResponseData& aIPCData, 232 ResponseData& aData) { 233 switch (aIPCData.type()) { 234 case IPCPaymentResponseData::TIPCGeneralResponse: { 235 const IPCGeneralResponse& data = aIPCData; 236 GeneralData gData; 237 gData.data = data.data(); 238 aData = gData; 239 break; 240 } 241 case IPCPaymentResponseData::TIPCBasicCardResponse: { 242 const IPCBasicCardResponse& data = aIPCData; 243 BasicCardData bData; 244 bData.cardholderName = data.cardholderName(); 245 bData.cardNumber = data.cardNumber(); 246 bData.expiryMonth = data.expiryMonth(); 247 bData.expiryYear = data.expiryYear(); 248 bData.cardSecurityCode = data.cardSecurityCode(); 249 bData.billingAddress.country = data.billingAddress().country(); 250 bData.billingAddress.addressLine = 251 data.billingAddress().addressLine().Clone(); 252 bData.billingAddress.region = data.billingAddress().region(); 253 bData.billingAddress.regionCode = data.billingAddress().regionCode(); 254 bData.billingAddress.city = data.billingAddress().city(); 255 bData.billingAddress.dependentLocality = 256 data.billingAddress().dependentLocality(); 257 bData.billingAddress.postalCode = data.billingAddress().postalCode(); 258 bData.billingAddress.sortingCode = data.billingAddress().sortingCode(); 259 bData.billingAddress.organization = data.billingAddress().organization(); 260 bData.billingAddress.recipient = data.billingAddress().recipient(); 261 bData.billingAddress.phone = data.billingAddress().phone(); 262 aData = bData; 263 break; 264 } 265 default: { 266 break; 267 } 268 } 269 } 270 271 void ConvertMethodChangeDetails(const IPCMethodChangeDetails& aIPCDetails, 272 ChangeDetails& aDetails) { 273 switch (aIPCDetails.type()) { 274 case IPCMethodChangeDetails::TIPCGeneralChangeDetails: { 275 const IPCGeneralChangeDetails& details = aIPCDetails; 276 GeneralDetails gDetails; 277 gDetails.details = details.details(); 278 aDetails = gDetails; 279 break; 280 } 281 case IPCMethodChangeDetails::TIPCBasicCardChangeDetails: { 282 const IPCBasicCardChangeDetails& details = aIPCDetails; 283 BasicCardDetails bDetails; 284 bDetails.billingAddress.country = details.billingAddress().country(); 285 bDetails.billingAddress.addressLine = 286 details.billingAddress().addressLine(); 287 bDetails.billingAddress.region = details.billingAddress().region(); 288 bDetails.billingAddress.regionCode = 289 details.billingAddress().regionCode(); 290 bDetails.billingAddress.city = details.billingAddress().city(); 291 bDetails.billingAddress.dependentLocality = 292 details.billingAddress().dependentLocality(); 293 bDetails.billingAddress.postalCode = 294 details.billingAddress().postalCode(); 295 bDetails.billingAddress.sortingCode = 296 details.billingAddress().sortingCode(); 297 bDetails.billingAddress.organization = 298 details.billingAddress().organization(); 299 bDetails.billingAddress.recipient = details.billingAddress().recipient(); 300 bDetails.billingAddress.phone = details.billingAddress().phone(); 301 aDetails = bDetails; 302 break; 303 } 304 default: { 305 break; 306 } 307 } 308 } 309 } // end of namespace 310 311 /* PaymentRequestManager */ 312 313 StaticRefPtr<PaymentRequestManager> gPaymentManager; 314 const char kSupportedRegionsPref[] = "dom.payments.request.supportedRegions"; 315 316 void SupportedRegionsPrefChangedCallback(const char* aPrefName, void* aRetval) { 317 auto retval = static_cast<nsTArray<nsString>*>(aRetval); 318 MOZ_ASSERT(NS_IsMainThread()); 319 MOZ_ASSERT(!strcmp(aPrefName, kSupportedRegionsPref)); 320 321 nsAutoString supportedRegions; 322 Preferences::GetString(aPrefName, supportedRegions); 323 retval->Clear(); 324 for (const nsAString& each : supportedRegions.Split(',')) { 325 retval->AppendElement(each); 326 } 327 } 328 329 PaymentRequestManager::PaymentRequestManager() { 330 Preferences::RegisterCallbackAndCall(SupportedRegionsPrefChangedCallback, 331 kSupportedRegionsPref, 332 &this->mSupportedRegions); 333 } 334 335 PaymentRequestManager::~PaymentRequestManager() { 336 MOZ_ASSERT(mActivePayments.Count() == 0); 337 Preferences::UnregisterCallback(SupportedRegionsPrefChangedCallback, 338 kSupportedRegionsPref, 339 &this->mSupportedRegions); 340 mSupportedRegions.Clear(); 341 } 342 343 bool PaymentRequestManager::IsRegionSupported(const nsAString& region) const { 344 return mSupportedRegions.Contains(region); 345 } 346 347 PaymentRequestChild* PaymentRequestManager::GetPaymentChild( 348 PaymentRequest* aRequest) { 349 MOZ_ASSERT(aRequest); 350 351 if (PaymentRequestChild* child = aRequest->GetIPC()) { 352 return child; 353 } 354 355 nsPIDOMWindowInner* win = aRequest->GetOwnerWindow(); 356 NS_ENSURE_TRUE(win, nullptr); 357 BrowserChild* browserChild = BrowserChild::GetFrom(win->GetDocShell()); 358 NS_ENSURE_TRUE(browserChild, nullptr); 359 nsAutoString requestId; 360 aRequest->GetInternalId(requestId); 361 362 PaymentRequestChild* paymentChild = new PaymentRequestChild(aRequest); 363 if (!browserChild->SendPPaymentRequestConstructor(paymentChild)) { 364 // deleted by Constructor 365 return nullptr; 366 } 367 368 return paymentChild; 369 } 370 371 nsresult PaymentRequestManager::SendRequestPayment( 372 PaymentRequest* aRequest, const IPCPaymentActionRequest& aAction, 373 bool aResponseExpected) { 374 PaymentRequestChild* requestChild = GetPaymentChild(aRequest); 375 // bug 1580496, ignoring the case that requestChild is nullptr. It could be 376 // nullptr while the corresponding nsPIDOMWindowInner is nullptr. 377 if (NS_WARN_IF(!requestChild)) { 378 return NS_ERROR_FAILURE; 379 } 380 nsresult rv = requestChild->RequestPayment(aAction); 381 if (NS_WARN_IF(NS_FAILED(rv))) { 382 return rv; 383 } 384 385 if (aResponseExpected) { 386 ++mActivePayments.LookupOrInsert(aRequest, 0); 387 } 388 return NS_OK; 389 } 390 391 void PaymentRequestManager::NotifyRequestDone(PaymentRequest* aRequest) { 392 auto entry = mActivePayments.Lookup(aRequest); 393 MOZ_ASSERT(entry); 394 MOZ_ASSERT(entry.Data() > 0); 395 396 uint32_t count = --entry.Data(); 397 if (count == 0) { 398 entry.Remove(); 399 } 400 } 401 402 void PaymentRequestManager::RequestIPCOver(PaymentRequest* aRequest) { 403 // This must only be called from ActorDestroy or if we're sure we won't 404 // receive any more IPC for aRequest. 405 mActivePayments.Remove(aRequest); 406 } 407 408 already_AddRefed<PaymentRequestManager> PaymentRequestManager::GetSingleton() { 409 if (!gPaymentManager) { 410 gPaymentManager = new PaymentRequestManager(); 411 ClearOnShutdown(&gPaymentManager); 412 } 413 RefPtr<PaymentRequestManager> manager = gPaymentManager; 414 return manager.forget(); 415 } 416 417 void GetSelectedShippingOption(const PaymentDetailsBase& aDetails, 418 nsAString& aOption) { 419 SetDOMStringToNull(aOption); 420 if (!aDetails.mShippingOptions.WasPassed()) { 421 return; 422 } 423 424 const Sequence<PaymentShippingOption>& shippingOptions = 425 aDetails.mShippingOptions.Value(); 426 for (const PaymentShippingOption& shippingOption : shippingOptions) { 427 // set aOption to last selected option's ID 428 if (shippingOption.mSelected) { 429 aOption = shippingOption.mId; 430 } 431 } 432 } 433 434 void PaymentRequestManager::CreatePayment( 435 JSContext* aCx, nsPIDOMWindowInner* aWindow, 436 nsIPrincipal* aTopLevelPrincipal, 437 const Sequence<PaymentMethodData>& aMethodData, 438 const PaymentDetailsInit& aDetails, const PaymentOptions& aOptions, 439 PaymentRequest** aRequest, ErrorResult& aRv) { 440 MOZ_ASSERT(NS_IsMainThread()); 441 MOZ_ASSERT(aCx); 442 MOZ_ASSERT(aRequest); 443 MOZ_ASSERT(aTopLevelPrincipal); 444 *aRequest = nullptr; 445 446 RefPtr<PaymentRequest> request = 447 PaymentRequest::CreatePaymentRequest(aWindow, aRv); 448 if (aRv.Failed()) { 449 return; 450 } 451 request->SetOptions(aOptions); 452 /* 453 * Set request's |mId| to details.id if details.id exists. 454 * Otherwise, set |mId| to internal id. 455 */ 456 nsAutoString requestId; 457 if (aDetails.mId.WasPassed() && !aDetails.mId.Value().IsEmpty()) { 458 requestId = aDetails.mId.Value(); 459 } else { 460 request->GetInternalId(requestId); 461 } 462 request->SetId(requestId); 463 464 /* 465 * Set request's |mShippingType| and |mShippingOption| if shipping is 466 * required. Set request's mShippingOption to last selected option's ID if 467 * details.shippingOptions exists, otherwise set it as null. 468 */ 469 nsAutoString shippingOption; 470 SetDOMStringToNull(shippingOption); 471 if (aOptions.mRequestShipping) { 472 request->ShippingWasRequested(); 473 request->SetShippingType( 474 Nullable<PaymentShippingType>(aOptions.mShippingType)); 475 GetSelectedShippingOption(aDetails, shippingOption); 476 } 477 request->SetShippingOption(shippingOption); 478 479 nsAutoString internalId; 480 request->GetInternalId(internalId); 481 482 nsTArray<IPCPaymentMethodData> methodData; 483 for (const PaymentMethodData& data : aMethodData) { 484 IPCPaymentMethodData ipcMethodData; 485 ConvertMethodData(aCx, data, ipcMethodData, aRv); 486 if (aRv.Failed()) { 487 return; 488 } 489 methodData.AppendElement(ipcMethodData); 490 } 491 492 IPCPaymentDetails details; 493 ConvertDetailsInit(aCx, aDetails, details, aOptions.mRequestShipping, aRv); 494 if (aRv.Failed()) { 495 return; 496 } 497 498 IPCPaymentOptions options; 499 ConvertOptions(aOptions, options); 500 501 uint64_t topOuterWindowId = 502 aWindow->GetWindowContext()->TopWindowContext()->OuterWindowId(); 503 IPCPaymentCreateActionRequest action(topOuterWindowId, internalId, 504 aTopLevelPrincipal, methodData, details, 505 options, shippingOption); 506 507 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(request, action, false)))) { 508 aRv.ThrowUnknownError("Internal error sending payment request"); 509 return; 510 } 511 request.forget(aRequest); 512 } 513 514 void PaymentRequestManager::CanMakePayment(PaymentRequest* aRequest, 515 ErrorResult& aRv) { 516 nsAutoString requestId; 517 aRequest->GetInternalId(requestId); 518 IPCPaymentCanMakeActionRequest action(requestId); 519 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) { 520 aRv.ThrowUnknownError("Internal error sending payment request"); 521 } 522 } 523 524 void PaymentRequestManager::ShowPayment(PaymentRequest* aRequest, 525 ErrorResult& aRv) { 526 nsAutoString requestId; 527 aRequest->GetInternalId(requestId); 528 IPCPaymentShowActionRequest action(requestId, aRequest->IsUpdating()); 529 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) { 530 aRv.ThrowUnknownError("Internal error sending payment request"); 531 } 532 } 533 534 void PaymentRequestManager::AbortPayment(PaymentRequest* aRequest, 535 ErrorResult& aRv) { 536 nsAutoString requestId; 537 aRequest->GetInternalId(requestId); 538 IPCPaymentAbortActionRequest action(requestId); 539 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) { 540 aRv.ThrowUnknownError("Internal error sending payment request"); 541 } 542 } 543 544 void PaymentRequestManager::CompletePayment(PaymentRequest* aRequest, 545 const PaymentComplete& aComplete, 546 ErrorResult& aRv, bool aTimedOut) { 547 nsString completeStatusString(u"unknown"_ns); 548 if (aTimedOut) { 549 completeStatusString.AssignLiteral("timeout"); 550 } else { 551 completeStatusString.AssignASCII(GetEnumString(aComplete)); 552 } 553 554 nsAutoString requestId; 555 aRequest->GetInternalId(requestId); 556 IPCPaymentCompleteActionRequest action(requestId, completeStatusString); 557 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action, false)))) { 558 aRv.ThrowUnknownError("Internal error sending payment request"); 559 } 560 } 561 562 void PaymentRequestManager::UpdatePayment(JSContext* aCx, 563 PaymentRequest* aRequest, 564 const PaymentDetailsUpdate& aDetails, 565 bool aRequestShipping, 566 ErrorResult& aRv) { 567 MOZ_ASSERT(aCx); 568 IPCPaymentDetails details; 569 ConvertDetailsUpdate(aCx, aDetails, details, aRequestShipping, aRv); 570 if (aRv.Failed()) { 571 return; 572 } 573 574 nsAutoString shippingOption; 575 SetDOMStringToNull(shippingOption); 576 if (aRequestShipping) { 577 GetSelectedShippingOption(aDetails, shippingOption); 578 aRequest->SetShippingOption(shippingOption); 579 } 580 581 nsAutoString requestId; 582 aRequest->GetInternalId(requestId); 583 IPCPaymentUpdateActionRequest action(requestId, details, shippingOption); 584 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action, false)))) { 585 aRv.ThrowUnknownError("Internal error sending payment request"); 586 } 587 } 588 589 nsresult PaymentRequestManager::ClosePayment(PaymentRequest* aRequest) { 590 // for the case, the payment request is waiting for response from user. 591 if (auto entry = mActivePayments.Lookup(aRequest)) { 592 NotifyRequestDone(aRequest); 593 } 594 nsAutoString requestId; 595 aRequest->GetInternalId(requestId); 596 IPCPaymentCloseActionRequest action(requestId); 597 return SendRequestPayment(aRequest, action, false); 598 } 599 600 void PaymentRequestManager::RetryPayment(JSContext* aCx, 601 PaymentRequest* aRequest, 602 const PaymentValidationErrors& aErrors, 603 ErrorResult& aRv) { 604 MOZ_ASSERT(aCx); 605 MOZ_ASSERT(aRequest); 606 607 nsAutoString requestId; 608 aRequest->GetInternalId(requestId); 609 610 nsAutoString error; 611 if (aErrors.mError.WasPassed()) { 612 error = aErrors.mError.Value(); 613 } 614 615 nsAutoString shippingAddressErrors; 616 if (aErrors.mShippingAddress.WasPassed()) { 617 if (!aErrors.mShippingAddress.Value().ToJSON(shippingAddressErrors)) { 618 aRv.ThrowTypeError("The ShippingAddressErrors can not be serialized"); 619 return; 620 } 621 } 622 623 nsAutoString payerErrors; 624 if (aErrors.mPayer.WasPassed()) { 625 if (!aErrors.mPayer.Value().ToJSON(payerErrors)) { 626 aRv.ThrowTypeError("The PayerErrors can not be serialized"); 627 return; 628 } 629 } 630 631 nsAutoString paymentMethodErrors; 632 if (aErrors.mPaymentMethod.WasPassed()) { 633 JS::Rooted<JSObject*> object(aCx, aErrors.mPaymentMethod.Value()); 634 if (NS_WARN_IF(NS_FAILED( 635 SerializeFromJSObject(aCx, object, paymentMethodErrors)))) { 636 aRv.ThrowTypeError("The PaymentMethodErrors can not be serialized"); 637 return; 638 } 639 } 640 IPCPaymentRetryActionRequest action(requestId, error, payerErrors, 641 paymentMethodErrors, 642 shippingAddressErrors); 643 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) { 644 aRv.ThrowUnknownError("Internal error sending payment request"); 645 } 646 } 647 648 nsresult PaymentRequestManager::RespondPayment( 649 PaymentRequest* aRequest, const IPCPaymentActionResponse& aResponse) { 650 switch (aResponse.type()) { 651 case IPCPaymentActionResponse::TIPCPaymentCanMakeActionResponse: { 652 const IPCPaymentCanMakeActionResponse& response = aResponse; 653 aRequest->RespondCanMakePayment(response.result()); 654 NotifyRequestDone(aRequest); 655 break; 656 } 657 case IPCPaymentActionResponse::TIPCPaymentShowActionResponse: { 658 const IPCPaymentShowActionResponse& response = aResponse; 659 ErrorResult rejectedReason; 660 ResponseData responseData; 661 ConvertResponseData(response.data(), responseData); 662 switch (response.status()) { 663 case nsIPaymentActionResponse::PAYMENT_ACCEPTED: { 664 break; 665 } 666 case nsIPaymentActionResponse::PAYMENT_REJECTED: { 667 rejectedReason.ThrowAbortError("The user rejected the payment"); 668 break; 669 } 670 case nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED: { 671 rejectedReason.ThrowNotSupportedError("No supported payment method"); 672 break; 673 } 674 default: { 675 rejectedReason.ThrowUnknownError("Unknown response for the payment"); 676 break; 677 } 678 } 679 // If PaymentActionResponse is not PAYMENT_ACCEPTED, no need to keep the 680 // PaymentRequestChild instance. Otherwise, keep PaymentRequestChild for 681 // merchants call PaymentResponse.complete() 682 if (rejectedReason.Failed()) { 683 NotifyRequestDone(aRequest); 684 } 685 aRequest->RespondShowPayment(response.methodName(), responseData, 686 response.payerName(), response.payerEmail(), 687 response.payerPhone(), 688 std::move(rejectedReason)); 689 break; 690 } 691 case IPCPaymentActionResponse::TIPCPaymentAbortActionResponse: { 692 const IPCPaymentAbortActionResponse& response = aResponse; 693 aRequest->RespondAbortPayment(response.isSucceeded()); 694 NotifyRequestDone(aRequest); 695 break; 696 } 697 case IPCPaymentActionResponse::TIPCPaymentCompleteActionResponse: { 698 aRequest->RespondComplete(); 699 NotifyRequestDone(aRequest); 700 break; 701 } 702 default: { 703 return NS_ERROR_FAILURE; 704 } 705 } 706 return NS_OK; 707 } 708 709 nsresult PaymentRequestManager::ChangeShippingAddress( 710 PaymentRequest* aRequest, const IPCPaymentAddress& aAddress) { 711 return aRequest->UpdateShippingAddress( 712 aAddress.country(), aAddress.addressLine(), aAddress.region(), 713 aAddress.regionCode(), aAddress.city(), aAddress.dependentLocality(), 714 aAddress.postalCode(), aAddress.sortingCode(), aAddress.organization(), 715 aAddress.recipient(), aAddress.phone()); 716 } 717 718 nsresult PaymentRequestManager::ChangeShippingOption(PaymentRequest* aRequest, 719 const nsAString& aOption) { 720 return aRequest->UpdateShippingOption(aOption); 721 } 722 723 nsresult PaymentRequestManager::ChangePayerDetail( 724 PaymentRequest* aRequest, const nsAString& aPayerName, 725 const nsAString& aPayerEmail, const nsAString& aPayerPhone) { 726 MOZ_ASSERT(aRequest); 727 RefPtr<PaymentResponse> response = aRequest->GetResponse(); 728 // ignoring the case call changePayerDetail during show(). 729 if (!response) { 730 return NS_OK; 731 } 732 return response->UpdatePayerDetail(aPayerName, aPayerEmail, aPayerPhone); 733 } 734 735 nsresult PaymentRequestManager::ChangePaymentMethod( 736 PaymentRequest* aRequest, const nsAString& aMethodName, 737 const IPCMethodChangeDetails& aMethodDetails) { 738 NS_ENSURE_ARG_POINTER(aRequest); 739 ChangeDetails methodDetails; 740 ConvertMethodChangeDetails(aMethodDetails, methodDetails); 741 return aRequest->UpdatePaymentMethod(aMethodName, methodDetails); 742 } 743 744 } // namespace mozilla::dom