Bug 1497215 - Refactor the IPC structure for PaymentResponse.details r=baku
☠☠ backed out by dedd03c893c8 ☠ ☠
authorEden Chuang <echuang@mozilla.com>
Mon, 22 Oct 2018 11:08:04 +0200
changeset 490630 fa6b7a70f2db81835c314543031fb6ce251fafce
parent 490629 47db66520bef4128c348f815dd4d75ea98335d0f
child 490631 430db29f46858faff930e2ec3ed45fbf13a73a20
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersbaku
bugs1497215
milestone64.0a1
Bug 1497215 - Refactor the IPC structure for PaymentResponse.details r=baku In original design, payment method response data is passed between processes through a simple nsString. It means a special encoder/decoder is needed for special response data, ex. BasicCardResponse, to serialize/deserialize into/from the nsString. However, when a token spliter, ':', ';' and '@', is used in response data, it makes the encoder/decoder can not work normally. It is hard to define a suitable token spliter set for encoder/decoder. So instead of using an error-prone encoder/decoder, this patch defining a new IPC structure for response data.
dom/interfaces/payments/nsIPaymentActionResponse.idl
dom/payments/BasicCardPayment.cpp
dom/payments/BasicCardPayment.h
dom/payments/PaymentActionResponse.cpp
dom/payments/PaymentActionResponse.h
dom/payments/PaymentRequest.cpp
dom/payments/PaymentRequest.h
dom/payments/PaymentRequestManager.cpp
dom/payments/PaymentResponse.cpp
dom/payments/PaymentResponse.h
dom/payments/ipc/PPaymentRequest.ipdl
dom/payments/ipc/PaymentRequestParent.cpp
dom/payments/ipc/PaymentRequestParent.h
dom/payments/test/BasiccardChromeScript.js
dom/payments/test/test_basiccard.html
--- a/dom/interfaces/payments/nsIPaymentActionResponse.idl
+++ b/dom/interfaces/payments/nsIPaymentActionResponse.idl
@@ -64,19 +64,44 @@ interface nsIGeneralResponseData : nsIPa
  *  given JSObject directly, because PaymentAddress creation in JS code is hard.
  *  To let UI code can create BasicCardResponse easier, nsIBasicCardResponse is
  *  provided for UI by passing the raw data of BasicCardResponse,
  */
 [builtinclass, scriptable, uuid(0d55a5e6-d185-44f0-b992-a8e1321e4bce)]
 interface nsIBasicCardResponseData : nsIPaymentResponseData
 {
   /**
-   *  The stringified response data.
+   *  The cardholder name.
+   */
+  readonly attribute AString cardholderName;
+
+  /**
+   *  The card number.
+   */
+  readonly attribute AString cardNumber;
+
+  /**
+   *  The expiry month.
    */
-  readonly attribute AString data;
+  readonly attribute AString expiryMonth;
+
+  /**
+   *  The expiry year.
+   */
+  readonly attribute AString expiryYear;
+
+  /**
+   *  The card security number.
+   */
+  readonly attribute AString cardSecurityCode;
+
+  /**
+   *  The billing address.
+   */
+  readonly attribute nsIPaymentAddress billingAddress;
 
   /**
    *  The initial method for nsIBasicCardResponseData.
    *  @param aCardholderName   - the cardholder name.
    *  @param aCardNumber       - the card number.
    *  @param aExpiryMonth      - the expiry month.
    *  @param aExpiryYear       - the expiry year.
    *  @param aCardSecurityCode - the card security code.
@@ -178,17 +203,17 @@ interface nsIPaymentShowActionResponse :
   /**
    *  The decided payment method name. i.e. "basic-card".
    */
   readonly attribute AString methodName;
 
   /**
    *  The data needed by the payment method. (it must be serializable)
    */
-  readonly attribute AString data;
+  readonly attribute nsIPaymentResponseData data;
 
   /**
    *  The payer name information.
    */
   readonly attribute AString payerName;
 
   /**
    *  The payer email information.
--- a/dom/payments/BasicCardPayment.cpp
+++ b/dom/payments/BasicCardPayment.cpp
@@ -9,127 +9,29 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "nsArrayUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsDataHashtable.h"
 
 namespace mozilla {
 namespace dom {
-#ifndef PaymentBasicCardMacros
-#define PaymentBasicCardMacros
-
-#define AMEX NS_LITERAL_STRING("amex")
-#define CARTEBANCAIRE NS_LITERAL_STRING("cartebancaire")
-#define DINERS NS_LITERAL_STRING("diners")
-#define DISCOVER NS_LITERAL_STRING("discover")
-#define JCB NS_LITERAL_STRING("jcb")
-#define MASTERCARD NS_LITERAL_STRING("mastercard")
-#define MIR NS_LITERAL_STRING("mir")
-#define UNIONPAY NS_LITERAL_STRING("unionpay")
-#define VISA NS_LITERAL_STRING("visa")
-
-#define CardholderName NS_LITERAL_STRING("cardholderName")
-#define CardNumber NS_LITERAL_STRING("cardNumber")
-#define ExpiryMonth NS_LITERAL_STRING("expiryMonth")
-#define ExpiryYear NS_LITERAL_STRING("expiryYear")
-#define CardSecurityCode NS_LITERAL_STRING("cardSecurityCode")
-
-#define Country NS_LITERAL_STRING("country")
-#define AddressLine NS_LITERAL_STRING("addressLine")
-#define Region NS_LITERAL_STRING("region")
-#define RegionCode NS_LITERAL_STRING("regionCode")
-#define City NS_LITERAL_STRING("city")
-#define DependentLocality NS_LITERAL_STRING("dependentLocality")
-#define PostalCode NS_LITERAL_STRING("postalCode")
-#define SortingCode NS_LITERAL_STRING("sortingCode")
-#define Organization NS_LITERAL_STRING("organization")
-#define Recipient NS_LITERAL_STRING("recipient")
-#define Phone NS_LITERAL_STRING("phone")
-
-#define PropertySpliter NS_LITERAL_STRING(";")
-#define KeyValueSpliter NS_LITERAL_STRING(":")
-#define AddressLineSpliter NS_LITERAL_STRING("%")
-
-#define EncodeBasicCardProperty(aPropertyName, aPropertyValue, aResult)        \
-  do {                                                                         \
-    if (!(aPropertyValue).IsEmpty()) {                                         \
-      (aResult) += (aPropertyName)                                             \
-                 + KeyValueSpliter                                             \
-                 + (aPropertyValue)                                            \
-                 + PropertySpliter;                                            \
-    }                                                                          \
-  } while(0)
-
-#define EncodeAddressProperty(aAddress, aPropertyName, aResult)                \
-  do {                                                                         \
-    nsAutoString propertyValue;                                                \
-    NS_ENSURE_SUCCESS((aAddress)->Get##aPropertyName(propertyValue),           \
-                                                     NS_ERROR_FAILURE);        \
-    EncodeBasicCardProperty((aPropertyName) ,propertyValue , (aResult));       \
-  } while(0)
-
-#define DecodeBasicCardProperty(aPropertyName, aPropertyValue,                 \
-                                aMatchPropertyName, aResponse)                 \
-  do {                                                                         \
-    if ((aPropertyName).Equals((aMatchPropertyName))) {                        \
-      (aResponse).m##aMatchPropertyName.Construct();                           \
-      (aResponse).m##aMatchPropertyName.Value() = (aPropertyValue);            \
-    }                                                                          \
-  } while(0)
-
-#define DecodeAddressProperty(aPropertyName, aPropertyValue,                   \
-                              aMatchPropertyName, aMatchPropertyValue)         \
-  do {                                                                         \
-    if ((aPropertyName).Equals((aMatchPropertyName))) {                        \
-      (aMatchPropertyValue) = (aPropertyValue);                                \
-    }                                                                          \
-  } while(0)
-
-#endif
-
 namespace {
-
 bool IsValidNetwork(const nsAString& aNetwork)
 {
-  return AMEX.Equals(aNetwork) ||
-         CARTEBANCAIRE.Equals(aNetwork) ||
-         DINERS.Equals(aNetwork) ||
-         DISCOVER.Equals(aNetwork) ||
-         JCB.Equals(aNetwork) ||
-         MASTERCARD.Equals(aNetwork) ||
-         MIR.Equals(aNetwork) ||
-         UNIONPAY.Equals(aNetwork) ||
-         VISA.Equals(aNetwork);
+  return aNetwork.Equals(NS_LITERAL_STRING("amex")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("cartebancaire")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("diners")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("discover")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("jcb")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("mastercard")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("mir")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("unionpay")) ||
+         aNetwork.Equals(NS_LITERAL_STRING("visa"));
 }
-
-bool IsBasicCardKey(const nsAString& aKey)
-{
-  return CardholderName.Equals(aKey) ||
-         CardNumber.Equals(aKey) ||
-         ExpiryMonth.Equals(aKey) ||
-         ExpiryYear.Equals(aKey) ||
-         CardSecurityCode.Equals(aKey);
-}
-
-bool IsAddressKey(const nsAString& aKey)
-{
-  return Country.Equals(aKey) ||
-         AddressLine.Equals(aKey) ||
-         Region.Equals(aKey) ||
-         RegionCode.Equals(aKey) ||
-         City.Equals(aKey) ||
-         DependentLocality.Equals(aKey) ||
-         PostalCode.Equals(aKey) ||
-         SortingCode.Equals(aKey) ||
-         Organization.Equals(aKey) ||
-         Recipient.Equals(aKey) ||
-         Phone.Equals(aKey);
-}
-
 } // end of namespace
 
 
 StaticRefPtr<BasicCardService> gBasicCardService;
 
 already_AddRefed<BasicCardService>
 BasicCardService::GetService()
 {
@@ -218,185 +120,22 @@ BasicCardService::IsValidExpiryYear(cons
           aExpiryYear.CharAt(index) > '9') {
         return false;
       }
     }
   }
   return true;
 }
 
-nsresult
-BasicCardService::EncodeBasicCardData(const nsAString& aCardholderName,
-                                      const nsAString& aCardNumber,
-                                      const nsAString& aExpiryMonth,
-                                      const nsAString& aExpiryYear,
-                                      const nsAString& aCardSecurityCode,
-                                      nsIPaymentAddress* aBillingAddress,
-                                      nsAString& aResult)
-{
-  // aBillingAddress can be nullptr
-  if (aCardNumber.IsEmpty()) {
-    return NS_ERROR_FAILURE;
-  }
-  EncodeBasicCardProperty(CardholderName, aCardholderName, aResult);
-  EncodeBasicCardProperty(CardNumber, aCardNumber, aResult);
-  EncodeBasicCardProperty(ExpiryMonth, aExpiryMonth, aResult);
-  EncodeBasicCardProperty(ExpiryYear, aExpiryYear, aResult);
-  EncodeBasicCardProperty(CardSecurityCode, aCardSecurityCode, aResult);
-  if (!aBillingAddress) {
-    return NS_OK;
-  }
-  EncodeAddressProperty(aBillingAddress, Country, aResult);
-  nsCOMPtr<nsIArray> addressLine;
-  NS_ENSURE_SUCCESS(aBillingAddress->GetAddressLine(getter_AddRefs(addressLine)),
-                                                    NS_ERROR_FAILURE);
-  uint32_t length;
-  nsAutoString addressLineString;
-  NS_ENSURE_SUCCESS(addressLine->GetLength(&length), NS_ERROR_FAILURE);
-  for (uint32_t index = 0; index < length; ++index) {
-    nsCOMPtr<nsISupportsString> address = do_QueryElementAt(addressLine, index);
-    MOZ_ASSERT(address);
-    nsAutoString addressString;
-    NS_ENSURE_SUCCESS(address->GetData(addressString), NS_ERROR_FAILURE);
-    addressLineString += addressString + AddressLineSpliter;
-  }
-  EncodeBasicCardProperty(AddressLine ,addressLineString , aResult);
-  EncodeAddressProperty(aBillingAddress, Region, aResult);
-  EncodeAddressProperty(aBillingAddress, RegionCode, aResult);
-  EncodeAddressProperty(aBillingAddress, City, aResult);
-  EncodeAddressProperty(aBillingAddress, DependentLocality, aResult);
-  EncodeAddressProperty(aBillingAddress, PostalCode, aResult);
-  EncodeAddressProperty(aBillingAddress, SortingCode, aResult);
-  EncodeAddressProperty(aBillingAddress, Organization, aResult);
-  EncodeAddressProperty(aBillingAddress, Recipient, aResult);
-  EncodeAddressProperty(aBillingAddress, Phone, aResult);
-  return NS_OK;
-}
-
-nsresult
-BasicCardService::DecodeBasicCardData(const nsAString& aData,
-                                      nsPIDOMWindowInner* aWindow,
-                                      BasicCardResponse& aResponse)
-{
-  // aWindow can be nullptr
-  bool isBillingAddressPassed = false;
-  nsTArray<nsString> addressLine;
-  nsAutoString country;
-  nsAutoString region;
-  nsAutoString regionCode;
-  nsAutoString city;
-  nsAutoString dependentLocality;
-  nsAutoString postalCode;
-  nsAutoString sortingCode;
-  nsAutoString organization;
-  nsAutoString recipient;
-  nsAutoString phone;
-
-  nsCharSeparatedTokenizer propertyTokenizer(aData, PropertySpliter.CharAt(0));
-  while (propertyTokenizer.hasMoreTokens()) {
-    nsDependentSubstring property = propertyTokenizer.nextToken();
-    nsCharSeparatedTokenizer keyValueTokenizer(property, KeyValueSpliter.CharAt(0));
-    MOZ_ASSERT(keyValueTokenizer.hasMoreTokens());
-    nsDependentSubstring key = keyValueTokenizer.nextToken();
-    nsDependentSubstring value = keyValueTokenizer.nextToken();
-    if (IsAddressKey(key) && !isBillingAddressPassed) {
-      isBillingAddressPassed = true;
-    }
-    if (!IsAddressKey(key) && !IsBasicCardKey(key)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (key.Equals(CardNumber)) {
-      aResponse.mCardNumber = (value);
-    }
-
-    DecodeBasicCardProperty(key, value, CardholderName, aResponse);
-    DecodeBasicCardProperty(key, value, ExpiryMonth, aResponse);
-    DecodeBasicCardProperty(key, value, ExpiryYear, aResponse);
-    DecodeBasicCardProperty(key, value, CardSecurityCode, aResponse);
-
-    DecodeAddressProperty(key, value, Country, country);
-    DecodeAddressProperty(key, value, Region, region);
-    DecodeAddressProperty(key, value, RegionCode, regionCode);
-    DecodeAddressProperty(key, value, City, city);
-    DecodeAddressProperty(key, value, DependentLocality, dependentLocality);
-    DecodeAddressProperty(key, value, PostalCode, postalCode);
-    DecodeAddressProperty(key, value, SortingCode, sortingCode);
-    DecodeAddressProperty(key, value, Organization, organization);
-    DecodeAddressProperty(key, value, Recipient, recipient);
-    DecodeAddressProperty(key, value, Phone, phone);
-
-    if ((key).Equals(AddressLine)) {
-      nsCharSeparatedTokenizer addressTokenizer(value, AddressLineSpliter.CharAt(0));
-      while (addressTokenizer.hasMoreTokens()) {
-        addressLine.AppendElement(addressTokenizer.nextToken());
-      }
-    }
-  }
-  if (isBillingAddressPassed) {
-    aResponse.mBillingAddress.Construct();
-    aResponse.mBillingAddress.Value() = new PaymentAddress(aWindow,
-                                                           country,
-                                                           addressLine,
-                                                           region,
-                                                           regionCode,
-                                                           city,
-                                                           dependentLocality,
-                                                           postalCode,
-                                                           sortingCode,
-                                                           organization,
-                                                           recipient,
-                                                           phone);
-  }
-  return NS_OK;
-}
-
 bool
 BasicCardService::IsValidBasicCardErrors(JSContext* aCx,
                                          JSObject* aData)
 {
   if (!aData) {
     return true;
   }
   JS::RootedValue data(aCx, JS::ObjectValue(*aData));
 
   BasicCardErrors bcError;
   return !bcError.Init(aCx, data);
 }
-
-#ifdef PaymentBasicCardMacros
-#undef PaymentBasicCardMacros
-#undef EncodeBasicCardProperty
-#undef EncodeAddressProperty
-#undef DecodeBasicCardProperty
-#undef DecodeAddressProperty
-#undef AMEX
-#undef CARTEBANCAIRE
-#undef DINERS
-#undef DISCOVER
-#undef JCB
-#undef MASTERCARD
-#undef MIR
-#undef UNIONPAY
-#undef VISA
-#undef CardholderName
-#undef CardNumber
-#undef ExpiryMonth
-#undef ExpiryYear
-#undef CardSecurityCode
-#undef Country
-#undef AddressLine
-#undef Region
-#undef RegionCode
-#undef City
-#undef DependentLocality
-#undef PostalCode
-#undef SortingCode
-#undef Organization
-#undef Recipient
-#undef Phone
-#undef PropertySpliter
-#undef KeyValueSpliter
-#undef AddressLineSpliter
-#endif
-
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/BasicCardPayment.h
+++ b/dom/payments/BasicCardPayment.h
@@ -22,32 +22,16 @@ public:
 
   static already_AddRefed<BasicCardService> GetService();
 
   bool IsBasicCardPayment(const nsAString& aSupportedMethods);
   bool IsValidBasicCardRequest(JSContext* aCx, JSObject* aData, nsAString& aErrorMsg);
   bool IsValidBasicCardErrors(JSContext* aCx, JSObject* aData);
   bool IsValidExpiryMonth(const nsAString& aExpiryMonth);
   bool IsValidExpiryYear(const nsAString& aExpiryYear);
-/*
-  To let BasicCardResponse using the same data type with non-BasicCard response
-  in IPC transferring, following two methods is used to Encode/Decode the raw
-  data of BasicCardResponse.
-*/
-  nsresult EncodeBasicCardData(const nsAString& aCardholderName,
-                               const nsAString& aCardNumber,
-                               const nsAString& aExpiryMonth,
-                               const nsAString& aExpiryYear,
-                               const nsAString& aCardSecurityCode,
-                               nsIPaymentAddress* aBillingAddress,
-                               nsAString& aResult);
-
-  nsresult DecodeBasicCardData(const nsAString& aData,
-                               nsPIDOMWindowInner* aWindow,
-                               BasicCardResponse& aResponse);
 private:
   BasicCardService() = default;
   ~BasicCardService() = default;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
--- a/dom/payments/PaymentActionResponse.cpp
+++ b/dom/payments/PaymentActionResponse.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PaymentActionResponse.h"
 #include "PaymentRequestUtils.h"
-#include "BasicCardPayment.h"
 
 namespace mozilla {
 namespace dom {
 
 /* PaymentResponseData */
 
 NS_IMPL_ISUPPORTS(PaymentResponseData, nsIPaymentResponseData)
 
@@ -73,19 +72,57 @@ NS_IMPL_ISUPPORTS_INHERITED(BasicCardRes
                             nsIBasicCardResponseData)
 
 BasicCardResponseData::BasicCardResponseData()
 {
   Init(nsIPaymentResponseData::BASICCARD_RESPONSE);
 }
 
 NS_IMETHODIMP
-BasicCardResponseData::GetData(nsAString& aData)
+BasicCardResponseData::GetCardholderName(nsAString& aCardholderName)
+{
+  aCardholderName = mCardholderName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetCardNumber(nsAString& aCardNumber)
+{
+  aCardNumber = mCardNumber;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetExpiryMonth(nsAString& aExpiryMonth)
 {
-  aData = mData;
+  aExpiryMonth = mExpiryMonth;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetExpiryYear(nsAString& aExpiryYear)
+{
+  aExpiryYear = mExpiryYear;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetCardSecurityCode(nsAString& aCardSecurityCode)
+{
+  aCardSecurityCode = mCardSecurityCode;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetBillingAddress(nsIPaymentAddress** aBillingAddress)
+{
+  NS_ENSURE_ARG_POINTER(aBillingAddress);
+  nsCOMPtr<nsIPaymentAddress> address;
+  address = mBillingAddress;
+  address.forget(aBillingAddress);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasicCardResponseData::InitData(const nsAString& aCardholderName,
                                 const nsAString& aCardNumber,
                                 const nsAString& aExpiryMonth,
                                 const nsAString& aExpiryYear,
@@ -102,26 +139,24 @@ BasicCardResponseData::InitData(const ns
 
   if (!service->IsValidExpiryMonth(aExpiryMonth)) {
     return NS_ERROR_FAILURE;
   }
 
   if (!service->IsValidExpiryYear(aExpiryYear)) {
     return NS_ERROR_FAILURE;
   }
-  nsresult rv = service->EncodeBasicCardData(aCardholderName,
-                                             aCardNumber,
-                                             aExpiryMonth,
-                                             aExpiryYear,
-                                             aCardSecurityCode,
-                                             aBillingAddress,
-                                             mData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+
+  mCardholderName = aCardholderName;
+  mCardNumber = aCardNumber;
+  mExpiryMonth = aExpiryMonth;
+  mExpiryYear = aExpiryYear;
+  mCardSecurityCode = aCardSecurityCode;
+  mBillingAddress = aBillingAddress;
+
   return NS_OK;
 }
 
 /* PaymentActionResponse */
 
 NS_IMPL_ISUPPORTS(PaymentActionResponse,
                   nsIPaymentActionResponse)
 
@@ -197,19 +232,21 @@ PaymentShowActionResponse::GetAcceptStat
 NS_IMETHODIMP
 PaymentShowActionResponse::GetMethodName(nsAString& aMethodName)
 {
   aMethodName = mMethodName;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PaymentShowActionResponse::GetData(nsAString& aData)
+PaymentShowActionResponse::GetData(nsIPaymentResponseData** aData)
 {
-  aData = mData;
+  NS_ENSURE_ARG_POINTER(aData);
+  nsCOMPtr<nsIPaymentResponseData> data = mData;
+  data.forget(aData);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentShowActionResponse::GetPayerName(nsAString& aPayerName)
 {
   aPayerName = mPayerName;
   return NS_OK;
@@ -253,38 +290,30 @@ PaymentShowActionResponse::Init(const ns
   if (aAcceptStatus == nsIPaymentActionResponse::PAYMENT_ACCEPTED) {
     uint32_t responseType;
     NS_ENSURE_SUCCESS(aData->GetType(&responseType), NS_ERROR_FAILURE);
     switch (responseType) {
       case nsIPaymentResponseData::GENERAL_RESPONSE: {
         if (isBasicCardPayment) {
           return NS_ERROR_FAILURE;
         }
-        nsCOMPtr<nsIGeneralResponseData> data = do_QueryInterface(aData);
-        MOZ_ASSERT(data);
-        NS_ENSURE_SUCCESS(data->GetData(mData), NS_ERROR_FAILURE);
         break;
       }
       case nsIPaymentResponseData::BASICCARD_RESPONSE: {
         if (!isBasicCardPayment) {
           return NS_ERROR_FAILURE;
         }
-        nsCOMPtr<nsIBasicCardResponseData> data = do_QueryInterface(aData);
-        MOZ_ASSERT(data);
-        NS_ENSURE_SUCCESS(data->GetData(mData), NS_ERROR_FAILURE);
         break;
       }
       default: {
         return NS_ERROR_FAILURE;
       }
     }
-    if (mData.IsEmpty()) {
-      return NS_ERROR_FAILURE;
-    }
   }
+  mData = aData;
   mPayerName = aPayerName;
   mPayerEmail = aPayerEmail;
   mPayerPhone = aPayerPhone;
   return NS_OK;
 }
 
 /* PaymentAbortActionResponse */
 
--- a/dom/payments/PaymentActionResponse.h
+++ b/dom/payments/PaymentActionResponse.h
@@ -52,17 +52,21 @@ public:
   NS_FORWARD_NSIPAYMENTRESPONSEDATA(PaymentResponseData::)
   NS_DECL_NSIBASICCARDRESPONSEDATA
 
   BasicCardResponseData();
 
 private:
   ~BasicCardResponseData() = default;
 
-  nsString mData;
+  nsString mCardholderName;
+  nsString mCardNumber;
+  nsString mExpiryMonth;
+  nsString mExpiryYear;
+  nsString mCardSecurityCode;
   nsCOMPtr<nsIPaymentAddress> mBillingAddress;
 };
 
 class PaymentActionResponse : public nsIPaymentActionResponse
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTACTIONRESPONSE
@@ -102,17 +106,17 @@ public:
 
   PaymentShowActionResponse();
 
 private:
   ~PaymentShowActionResponse() = default;
 
   uint32_t mAcceptStatus;
   nsString mMethodName;
-  nsString mData;
+  nsCOMPtr<nsIPaymentResponseData> mData;
   nsString mPayerName;
   nsString mPayerEmail;
   nsString mPayerPhone;
 };
 
 class PaymentAbortActionResponse final : public nsIPaymentAbortActionResponse
                                        , public PaymentActionResponse
 {
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -792,17 +792,17 @@ PaymentRequest::RejectShowPayment(nsresu
     mAcceptPromise->MaybeReject(aRejectReason);
   }
   mState = eClosed;
   mAcceptPromise = nullptr;
 }
 
 void
 PaymentRequest::RespondShowPayment(const nsAString& aMethodName,
-                                   const nsAString& aDetails,
+                                   const ResponseData& aDetails,
                                    const nsAString& aPayerName,
                                    const nsAString& aPayerEmail,
                                    const nsAString& aPayerPhone,
                                    nsresult aRv)
 {
   MOZ_ASSERT(mAcceptPromise || mResponse);
   MOZ_ASSERT(mState == eInteractive);
 
@@ -883,17 +883,17 @@ PaymentRequest::RespondAbortPayment(bool
   //   => Reject |mAcceptPromise| and reset |mUpdateError| to complete
   //      the action, regardless of |aSuccess|.
   //
   // - Otherwise, we are handling |Abort| method call from merchant.
   //   => Resolve/Reject |mAbortPromise| based on |aSuccess|.
   if (NS_FAILED(mUpdateError)) {
     // Respond show with mUpdateError, set mUpdating to false.
     mUpdating = false;
-    RespondShowPayment(EmptyString(), EmptyString(), EmptyString(),
+    RespondShowPayment(EmptyString(), ResponseData(), EmptyString(),
                        EmptyString(), EmptyString(), mUpdateError);
     mUpdateError = NS_OK;
     return;
   }
 
   MOZ_ASSERT(mAbortPromise);
   MOZ_ASSERT(mState == eInteractive);
 
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -18,16 +18,17 @@
 
 namespace mozilla {
 namespace dom {
 
 class EventHandlerNonNull;
 class PaymentAddress;
 class PaymentRequestChild;
 class PaymentResponse;
+class ResponseData;
 
 class PaymentRequest final
   : public DOMEventTargetHelper
   , public PromiseNativeHandler
   , public nsIDocumentActivity
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -91,17 +92,17 @@ public:
 
   already_AddRefed<Promise> CanMakePayment(ErrorResult& aRv);
   void RespondCanMakePayment(bool aResult);
 
   already_AddRefed<Promise> Show(
     const Optional<OwningNonNull<Promise>>& detailsPromise,
     ErrorResult& aRv);
   void RespondShowPayment(const nsAString& aMethodName,
-                          const nsAString& aDetails,
+                          const ResponseData& aDetails,
                           const nsAString& aPayerName,
                           const nsAString& aPayerEmail,
                           const nsAString& aPayerPhone,
                           nsresult aRv);
   void RejectShowPayment(nsresult aRejectReason);
   void RespondComplete();
 
   already_AddRefed<Promise> Abort(ErrorResult& aRv);
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -244,16 +244,84 @@ ConvertOptions(const PaymentOptions& aOp
       PaymentShippingTypeValues::strings[shippingTypeIndex].value);
   }
   aIPCOption = IPCPaymentOptions(aOptions.mRequestPayerName,
                                  aOptions.mRequestPayerEmail,
                                  aOptions.mRequestPayerPhone,
                                  aOptions.mRequestShipping,
                                  shippingType);
 }
+
+nsresult
+ConvertResponseData(nsPIDOMWindowInner* aWindow,
+                    const IPCPaymentResponseData& aIPCResponseData,
+                    ResponseData& aResponseData)
+{
+  NS_ENSURE_ARG_POINTER(aWindow);
+  switch(aIPCResponseData.type()) {
+    case IPCPaymentResponseData::TIPCGeneralResponse: {
+      const IPCGeneralResponse& rawData = aIPCResponseData;
+      aResponseData.Init(rawData.data());
+      break;
+    }
+    case IPCPaymentResponseData::TIPCBasicCardResponse: {
+      const IPCBasicCardResponse& rawData = aIPCResponseData;
+      BasicCardResponse basicCardResponse;
+      if (!rawData.cardholderName().IsEmpty()) {
+        basicCardResponse.mCardholderName.Construct();
+        basicCardResponse.mCardholderName.Value() = rawData.cardholderName();
+      }
+      basicCardResponse.mCardNumber = rawData.cardNumber();
+      if (!rawData.expiryMonth().IsEmpty()) {
+        basicCardResponse.mExpiryMonth.Construct();
+        basicCardResponse.mExpiryMonth.Value() = rawData.expiryMonth();
+      }
+      if (!rawData.expiryYear().IsEmpty()) {
+        basicCardResponse.mExpiryYear.Construct();
+        basicCardResponse.mExpiryYear.Value() = rawData.expiryYear();
+      }
+      if (!rawData.cardSecurityCode().IsEmpty()) {
+        basicCardResponse.mCardSecurityCode.Construct();
+        basicCardResponse.mCardSecurityCode.Value() = rawData.cardSecurityCode();
+      }
+      if (!rawData.billingAddress().country().IsEmpty() ||
+          !rawData.billingAddress().addressLine().IsEmpty() ||
+          !rawData.billingAddress().region().IsEmpty() ||
+          !rawData.billingAddress().regionCode().IsEmpty() ||
+          !rawData.billingAddress().city().IsEmpty() ||
+          !rawData.billingAddress().dependentLocality().IsEmpty() ||
+          !rawData.billingAddress().postalCode().IsEmpty() ||
+          !rawData.billingAddress().sortingCode().IsEmpty() ||
+          !rawData.billingAddress().organization().IsEmpty() ||
+          !rawData.billingAddress().recipient().IsEmpty() ||
+          !rawData.billingAddress().phone().IsEmpty()) {
+        basicCardResponse.mBillingAddress.Construct();
+        basicCardResponse.mBillingAddress.Value() =
+          new PaymentAddress(aWindow,
+                             rawData.billingAddress().country(),
+                             rawData.billingAddress().addressLine(),
+                             rawData.billingAddress().region(),
+                             rawData.billingAddress().regionCode(),
+                             rawData.billingAddress().city(),
+                             rawData.billingAddress().dependentLocality(),
+                             rawData.billingAddress().postalCode(),
+                             rawData.billingAddress().sortingCode(),
+                             rawData.billingAddress().organization(),
+                             rawData.billingAddress().recipient(),
+                             rawData.billingAddress().phone());
+      }
+      aResponseData.Init(basicCardResponse);
+      break;
+    }
+    default: {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  return NS_OK;
+}
 } // end of namespace
 
 /* PaymentRequestManager */
 
 StaticRefPtr<PaymentRequestManager> gPaymentManager;
 
 PaymentRequestChild*
 PaymentRequestManager::GetPaymentChild(PaymentRequest* aRequest)
@@ -584,36 +652,41 @@ PaymentRequestManager::RespondPayment(Pa
       const IPCPaymentCanMakeActionResponse& response = aResponse;
       aRequest->RespondCanMakePayment(response.result());
       NotifyRequestDone(aRequest);
       break;
     }
     case IPCPaymentActionResponse::TIPCPaymentShowActionResponse: {
       const IPCPaymentShowActionResponse& response = aResponse;
       nsresult rejectedReason = NS_ERROR_DOM_ABORT_ERR;
+      ResponseData responseData;
       switch (response.status()) {
         case nsIPaymentActionResponse::PAYMENT_ACCEPTED: {
           rejectedReason = NS_OK;
+          NS_ENSURE_SUCCESS(ConvertResponseData(aRequest->GetOwner(),
+                                                response.data(),
+                                                responseData),
+                            NS_ERROR_FAILURE);
           break;
         }
         case nsIPaymentActionResponse::PAYMENT_REJECTED: {
           rejectedReason = NS_ERROR_DOM_ABORT_ERR;
           break;
         }
         case nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED: {
           rejectedReason = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
           break;
         }
         default: {
           rejectedReason = NS_ERROR_UNEXPECTED;
           break;
         }
       }
       aRequest->RespondShowPayment(response.methodName(),
-                                   response.data(),
+                                   responseData,
                                    response.payerName(),
                                    response.payerEmail(),
                                    response.payerPhone(),
                                    rejectedReason);
       if (NS_FAILED(rejectedReason)) {
         NotifyRequestDone(aRequest);
       }
       break;
--- a/dom/payments/PaymentResponse.cpp
+++ b/dom/payments/PaymentResponse.cpp
@@ -11,16 +11,60 @@
 #include "BasicCardPayment.h"
 #include "PaymentAddress.h"
 #include "PaymentRequestUtils.h"
 #include "mozilla/EventStateManager.h"
 
 namespace mozilla {
 namespace dom {
 
+ResponseData::ResponseData()
+  : mType(ResponseData::UNKNOWN)
+{
+}
+
+void
+ResponseData::GetData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const
+{
+  switch (mType) {
+    case ResponseData::GENERAL_RESPONSE : {
+      DeserializeToJSObject(mGeneralResponse, aCx, aRetVal);
+      break;
+    }
+    case ResponseData::BASICCARD_RESPONSE : {
+      MOZ_ASSERT(aCx);
+      JS::RootedValue value(aCx);
+      if (NS_WARN_IF(!mBasicCardResponse.ToObjectInternal(aCx, &value))) {
+        return;
+      }
+      aRetVal.set(&value.toObject());
+      break;
+    }
+    default : {
+      break;
+    }
+  }
+}
+
+void
+ResponseData::Init(const nsString& aGeneralResponse)
+{
+  mType = ResponseData::GENERAL_RESPONSE;
+  mGeneralResponse = aGeneralResponse;
+  mBasicCardResponse = BasicCardResponse();
+}
+
+void
+ResponseData::Init(const BasicCardResponse& aBasicCardResponse)
+{
+  mType = ResponseData::BASICCARD_RESPONSE;
+  mBasicCardResponse = aBasicCardResponse;
+  mGeneralResponse = EmptyString();
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentResponse)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PaymentResponse,
                                                DOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // DOMEventTargetHelper does it for us.
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
@@ -46,17 +90,17 @@ NS_IMPL_ADDREF_INHERITED(PaymentResponse
 NS_IMPL_RELEASE_INHERITED(PaymentResponse, DOMEventTargetHelper)
 
 PaymentResponse::PaymentResponse(nsPIDOMWindowInner* aWindow,
                                  PaymentRequest* aRequest,
                                  const nsAString& aRequestId,
                                  const nsAString& aMethodName,
                                  const nsAString& aShippingOption,
                                  PaymentAddress* aShippingAddress,
-                                 const nsAString& aDetails,
+                                 const ResponseData& aDetails,
                                  const nsAString& aPayerName,
                                  const nsAString& aPayerEmail,
                                  const nsAString& aPayerPhone)
   : DOMEventTargetHelper(aWindow)
   , mCompleteCalled(false)
   , mRequest(aRequest)
   , mRequestId(aRequestId)
   , mMethodName(aMethodName)
@@ -96,34 +140,17 @@ PaymentResponse::GetMethodName(nsString&
 {
   aRetVal = mMethodName;
 }
 
 void
 PaymentResponse::GetDetails(JSContext* aCx,
                             JS::MutableHandle<JSObject*> aRetVal) const
 {
-  RefPtr<BasicCardService> service = BasicCardService::GetService();
-  MOZ_ASSERT(service);
-  if (!service->IsBasicCardPayment(mMethodName)) {
-    DeserializeToJSObject(mDetails, aCx, aRetVal);
-  } else {
-    BasicCardResponse response;
-    nsresult rv = service->DecodeBasicCardData(mDetails, GetOwner(), response);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    MOZ_ASSERT(aCx);
-    JS::RootedValue value(aCx);
-    if (NS_WARN_IF(!response.ToObjectInternal(aCx, &value))) {
-      return;
-    }
-    aRetVal.set(&value.toObject());
-  }
+  mDetails.GetData(aCx, aRetVal);
 }
 
 void
 PaymentResponse::GetShippingOption(nsString& aRetVal) const
 {
   aRetVal = mShippingOption;
 }
 
@@ -270,17 +297,17 @@ PaymentResponse::Retry(JSContext* aCx,
   mRetryPromise = promise;
   return promise.forget();
 }
 
 void
 PaymentResponse::RespondRetry(const nsAString& aMethodName,
                               const nsAString& aShippingOption,
                               PaymentAddress* aShippingAddress,
-                              const nsAString& aDetails,
+                              const ResponseData& aDetails,
                               const nsAString& aPayerName,
                               const nsAString& aPayerEmail,
                               const nsAString& aPayerPhone)
 {
   mMethodName = aMethodName;
   mShippingOption = aShippingOption;
   mShippingAddress = aShippingAddress;
   mDetails = aDetails;
--- a/dom/payments/PaymentResponse.h
+++ b/dom/payments/PaymentResponse.h
@@ -3,27 +3,52 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PaymentResponse_h
 #define mozilla_dom_PaymentResponse_h
 
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/BasicCardPaymentBinding.h"
 #include "mozilla/dom/PaymentResponseBinding.h" // PaymentComplete
 #include "nsPIDOMWindow.h"
 #include "nsITimer.h"
 
 namespace mozilla {
 namespace dom {
 
 class PaymentAddress;
 class PaymentRequest;
 class Promise;
 
+class ResponseData final
+{
+public:
+  enum TYPE {
+    UNKNOWN = 0,
+    GENERAL_RESPONSE,
+    BASICCARD_RESPONSE,
+  };
+
+  ResponseData();
+
+  ~ResponseData() = default;
+
+  const TYPE& GetType() const { return mType; }
+  void GetData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  void Init(const nsString& aGeneralResponse);
+  void Init(const BasicCardResponse& aBasicCardResponse);
+
+private:
+  TYPE mType;
+  nsString mGeneralResponse;
+  BasicCardResponse mBasicCardResponse;
+};
+
 class PaymentResponse final
   : public DOMEventTargetHelper
   , public nsITimerCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PaymentResponse,
@@ -32,17 +57,17 @@ public:
   NS_IMETHOD Notify(nsITimer* aTimer) override;
 
   PaymentResponse(nsPIDOMWindowInner* aWindow,
                   PaymentRequest* aRequest,
                   const nsAString& aRequestId,
                   const nsAString& aMethodName,
                   const nsAString& aShippingOption,
                   PaymentAddress* aShippingAddress,
-                  const nsAString& aDetails,
+                  const ResponseData& aDetails,
                   const nsAString& aPayerName,
                   const nsAString& aPayerEmail,
                   const nsAString& aPayerPhone);
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   void GetRequestId(nsString& aRetVal) const;
@@ -75,17 +100,17 @@ public:
 
   already_AddRefed<Promise> Retry(JSContext* aCx,
                                   const PaymentValidationErrors& errorField,
                                   ErrorResult& aRv);
 
   void RespondRetry(const nsAString& aMethodName,
                     const nsAString& aShippingOption,
                     PaymentAddress* aShippingAddress,
-                    const nsAString& aDetails,
+                    const ResponseData& aDetails,
                     const nsAString& aPayerName,
                     const nsAString& aPayerEmail,
                     const nsAString& aPayerPhone);
   void RejectRetry(nsresult aRejectReason);
 
 protected:
   ~PaymentResponse();
 
@@ -98,17 +123,17 @@ protected:
 
   nsresult DispatchUpdateEvent(const nsAString& aType);
 
 private:
   bool mCompleteCalled;
   PaymentRequest* mRequest;
   nsString mRequestId;
   nsString mMethodName;
-  nsString mDetails;
+  ResponseData mDetails;
   nsString mShippingOption;
   nsString mPayerName;
   nsString mPayerEmail;
   nsString mPayerPhone;
   RefPtr<PaymentAddress> mShippingAddress;
   // Promise for "PaymentResponse::Complete"
   RefPtr<Promise> mPromise;
   // Timer for timing out if the page doesn't call
--- a/dom/payments/ipc/PPaymentRequest.ipdl
+++ b/dom/payments/ipc/PPaymentRequest.ipdl
@@ -133,22 +133,58 @@ union IPCPaymentActionRequest
 };
 
 struct IPCPaymentCanMakeActionResponse
 {
   nsString requestId;
   bool result;
 };
 
+struct IPCPaymentAddress
+{
+  nsString country;
+  nsString[] addressLine;
+  nsString region;
+  nsString regionCode;
+  nsString city;
+  nsString dependentLocality;
+  nsString postalCode;
+  nsString sortingCode;
+  nsString organization;
+  nsString recipient;
+  nsString phone;
+};
+
+struct IPCGeneralResponse
+{
+  nsString data;
+};
+
+struct IPCBasicCardResponse
+{
+  nsString cardholderName;
+  nsString cardNumber;
+  nsString expiryMonth;
+  nsString expiryYear;
+  nsString cardSecurityCode;
+  IPCPaymentAddress billingAddress;
+};
+
+union IPCPaymentResponseData
+{
+  IPCGeneralResponse;
+  IPCBasicCardResponse;
+};
+
 struct IPCPaymentShowActionResponse
 {
   nsString requestId;
   uint32_t status;
   nsString methodName;
-  nsString data;
+  IPCPaymentResponseData data;
   nsString payerName;
   nsString payerEmail;
   nsString payerPhone;
 };
 
 struct IPCPaymentAbortActionResponse
 {
   nsString requestId;
@@ -164,31 +200,16 @@ struct IPCPaymentCompleteActionResponse
 union IPCPaymentActionResponse
 {
   IPCPaymentCanMakeActionResponse;
   IPCPaymentShowActionResponse;
   IPCPaymentAbortActionResponse;
   IPCPaymentCompleteActionResponse;
 };
 
-struct IPCPaymentAddress
-{
-  nsString country;
-  nsString[] addressLine;
-  nsString region;
-  nsString regionCode;
-  nsString city;
-  nsString dependentLocality;
-  nsString postalCode;
-  nsString sortingCode;
-  nsString organization;
-  nsString recipient;
-  nsString phone;
-};
-
 sync protocol PPaymentRequest
 {
   manager PBrowser;
 
 parent:
   async __delete__();
 
   async RequestPayment(IPCPaymentActionRequest aAction);
--- a/dom/payments/ipc/PaymentRequestParent.cpp
+++ b/dom/payments/ipc/PaymentRequestParent.cpp
@@ -133,28 +133,37 @@ PaymentRequestParent::RespondPayment(nsI
     case nsIPaymentActionResponse::SHOW_ACTION: {
       nsCOMPtr<nsIPaymentShowActionResponse> response =
         do_QueryInterface(aResponse);
       MOZ_ASSERT(response);
       uint32_t acceptStatus;
       NS_ENSURE_SUCCESS(response->GetAcceptStatus(&acceptStatus), NS_ERROR_FAILURE);
       nsAutoString methodName;
       NS_ENSURE_SUCCESS(response->GetMethodName(methodName), NS_ERROR_FAILURE);
-      nsAutoString data;
-      NS_ENSURE_SUCCESS(response->GetData(data), NS_ERROR_FAILURE);
+      IPCPaymentResponseData ipcData;
+      if (acceptStatus == nsIPaymentActionResponse::PAYMENT_ACCEPTED) {
+        nsCOMPtr<nsIPaymentResponseData> data;
+        NS_ENSURE_SUCCESS(response->GetData(getter_AddRefs(data)),
+                          NS_ERROR_FAILURE);
+        MOZ_ASSERT(data);
+        NS_ENSURE_SUCCESS(SerializeResponseData(ipcData, data), NS_ERROR_FAILURE);
+      } else {
+        ipcData = IPCGeneralResponse();
+      }
+
       nsAutoString payerName;
       NS_ENSURE_SUCCESS(response->GetPayerName(payerName), NS_ERROR_FAILURE);
       nsAutoString payerEmail;
       NS_ENSURE_SUCCESS(response->GetPayerEmail(payerEmail), NS_ERROR_FAILURE);
       nsAutoString payerPhone;
       NS_ENSURE_SUCCESS(response->GetPayerPhone(payerPhone), NS_ERROR_FAILURE);
       IPCPaymentShowActionResponse actionResponse(requestId,
                                                   acceptStatus,
                                                   methodName,
-                                                  data,
+                                                  ipcData,
                                                   payerName,
                                                   payerEmail,
                                                   payerPhone);
       if (!SendRespondPayment(actionResponse)) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
@@ -204,77 +213,21 @@ PaymentRequestParent::ChangeShippingAddr
     {
       self->ChangeShippingAddress(requestId, address);
     });
     return NS_DispatchToMainThread(r);
   }
   if (!mActorAlive) {
     return NS_ERROR_FAILURE;
   }
-  nsAutoString country;
-  nsresult rv = aAddress->GetCountry(country);
-  NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIArray> iaddressLine;
-  rv = aAddress->GetAddressLine(getter_AddRefs(iaddressLine));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString region;
-  rv = aAddress->GetRegion(region);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString regionCode;
-  rv = aAddress->GetRegionCode(regionCode);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString city;
-  rv = aAddress->GetCity(city);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString dependentLocality;
-  rv = aAddress->GetDependentLocality(dependentLocality);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString postalCode;
-  rv = aAddress->GetPostalCode(postalCode);
+  IPCPaymentAddress ipcAddress;
+  nsresult rv = SerializeAddress(ipcAddress, aAddress);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsAutoString sortingCode;
-  rv = aAddress->GetSortingCode(sortingCode);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString organization;
-  rv = aAddress->GetOrganization(organization);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString recipient;
-  rv = aAddress->GetRecipient(recipient);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString phone;
-  rv = aAddress->GetPhone(phone);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsTArray<nsString> addressLine;
-  uint32_t length;
-  rv = iaddressLine->GetLength(&length);
-  NS_ENSURE_SUCCESS(rv, rv);
-  for (uint32_t index = 0; index < length; ++index) {
-    nsCOMPtr<nsISupportsString> iaddress = do_QueryElementAt(iaddressLine, index);
-    MOZ_ASSERT(iaddress);
-    nsAutoString address;
-    rv = iaddress->GetData(address);
-    NS_ENSURE_SUCCESS(rv, rv);
-    addressLine.AppendElement(address);
-  }
-
-  IPCPaymentAddress ipcAddress(country, addressLine, region, regionCode, city,
-                               dependentLocality, postalCode, sortingCode,
-                               organization, recipient, phone);
-
   nsAutoString requestId(aRequestId);
   if (!SendChangeShippingAddress(requestId, ipcAddress)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
@@ -355,10 +308,127 @@ PaymentRequestParent::ActorDestroy(Actor
       return;
     }
     payments::PaymentRequest* rowRequest =
       static_cast<payments::PaymentRequest*>(request.get());
     MOZ_ASSERT(rowRequest);
     rowRequest->SetIPC(nullptr);
   }
 }
+
+nsresult
+PaymentRequestParent::SerializeAddress(IPCPaymentAddress& aIPCAddress,
+                                       nsIPaymentAddress* aAddress)
+{
+  // address can be nullptr
+  if (!aAddress) {
+    return NS_OK;
+  }
+  nsAutoString country;
+  nsresult rv = aAddress->GetCountry(country);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIArray> iaddressLine;
+  rv = aAddress->GetAddressLine(getter_AddRefs(iaddressLine));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString region;
+  rv = aAddress->GetRegion(region);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString regionCode;
+  rv = aAddress->GetRegionCode(regionCode);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString city;
+  rv = aAddress->GetCity(city);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString dependentLocality;
+  rv = aAddress->GetDependentLocality(dependentLocality);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString postalCode;
+  rv = aAddress->GetPostalCode(postalCode);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString sortingCode;
+  rv = aAddress->GetSortingCode(sortingCode);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString organization;
+  rv = aAddress->GetOrganization(organization);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString recipient;
+  rv = aAddress->GetRecipient(recipient);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString phone;
+  rv = aAddress->GetPhone(phone);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsTArray<nsString> addressLine;
+  uint32_t length;
+  rv = iaddressLine->GetLength(&length);
+  NS_ENSURE_SUCCESS(rv, rv);
+  for (uint32_t index = 0; index < length; ++index) {
+    nsCOMPtr<nsISupportsString> iaddress = do_QueryElementAt(iaddressLine, index);
+    MOZ_ASSERT(iaddress);
+    nsAutoString address;
+    rv = iaddress->GetData(address);
+    NS_ENSURE_SUCCESS(rv, rv);
+    addressLine.AppendElement(address);
+  }
+
+  aIPCAddress = IPCPaymentAddress(country, addressLine, region, regionCode, city,
+                                  dependentLocality, postalCode, sortingCode,
+                                  organization, recipient, phone);
+  return NS_OK;
+}
+
+nsresult
+PaymentRequestParent::SerializeResponseData(IPCPaymentResponseData& aIPCData,
+                                            nsIPaymentResponseData* aData)
+{
+  NS_ENSURE_ARG_POINTER(aData);
+  uint32_t dataType;
+  NS_ENSURE_SUCCESS(aData->GetType(&dataType), NS_ERROR_FAILURE);
+  switch(dataType) {
+    case nsIPaymentResponseData::GENERAL_RESPONSE: {
+      nsCOMPtr<nsIGeneralResponseData> response = do_QueryInterface(aData);
+      MOZ_ASSERT(response);
+      IPCGeneralResponse data;
+      NS_ENSURE_SUCCESS(response->GetData(data.data()), NS_ERROR_FAILURE);
+      aIPCData = data;
+      break;
+    }
+    case nsIPaymentResponseData::BASICCARD_RESPONSE: {
+      nsCOMPtr<nsIBasicCardResponseData> response = do_QueryInterface(aData);
+      MOZ_ASSERT(response);
+      IPCBasicCardResponse data;
+      NS_ENSURE_SUCCESS(response->GetCardholderName(data.cardholderName()),
+                        NS_ERROR_FAILURE);
+      NS_ENSURE_SUCCESS(response->GetCardNumber(data.cardNumber()),
+                        NS_ERROR_FAILURE);
+      NS_ENSURE_SUCCESS(response->GetExpiryMonth(data.expiryMonth()),
+                        NS_ERROR_FAILURE);
+      NS_ENSURE_SUCCESS(response->GetExpiryYear(data.expiryYear()),
+                        NS_ERROR_FAILURE);
+      NS_ENSURE_SUCCESS(response->GetCardSecurityCode(data.cardSecurityCode()),
+                        NS_ERROR_FAILURE);
+      nsCOMPtr<nsIPaymentAddress> address;
+      NS_ENSURE_SUCCESS(response->GetBillingAddress(getter_AddRefs(address)),
+                        NS_ERROR_FAILURE);
+      IPCPaymentAddress ipcAddress;
+      NS_ENSURE_SUCCESS(SerializeAddress(ipcAddress, address), NS_ERROR_FAILURE);
+      data.billingAddress() = ipcAddress;
+      aIPCData = data;
+      break;
+    }
+    default: {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  return NS_OK;
+}
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/ipc/PaymentRequestParent.h
+++ b/dom/payments/ipc/PaymentRequestParent.h
@@ -36,16 +36,21 @@ protected:
   RecvRequestPayment(const IPCPaymentActionRequest& aRequest) override;
 
   mozilla::ipc::IPCResult Recv__delete__() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 private:
   ~PaymentRequestParent() = default;
 
+  nsresult SerializeAddress(IPCPaymentAddress& ipcAddress,
+                            nsIPaymentAddress* aAddress);
+  nsresult SerializeResponseData(IPCPaymentResponseData& ipcData,
+                                 nsIPaymentResponseData* aData);
+
   bool mActorAlive;
   uint64_t mTabId;
   nsString mRequestId;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
--- a/dom/payments/test/BasiccardChromeScript.js
+++ b/dom/payments/test/BasiccardChromeScript.js
@@ -24,16 +24,34 @@ billingAddress.init("USA",              
                      "San Bruno",        // city
                      "",                 // dependent locality
                      "94066",            // postal code
                      "123456",           // sorting code
                      "",                 // organization
                      "Bill A. Pacheco",  // recipient
                      "+14344413879"); // phone
 
+const specialAddress = Cc["@mozilla.org/dom/payments/payment-address;1"].
+                           createInstance(Ci.nsIPaymentAddress);
+const specialAddressLine = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+const specialData = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+specialData.data = ":$%@&*";
+specialAddressLine.appendElement(specialData);
+specialAddress.init("USA",               // country
+                     specialAddressLine, // address line
+                     "CA",               // region
+                     "CA",               // region code
+                     "San Bruno",        // city
+                     "",                 // dependent locality
+                     "94066",            // postal code
+                     "123456",           // sorting code
+                     "",                 // organization
+                     "Bill A. Pacheco",  // recipient
+                     "+14344413879"); // phone
+
 const basiccardResponseData = Cc["@mozilla.org/dom/payments/basiccard-response-data;1"].
                                  createInstance(Ci.nsIBasicCardResponseData);
 
 const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
                         createInstance(Ci.nsIPaymentShowActionResponse);
 
 function abortPaymentResponse(requestId) {
   let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"].
@@ -104,24 +122,58 @@ const simpleResponseUI = {
   completePayment: completePaymentResponse,
   updatePayment: function(requestId) {
   },
   closePayment: function(requestId) {
   },
   QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
+const specialAddressUI = {
+  showPayment: function(requestId) {
+    try {
+      basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
+                                     "4916855166538720", // cardNumber
+                                     "01",               // expiryMonth
+                                     "2024",             // expiryYear
+                                     "180",              // cardSecurityCode
+                                     specialAddress);    // billingAddress
+    } catch (e) {
+      emitTestFail("Fail to initialize basic card response data.");
+    }
+    showResponse.init(requestId,
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "basic-card",         // payment method
+                      basiccardResponseData,// payment method data
+                      "Bill A. Pacheco",    // payer name
+                      "",                   // payer email
+                      "");                  // payer phone
+    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+  },
+  abortPayment: abortPaymentResponse,
+  completePayment: completePaymentResponse,
+  updatePayment: function(requestId) {
+  },
+  closePayment: function (requestId) {
+  },
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
+};
+
 addMessageListener("set-detailed-ui-service", function() {
   paymentSrv.setTestingUIService(detailedResponseUI.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("set-simple-ui-service", function() {
   paymentSrv.setTestingUIService(simpleResponseUI.QueryInterface(Ci.nsIPaymentUIService));
 });
 
+addMessageListener("set-special-address-ui-service", function() {
+  paymentSrv.setTestingUIService(specialAddressUI.QueryInterface(Ci.nsIPaymentUIService));
+});
+
 addMessageListener("error-response-test", function() {
   // test empty cardNumber
   try {
     basiccardResponseData.initData("", "", "", "", "", null);
     emitTestFail("BasicCardResponse should not be initialized with empty cardNumber.");
   } catch (e) {
     if (e.name != "NS_ERROR_FAILURE") {
       emitTestFail("Empty cardNumber expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
--- a/dom/payments/test/test_basiccard.html
+++ b/dom/payments/test/test_basiccard.html
@@ -236,16 +236,34 @@ https://bugzilla.mozilla.org/show_bug.cg
         });
       }).catch( e => {
         ok(false, "Unexpected error: " + e.name);
         resolve();
       }).finally(handler.destruct);
     });
   }
 
+  function testSpecialAddressResponse() {
+    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+    gScript.sendAsyncMessage("set-special-address-ui-service");
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.show().then(response => {
+        ok(response.details, "BasiccardResponse should exist.");
+        ok(response.details.billingAddress,
+           "BasiccardResponse.billingAddress should exist.");
+        is(response.details.billingAddress.addressLine[0], ":$%@&*",
+           "AddressLine should be ':$%@&*'");
+        response.complete("success").then(()=>{
+          resolve();
+        });
+      }).finally(handler.destruct);
+    });
+  }
+
   function testBasicCardErrorResponse() {
     return new Promise((resolve, reject) => {
       gScript.addMessageListener("error-response-complete",
                                  function errorResponseCompleteHandler() {
         gScript.removeMessageListener("error-response-complete",
                                       errorResponseCompleteHandler);
         resolve();
       });
@@ -267,16 +285,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     testBasicCardRequestWithErrorTypes()
     .then(testBasicCardRequestWithErrorNetworks)
     .then(testBasicCardRequestWithUnconvertableData)
     .then(testBasicCardRequestWithNullData)
     .then(testBasicCardRequestWithEmptyData)
     .then(testCanMakePaymentWithBasicCardRequest)
     .then(testBasicCardSimpleResponse)
     .then(testBasicCardDetailedResponse)
+    .then(testSpecialAddressResponse)
     .then(testBasicCardErrorResponse)
     .then(teardown)
     .catch( e => {
       ok(false, "Unexpected error: " + e.name);
       SimpleTest.finish();
     });
   }