Bug 1375345 - Basic card payment implementation. r=baku
authorEden Chuang <echuang@mozilla.com>
Wed, 26 Jul 2017 16:07:07 +0800
changeset 420009 2a26a2f147e4d556e4c4329fe33e2d7febe7cd9f
parent 420008 a3efdf436e9577b4472a468b0189b7743a9cfcf4
child 420010 cce9237283c936c62de6765a4aef690f848abc48
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1375345
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1375345 - Basic card payment implementation. r=baku
dom/interfaces/payments/nsIPaymentActionResponse.idl
dom/interfaces/payments/nsIPaymentRequest.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/PaymentRequestData.cpp
dom/payments/PaymentRequestModule.cpp
dom/payments/PaymentRequestService.cpp
dom/payments/PaymentRequestService.h
dom/payments/PaymentRequestUtils.cpp
dom/payments/PaymentRequestUtils.h
dom/payments/PaymentResponse.cpp
dom/payments/moz.build
dom/webidl/BasicCardPayment.webidl
dom/webidl/moz.build
--- a/dom/interfaces/payments/nsIPaymentActionResponse.idl
+++ b/dom/interfaces/payments/nsIPaymentActionResponse.idl
@@ -1,15 +1,57 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsISupports.idl"
 #include "nsIVariant.idl"
+#include "nsIPaymentAddress.idl"
+
+[builtinclass, scriptable, uuid(2a338575-c688-40ee-a157-7488ab292ef2)]
+interface nsIPaymentResponseData: nsISupports
+{
+  const uint32_t GENERAL_RESPONSE = 0;
+  const uint32_t BASICCARD_RESPONSE = 1;
+
+  readonly attribute uint32_t type;
+  void init(in uint32_t aType);
+};
+
+[builtinclass, scriptable, uuid(b986773e-2b30-4ed2-b8fe-6a96631c8000)]
+interface nsIGeneralResponseData : nsIPaymentResponseData
+{
+  readonly attribute AString data;
+
+  [implicit_jscontext]
+  void initData(in jsval aData);
+};
+
+/*
+  Since PaymentAddress is an no constructor interface type, UI code can not
+  easy create PaymentAddress by calling new PaymentAddress().
+  Unfortunately, BasicCardResponse has a PaymentAddress attribute, billingAddress
+  , it means UI can not create BsaicCardResponse by calling the init() with a
+  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
+{
+  readonly attribute AString data;
+
+  void initData(in AString aCardholderName,
+                in AString aCardNumber,
+                in AString aExpiryMonth,
+                in AString aExpiryYear,
+                in AString aCardSecurityCode,
+                in nsIPaymentAddress billingAddress);
+};
 
 [builtinclass, scriptable, uuid(a607c095-ef60-4a9b-a3d0-0506c60728b3)]
 interface nsIPaymentActionResponse : nsISupports
 {
   /*
    *  Align type to nsIPaymentActionRequest types,
    *  where 1 is for payment request creation.
    *  the action expects no response from UI module.
@@ -89,17 +131,17 @@ interface nsIPaymentShowActionResponse :
   readonly attribute AString payerPhone;
 
   /*
    *  Initialize function for this response.
    */
   void init(in AString aRequestId,
             in uint32_t aAcceptStatus,
             in AString aMethodName,
-            in AString aData,
+            in nsIPaymentResponseData aData,
             in AString aPayerName,
             in AString aPayerEmail,
             in AString aPayerPhone);
 
   /*
    *  Check if the payment is accpeted
    */
   bool isAccepted();
@@ -137,16 +179,26 @@ interface nsIPaymentCompleteActionRespon
 
   /*
    *  Check if the UI is finished.
    */
   bool isCompleted();
 };
 
 %{C++
+#define NS_GENERAL_RESPONSE_DATA_CID \
+  { 0xb986773e, 0x2b30, 0x4ed2, { 0xb8, 0xfe, 0x6a, 0x96, 0x63, 0x1c, 0x80, 0x00 } }
+#define NS_GENERAL_RESPONSE_DATA_CONTRACT_ID \
+  "@mozilla.org/dom/payments/general-response-data;1"
+
+#define NS_BASICCARD_RESPONSE_DATA_CID \
+  { 0x0d55a5e6, 0xd185, 0x44f0, { 0xb9, 0x92, 0xa8, 0xe1, 0x32, 0x1e, 0x4b, 0xce } }
+#define NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID \
+  "@mozilla.org/dom/payments/basiccard-response-data;1"
+
 #define NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID \
   { 0x52fc3f9f, 0xc0cb, 0x4874, { 0xb3, 0xd4, 0xee, 0x4b, 0x6e, 0x9c, 0xbe, 0x9c } }
 #define NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID \
   "@mozilla.org/dom/payments/payment-canmake-action-response;1"
 
 #define NS_PAYMENT_SHOW_ACTION_RESPONSE_CID \
   { 0x184385cb, 0x2d35, 0x4b99, { 0xa9, 0xa3, 0x7c, 0x78, 0x0b, 0xf6, 0x6b, 0x9b } }
 #define NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID \
--- a/dom/interfaces/payments/nsIPaymentRequest.idl
+++ b/dom/interfaces/payments/nsIPaymentRequest.idl
@@ -7,17 +7,18 @@
 #include "nsIVariant.idl"
 
 interface nsIArray;
 
 [scriptable, builtinclass, uuid(2fe296cc-d917-4820-b492-aa42df23f9b4)]
 interface nsIPaymentMethodData : nsISupports
 {
   readonly attribute AString supportedMethods;
-  readonly attribute AString data;
+  [implicit_jscontext]
+  readonly attribute jsval data;
 };
 
 [scriptable, builtinclass, uuid(d22a6f5f-767b-4fea-bf92-68b0b8003eba)]
 interface nsIPaymentCurrencyAmount : nsISupports
 {
   readonly attribute AString currency;
   readonly attribute AString value;
 };
@@ -31,17 +32,18 @@ interface nsIPaymentItem : nsISupports
 };
 
 [scriptable, builtinclass, uuid(74259861-c318-40e8-b3d5-518e701bed80)]
 interface nsIPaymentDetailsModifier : nsISupports
 {
   readonly attribute AString supportedMethods;
   readonly attribute nsIPaymentItem total;
   readonly attribute nsIArray additionalDisplayItems;
-  readonly attribute AString data;
+  [implicit_jscontext]
+  readonly attribute jsval data;
 };
 
 [scriptable, builtinclass, uuid(68341551-3605-4381-b936-41e830aa88fb)]
 interface nsIPaymentShippingOption : nsISupports
 {
   readonly attribute AString id;
   readonly attribute AString label;
   readonly attribute nsIPaymentCurrencyAmount amount;
new file mode 100644
--- /dev/null
+++ b/dom/payments/BasicCardPayment.cpp
@@ -0,0 +1,390 @@
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "BasicCardPayment.h"
+#include "PaymentAddress.h"
+#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 City NS_LITERAL_STRING("city")
+#define DependentLocality NS_LITERAL_STRING("dependentLocality")
+#define PostalCode NS_LITERAL_STRING("postalCode")
+#define SortingCode NS_LITERAL_STRING("sortingCode")
+#define LanguageCode NS_LITERAL_STRING("languageCode")
+#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);
+}
+
+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) ||
+         City.Equals(aKey) ||
+         DependentLocality.Equals(aKey) ||
+         PostalCode.Equals(aKey) ||
+         SortingCode.Equals(aKey) ||
+         LanguageCode.Equals(aKey) ||
+         Organization.Equals(aKey) ||
+         Recipient.Equals(aKey) ||
+         Phone.Equals(aKey);
+}
+
+} // end of namespace
+
+
+StaticRefPtr<BasicCardService> gBasicCardService;
+
+already_AddRefed<BasicCardService>
+BasicCardService::GetService()
+{
+  if (!gBasicCardService) {
+    gBasicCardService = new BasicCardService();
+    ClearOnShutdown(&gBasicCardService);
+  }
+  RefPtr<BasicCardService> service = gBasicCardService;
+  return service.forget();
+}
+
+bool
+BasicCardService::IsBasicCardPayment(const nsAString& aSupportedMethods)
+{
+  return aSupportedMethods.Equals(NS_LITERAL_STRING("basic-card"));
+}
+
+bool
+BasicCardService::IsValidBasicCardRequest(JSContext* aCx,
+                                          JSObject* aData,
+                                          nsAString& aErrorMsg)
+{
+  if (!aData) {
+    return true;
+  }
+  JS::RootedValue data(aCx, JS::ObjectValue(*aData));
+
+  BasicCardRequest request;
+  if (!request.Init(aCx, data)) {
+    aErrorMsg.AssignLiteral("Fail to convert methodData.data to BasicCardRequest.");
+    return false;
+  }
+
+  if (request.mSupportedNetworks.WasPassed()) {
+    for (const nsString& network : request.mSupportedNetworks.Value()) {
+      if (!IsValidNetwork(network)) {
+        aErrorMsg.Assign(network + NS_LITERAL_STRING(" is not an valid network."));
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool
+BasicCardService::IsValidExpiryMonth(const nsAString& aExpiryMonth)
+{
+  // ExpiryMonth can only be
+  //   1. empty string
+  //   2. 01 ~ 12
+  if (aExpiryMonth.IsEmpty()) {
+    return true;
+  }
+  if (aExpiryMonth.Length() != 2) {
+    return false;
+  }
+  // can only be 00 ~ 09
+  if (aExpiryMonth.CharAt(0) == '0') {
+    if (aExpiryMonth.CharAt(1) < '0' || aExpiryMonth.CharAt(1) > '9') {
+      return false;
+    }
+    return true;
+  }
+  // can only be 11 or 12
+  if (aExpiryMonth.CharAt(0) == '1') {
+    if (aExpiryMonth.CharAt(1) != '1' && aExpiryMonth.CharAt(1) != '2') {
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool
+BasicCardService::IsValidExpiryYear(const nsAString& aExpiryYear)
+{
+  // ExpiryYear can only be
+  //   1. empty string
+  //   2. 0000 ~ 9999
+  if (!aExpiryYear.IsEmpty()) {
+    if (aExpiryYear.Length() != 4) {
+      return false;
+    }
+    for (uint32_t index = 0; index < 4; ++index) {
+      if (aExpiryYear.CharAt(index) < '0' ||
+          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, City, aResult);
+  EncodeAddressProperty(aBillingAddress, DependentLocality, aResult);
+  EncodeAddressProperty(aBillingAddress, PostalCode, aResult);
+  EncodeAddressProperty(aBillingAddress, SortingCode, aResult);
+  EncodeAddressProperty(aBillingAddress, LanguageCode, 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 city;
+  nsAutoString dependentLocality;
+  nsAutoString postalCode;
+  nsAutoString sortingCode;
+  nsAutoString languageCode;
+  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, City, city);
+    DecodeAddressProperty(key, value, DependentLocality, dependentLocality);
+    DecodeAddressProperty(key, value, PostalCode, postalCode);
+    DecodeAddressProperty(key, value, SortingCode, sortingCode);
+    DecodeAddressProperty(key, value, LanguageCode, languageCode);
+    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,
+                                                           city,
+                                                           dependentLocality,
+                                                           postalCode,
+                                                           sortingCode,
+                                                           languageCode,
+                                                           organization,
+                                                           recipient,
+                                                           phone);
+  }
+  return NS_OK;
+}
+
+#ifdef PaymentBasicCardMacros
+#undef PaymentBasicCardMacros
+#undef EncodeBasicCardProperty
+#undef EncodeAddressProperty
+#undef DecodeBasicCardProperty
+#undef DecodeBasicCardCardNumber
+#undef DecodeAddressProperty
+#undef DecodeAddressLine
+#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 City
+#undef DependentLocality
+#undef PostalCode
+#undef SortingCode
+#undef LanguageCode
+#undef Organization
+#undef Recipient
+#undef Phone
+#undef PropertySpliter
+#undef KeyValueSpliter
+#undef AddressLineSpliter
+#endif
+
+} // end of namespace dom
+} // end of namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/payments/BasicCardPayment.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_BasicCardPayment_h
+#define mozilla_dom_BasicCardPayment_h
+
+#include "mozilla/dom/BasicCardPaymentBinding.h"
+#include "nsPIDOMWindow.h"
+#include "nsIPaymentAddress.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class BasicCardService final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(BasicCardService)
+
+  static already_AddRefed<BasicCardService> GetService();
+
+  bool IsBasicCardPayment(const nsAString& aSupportedMethods);
+  bool IsValidBasicCardRequest(JSContext* aCx, JSObject* aData, nsAString& aErrorMsg);
+  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
+
+#endif
--- a/dom/payments/PaymentActionResponse.cpp
+++ b/dom/payments/PaymentActionResponse.cpp
@@ -1,20 +1,130 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "nsIRunnable.h"
+#include "PaymentRequestUtils.h"
+#include "BasicCardPayment.h"
 
 namespace mozilla {
 namespace dom {
 
+/* PaymentResponseData */
+
+NS_IMPL_ISUPPORTS(PaymentResponseData, nsIPaymentResponseData)
+
+NS_IMETHODIMP
+PaymentResponseData::GetType(uint32_t* aType)
+{
+  NS_ENSURE_ARG_POINTER(aType);
+  *aType = mType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PaymentResponseData::Init(const uint32_t aType)
+{
+  if (aType != nsIPaymentResponseData::GENERAL_RESPONSE &&
+      aType != nsIPaymentResponseData::BASICCARD_RESPONSE) {
+    return NS_ERROR_FAILURE;
+  }
+  mType = aType;
+  return NS_OK;
+}
+
+/* GeneralResponseData */
+
+NS_IMPL_ISUPPORTS_INHERITED(GeneralResponseData,
+                            PaymentResponseData,
+                            nsIGeneralResponseData)
+
+GeneralResponseData::GeneralResponseData()
+  : mData(NS_LITERAL_STRING("{}"))
+{
+  Init(nsIPaymentResponseData::GENERAL_RESPONSE);
+}
+
+NS_IMETHODIMP
+GeneralResponseData::GetData(nsAString& aData)
+{
+  aData = mData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeneralResponseData::InitData(JS::HandleValue aValue, JSContext* aCx)
+{
+  if (aValue.isNullOrUndefined()) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = SerializeFromJSVal(aCx, aValue, mData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+/* BasicCardResponseData */
+
+NS_IMPL_ISUPPORTS_INHERITED(BasicCardResponseData,
+                            PaymentResponseData,
+                            nsIBasicCardResponseData)
+
+BasicCardResponseData::BasicCardResponseData()
+{
+  Init(nsIPaymentResponseData::BASICCARD_RESPONSE);
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetData(nsAString& aData)
+{
+  aData = mData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::InitData(const nsAString& aCardholderName,
+                                const nsAString& aCardNumber,
+                                const nsAString& aExpiryMonth,
+                                const nsAString& aExpiryYear,
+                                const nsAString& aCardSecurityCode,
+                                nsIPaymentAddress* aBillingAddress)
+{
+  // cardNumber is a required attribute, cannot be empty;
+  if (aCardNumber.IsEmpty()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+
+  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;
+  }
+  return NS_OK;
+}
+
 /* PaymentActionResponse */
 
 NS_IMPL_ISUPPORTS(PaymentActionResponse,
                   nsIPaymentActionResponse)
 
 PaymentActionResponse::PaymentActionResponse()
   : mRequestId(EmptyString())
   , mType(nsIPaymentActionResponse::NO_TYPE)
@@ -116,25 +226,59 @@ PaymentShowActionResponse::GetPayerPhone
   aPayerPhone = mPayerPhone;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentShowActionResponse::Init(const nsAString& aRequestId,
                                 const uint32_t aAcceptStatus,
                                 const nsAString& aMethodName,
-                                const nsAString& aData,
+                                nsIPaymentResponseData* aData,
                                 const nsAString& aPayerName,
                                 const nsAString& aPayerEmail,
                                 const nsAString& aPayerPhone)
 {
+  NS_ENSURE_ARG_POINTER(aData);
   mRequestId = aRequestId;
   mAcceptStatus = aAcceptStatus;
   mMethodName = aMethodName;
-  mData = aData;
+
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+  bool isBasicCardPayment = service->IsBasicCardPayment(mMethodName);
+
+  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;
+  }
+
   mPayerName = aPayerName;
   mPayerEmail = aPayerEmail;
   mPayerPhone = aPayerPhone;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentShowActionResponse::IsAccepted(bool* aIsAccepted)
--- a/dom/payments/PaymentActionResponse.h
+++ b/dom/payments/PaymentActionResponse.h
@@ -9,16 +9,63 @@
 
 #include "nsIPaymentActionResponse.h"
 
 namespace mozilla {
 namespace dom {
 
 class PaymentRequestParent;
 
+class PaymentResponseData : public nsIPaymentResponseData
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPAYMENTRESPONSEDATA
+
+  PaymentResponseData() = default;
+
+protected:
+  virtual ~PaymentResponseData() = default;
+
+  uint32_t mType;
+};
+
+class GeneralResponseData final : public PaymentResponseData
+                                , public nsIGeneralResponseData
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIPAYMENTRESPONSEDATA(PaymentResponseData::)
+  NS_DECL_NSIGENERALRESPONSEDATA
+
+  GeneralResponseData();
+
+private:
+  ~GeneralResponseData() = default;
+
+  nsString mData;
+};
+
+class BasicCardResponseData final : public nsIBasicCardResponseData
+                                  , public PaymentResponseData
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIPAYMENTRESPONSEDATA(PaymentResponseData::)
+  NS_DECL_NSIBASICCARDRESPONSEDATA
+
+  BasicCardResponseData();
+
+private:
+  ~BasicCardResponseData() = default;
+
+  nsString mData;
+  nsCOMPtr<nsIPaymentAddress> mBillingAddress;
+};
+
 class PaymentActionResponse : public nsIPaymentActionResponse
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTACTIONRESPONSE
 
   PaymentActionResponse();
 
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -3,16 +3,17 @@
 /* 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 "mozilla/dom/Element.h"
 #include "mozilla/dom/PaymentRequest.h"
 #include "mozilla/dom/PaymentResponse.h"
 #include "nsContentUtils.h"
+#include "BasicCardPayment.h"
 #include "PaymentRequestManager.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PaymentRequest,
@@ -47,30 +48,44 @@ NS_IMPL_RELEASE_INHERITED(PaymentRequest
 
 bool
 PaymentRequest::PrefEnabled(JSContext* aCx, JSObject* aObj)
 {
   return Preferences::GetBool("dom.payments.request.enabled");
 }
 
 bool
-PaymentRequest::IsValidMethodData(const Sequence<PaymentMethodData>& aMethodData,
+PaymentRequest::IsValidMethodData(JSContext* aCx,
+                                  const Sequence<PaymentMethodData>& aMethodData,
                                   nsAString& aErrorMsg)
 {
   if (!aMethodData.Length()) {
     aErrorMsg.AssignLiteral("At least one payment method is required.");
     return false;
   }
 
   for (const PaymentMethodData& methodData : aMethodData) {
-    if (!methodData.mSupportedMethods.Length()) {
+    if (methodData.mSupportedMethods.IsEmpty()) {
       aErrorMsg.AssignLiteral(
-        "At least one payment method identifier is required.");
+        "Payment method identifier is required.");
       return false;
     }
+    RefPtr<BasicCardService> service = BasicCardService::GetService();
+    MOZ_ASSERT(service);
+    if (service->IsBasicCardPayment(methodData.mSupportedMethods)) {
+      if (!methodData.mData.WasPassed()) {
+        continue;
+      }
+      MOZ_ASSERT(aCx);
+      if (!service->IsValidBasicCardRequest(aCx,
+                                            methodData.mData.Value(),
+                                            aErrorMsg)) {
+        return false;
+      }
+    }
   }
 
   return true;
 }
 
 bool
 PaymentRequest::IsValidNumber(const nsAString& aItem,
                               const nsAString& aStr,
@@ -254,17 +269,19 @@ PaymentRequest::Constructor(const Global
         }
       }
     }
     node = parentNode;
   } while (node);
 
   // Check payment methods and details
   nsAutoString message;
-  if (!IsValidMethodData(aMethodData, message) ||
+  if (!IsValidMethodData(nsContentUtils::GetCurrentJSContext(),
+                         aMethodData,
+                         message) ||
       !IsValidDetailsInit(aDetails, message)) {
     aRv.ThrowTypeError<MSG_ILLEGAL_PR_CONSTRUCTOR>(message);
     return nullptr;
   }
 
   RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
   if (NS_WARN_IF(!manager)) {
     return nullptr;
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -31,17 +31,18 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<PaymentRequest>
   CreatePaymentRequest(nsPIDOMWindowInner* aWindow, nsresult& aRv);
 
   static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
 
-  static bool IsValidMethodData(const Sequence<PaymentMethodData>& aMethodData,
+  static bool IsValidMethodData(JSContext* aCx,
+                                const Sequence<PaymentMethodData>& aMethodData,
                                 nsAString& aErrorMsg);
 
   static bool
   IsValidNumber(const nsAString& aItem,
                 const nsAString& aStr,
                 nsAString& aErrorMsg);
   static bool
   IsNonNegativeNumber(const nsAString& aItem,
--- a/dom/payments/PaymentRequestData.cpp
+++ b/dom/payments/PaymentRequestData.cpp
@@ -39,19 +39,26 @@ PaymentMethodData::Create(const IPCPayme
 NS_IMETHODIMP
 PaymentMethodData::GetSupportedMethods(nsAString& aSupportedMethods)
 {
   aSupportedMethods = mSupportedMethods;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PaymentMethodData::GetData(nsAString& aData)
+PaymentMethodData::GetData(JSContext* aCx, JS::MutableHandleValue aData)
 {
-  aData = mData;
+  if (mData.IsEmpty()) {
+    aData.set(JS::NullValue());
+    return NS_OK;
+  }
+  nsresult rv = DeserializeToJSValue(mData, aCx ,aData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   return NS_OK;
 }
 
 /* PaymentCurrencyAmount */
 
 NS_IMPL_ISUPPORTS(PaymentCurrencyAmount,
                   nsIPaymentCurrencyAmount)
 
@@ -217,19 +224,26 @@ PaymentDetailsModifier::GetAdditionalDis
 {
   NS_ENSURE_ARG_POINTER(aAdditionalDisplayItems);
   nsCOMPtr<nsIArray> additionalItems = mAdditionalDisplayItems;
   additionalItems.forget(aAdditionalDisplayItems);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PaymentDetailsModifier::GetData(nsAString& aData)
+PaymentDetailsModifier::GetData(JSContext* aCx, JS::MutableHandleValue aData)
 {
-  aData = mData;
+  if (mData.IsEmpty()) {
+    aData.set(JS::NullValue());
+    return NS_OK;
+  }
+  nsresult rv = DeserializeToJSValue(mData, aCx ,aData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   return NS_OK;
 }
 
 /* PaymentShippingOption */
 
 NS_IMPL_ISUPPORTS(PaymentShippingOption,
                   nsIPaymentShippingOption)
 
--- a/dom/payments/PaymentRequestModule.cpp
+++ b/dom/payments/PaymentRequestModule.cpp
@@ -5,79 +5,91 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ModuleUtils.h"
 #include "PaymentActionRequest.h"
 #include "PaymentActionResponse.h"
 #include "PaymentRequestData.h"
 #include "PaymentRequestService.h"
 
+using mozilla::dom::GeneralResponseData;
+using mozilla::dom::BasicCardResponseData;
 using mozilla::dom::PaymentActionRequest;
 using mozilla::dom::PaymentCreateActionRequest;
 using mozilla::dom::PaymentCompleteActionRequest;
 using mozilla::dom::PaymentUpdateActionRequest;
 using mozilla::dom::PaymentCanMakeActionResponse;
 using mozilla::dom::PaymentAbortActionResponse;
 using mozilla::dom::PaymentShowActionResponse;
 using mozilla::dom::PaymentCompleteActionResponse;
 using mozilla::dom::payments::PaymentAddress;
 using mozilla::dom::PaymentRequestService;
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(GeneralResponseData)
+NS_GENERIC_FACTORY_CONSTRUCTOR(BasicCardResponseData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCreateActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCompleteActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentUpdateActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCanMakeActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentAbortActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentShowActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCompleteActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentAddress)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(PaymentRequestService,
                                          PaymentRequestService::GetSingleton)
 
+NS_DEFINE_NAMED_CID(NS_GENERAL_RESPONSE_DATA_CID);
+NS_DEFINE_NAMED_CID(NS_BASICCARD_RESPONSE_DATA_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_CREATE_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_COMPLETE_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_UPDATE_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ABORT_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_SHOW_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ADDRESS_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_REQUEST_SERVICE_CID);
 
 static const mozilla::Module::CIDEntry kPaymentRequestCIDs[] = {
+  { &kNS_GENERAL_RESPONSE_DATA_CID, false, nullptr, GeneralResponseDataConstructor},
+  { &kNS_BASICCARD_RESPONSE_DATA_CID, false, nullptr, BasicCardResponseDataConstructor},
   { &kNS_PAYMENT_ACTION_REQUEST_CID, false, nullptr, PaymentActionRequestConstructor},
   { &kNS_PAYMENT_CREATE_ACTION_REQUEST_CID, false, nullptr, PaymentCreateActionRequestConstructor},
   { &kNS_PAYMENT_COMPLETE_ACTION_REQUEST_CID, false, nullptr, PaymentCompleteActionRequestConstructor},
   { &kNS_PAYMENT_UPDATE_ACTION_REQUEST_CID, false, nullptr, PaymentUpdateActionRequestConstructor},
   { &kNS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID, false, nullptr, PaymentCanMakeActionResponseConstructor},
   { &kNS_PAYMENT_ABORT_ACTION_RESPONSE_CID, false, nullptr, PaymentAbortActionResponseConstructor},
   { &kNS_PAYMENT_SHOW_ACTION_RESPONSE_CID, false, nullptr, PaymentShowActionResponseConstructor},
   { &kNS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID, false, nullptr, PaymentCompleteActionResponseConstructor},
   { &kNS_PAYMENT_ADDRESS_CID, false, nullptr, PaymentAddressConstructor},
   { &kNS_PAYMENT_REQUEST_SERVICE_CID, true, nullptr, PaymentRequestServiceConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kPaymentRequestContracts[] = {
+  { NS_GENERAL_RESPONSE_DATA_CONTRACT_ID, &kNS_GENERAL_RESPONSE_DATA_CID },
+  { NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID, &kNS_BASICCARD_RESPONSE_DATA_CID },
   { NS_PAYMENT_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_ACTION_REQUEST_CID },
   { NS_PAYMENT_CREATE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_CREATE_ACTION_REQUEST_CID },
   { NS_PAYMENT_COMPLETE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_COMPLETE_ACTION_REQUEST_CID },
   { NS_PAYMENT_UPDATE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_UPDATE_ACTION_REQUEST_CID },
   { NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID },
   { NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_ABORT_ACTION_RESPONSE_CID },
   { NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_SHOW_ACTION_RESPONSE_CID },
   { NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID },
   { NS_PAYMENT_ADDRESS_CONTRACT_ID, &kNS_PAYMENT_ADDRESS_CID },
   { NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID, &kNS_PAYMENT_REQUEST_SERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kPaymentRequestCategories[] = {
+  { "payment-request", "GeneralResponseData", NS_GENERAL_RESPONSE_DATA_CONTRACT_ID },
+  { "payment-request", "BasicCardResponseData", NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID },
   { "payment-request", "PaymentActionRequest", NS_PAYMENT_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentCreateActionRequest", NS_PAYMENT_CREATE_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentCompleteActionRequest", NS_PAYMENT_COMPLETE_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentUpdateActionRequest", NS_PAYMENT_UPDATE_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentCanMakeActionResponse", NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentAbortActionResponse", NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentShowActionResponse", NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentCompleteActionResponse", NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID },
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "mozilla/ClearOnShutdown.h"
 #include "PaymentRequestData.h"
 #include "PaymentRequestService.h"
+#include "BasicCardPayment.h"
 
 namespace mozilla {
 namespace dom {
 
 StaticRefPtr<PaymentRequestService> gPaymentService;
 
 namespace {
 
@@ -295,41 +296,57 @@ PaymentRequestService::RequestPayment(ns
         new payments::PaymentRequest(tabId, requestId, methodData, details, options);
 
       if (!mRequestQueue.AppendElement(payment, mozilla::fallible)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
       break;
     }
     /*
-     *  TODO: 1. Check basic card support once the Basic Card Payment spec is
-     *           implemented.
-     *           https://www.w3.org/TR/payment-method-basic-card/
-     *        2. Check third party payment app support by traversing all
+     *  TODO: 1. Check third party payment app support by traversing all
      *           registered third party payment apps.
      */
     case nsIPaymentActionRequest::CANMAKE_ACTION: {
-      rv = CallTestingUIAction(requestId, type);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_ERROR_FAILURE;
+      if (IsBasicCardPayment(requestId)) {
+        nsCOMPtr<nsIPaymentCanMakeActionResponse> canMakeResponse =
+          do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID);
+        MOZ_ASSERT(canMakeResponse);
+        rv = canMakeResponse->Init(requestId, true);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        nsCOMPtr<nsIPaymentActionResponse> response = do_QueryInterface(canMakeResponse);
+        MOZ_ASSERT(response);
+        rv = RespondPayment(response);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+      } else {
+        rv = CallTestingUIAction(requestId, type);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NS_ERROR_FAILURE;
+        }
       }
       break;
     }
     /*
      *  TODO: Launch/inform payment UI here once the UI module is implemented.
      */
     case nsIPaymentActionRequest::SHOW_ACTION: {
       if (mShowingRequest) {
+        nsCOMPtr<nsIPaymentResponseData> responseData =
+          do_CreateInstance(NS_GENERAL_RESPONSE_DATA_CONTRACT_ID);
+        MOZ_ASSERT(responseData);
         nsCOMPtr<nsIPaymentShowActionResponse> showResponse =
           do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID);
         MOZ_ASSERT(showResponse);
         rv = showResponse->Init(requestId,
                                 nsIPaymentActionResponse::PAYMENT_REJECTED,
                                 EmptyString(),
-                                EmptyString(),
+                                responseData,
                                 EmptyString(),
                                 EmptyString(),
                                 EmptyString());
         nsCOMPtr<nsIPaymentActionResponse> response = do_QueryInterface(showResponse);
         MOZ_ASSERT(response);
         rv = RespondPayment(response);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
@@ -514,10 +531,37 @@ PaymentRequestService::RemoveActionCallb
   nsCOMPtr<nsIPaymentActionCallback> callback;
   if (!mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) {
     return NS_ERROR_FAILURE;
   }
   mCallbackHashtable.Remove(aRequestId);
   return NS_OK;
 }
 
+bool
+PaymentRequestService::IsBasicCardPayment(const nsAString& aRequestId)
+{
+  nsCOMPtr<nsIPaymentRequest> payment;
+  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(payment));
+  NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIArray> methods;
+  rv = payment->GetPaymentMethods(getter_AddRefs(methods));
+  NS_ENSURE_SUCCESS(rv, false);
+  uint32_t length;
+  rv = methods->GetLength(&length);
+  NS_ENSURE_SUCCESS(rv, false);
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+  for (uint32_t index = 0; index < length; ++index) {
+    nsCOMPtr<nsIPaymentMethodData> method = do_QueryElementAt(methods, index);
+    MOZ_ASSERT(method);
+    nsAutoString supportedMethods;
+    rv = method->GetSupportedMethods(supportedMethods);
+    NS_ENSURE_SUCCESS(rv, false);
+    if (service->IsBasicCardPayment(supportedMethods)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentRequestService.h
+++ b/dom/payments/PaymentRequestService.h
@@ -41,16 +41,19 @@ private:
                     nsIPaymentActionCallback* aCallback);
   nsresult
   RemoveActionCallback(const nsAString& aRequestId);
 
   // this method is only used for testing
   nsresult
   CallTestingUIAction(const nsAString& aRequestId, uint32_t aActionType);
 
+  bool
+  IsBasicCardPayment(const nsAString& aRequestId);
+
   FallibleTArray<nsCOMPtr<nsIPaymentRequest>> mRequestQueue;
 
   nsInterfaceHashtable<nsStringHashKey, nsIPaymentActionCallback> mCallbackHashtable;
 
   nsCOMPtr<nsIPaymentUIService> mTestingUIService;
 
   nsCOMPtr<nsIPaymentRequest> mShowingRequest;
 };
--- a/dom/payments/PaymentRequestUtils.cpp
+++ b/dom/payments/PaymentRequestUtils.cpp
@@ -9,27 +9,43 @@
 #include "nsIMutableArray.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIJSON.h"
 
 namespace mozilla {
 namespace dom {
 
 nsresult
-SerializeFromJSObject(JSContext* aCx, JS::HandleObject aObject, nsAString& aSerializedObject) {
+SerializeFromJSObject(JSContext* aCx, JS::HandleObject aObject, nsAString& aSerializedObject)
+{
+  MOZ_ASSERT(aCx);
   nsCOMPtr<nsIJSON> serializer = do_CreateInstance("@mozilla.org/dom/json;1");
   if (NS_WARN_IF(!serializer)) {
     return NS_ERROR_FAILURE;
   }
   JS::RootedValue value(aCx, JS::ObjectValue(*aObject));
   return serializer->EncodeFromJSVal(value.address(), aCx, aSerializedObject);
 }
 
 nsresult
-DeserializeToJSObject(const nsAString& aSerializedObject, JSContext* aCx, JS::MutableHandleObject aObject) {
+SerializeFromJSVal(JSContext* aCx, JS::HandleValue aValue, nsAString& aSerializedValue)
+{
+  MOZ_ASSERT(aCx);
+  nsCOMPtr<nsIJSON> serializer = do_CreateInstance("@mozilla.org/dom/json;1");
+  if (NS_WARN_IF(!serializer)) {
+    return NS_ERROR_FAILURE;
+  }
+  JS::RootedValue value(aCx, aValue.get());
+  return serializer->EncodeFromJSVal(value.address(), aCx, aSerializedValue);
+}
+
+nsresult
+DeserializeToJSObject(const nsAString& aSerializedObject, JSContext* aCx, JS::MutableHandleObject aObject)
+{
+  MOZ_ASSERT(aCx);
   nsCOMPtr<nsIJSON> deserializer = do_CreateInstance("@mozilla.org/dom/json;1");
   if (NS_WARN_IF(!deserializer)) {
     return NS_ERROR_FAILURE;
   }
   JS::RootedValue value(aCx);
   JS::MutableHandleValue handleVal(&value);
   nsresult rv = deserializer->DecodeToJSVal(aSerializedObject, aCx, handleVal);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -38,10 +54,25 @@ DeserializeToJSObject(const nsAString& a
   if (value.isObject()) {
     aObject.set(&value.toObject());
   } else {
     aObject.set(nullptr);
   }
   return NS_OK;
 }
 
+nsresult
+DeserializeToJSValue(const nsAString& aSerializedObject, JSContext* aCx, JS::MutableHandleValue aValue)
+{
+  MOZ_ASSERT(aCx);
+  nsCOMPtr<nsIJSON> deserializer = do_CreateInstance("@mozilla.org/dom/json;1");
+  if (NS_WARN_IF(!deserializer)) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = deserializer->DecodeToJSVal(aSerializedObject, aCx, aValue);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentRequestUtils.h
+++ b/dom/payments/PaymentRequestUtils.h
@@ -14,16 +14,26 @@ namespace mozilla {
 namespace dom {
 
 nsresult
 SerializeFromJSObject(JSContext* aCx,
                       JS::HandleObject aObject,
                       nsAString& aSerializedObject);
 
 nsresult
+SerializeFromJSVal(JSContext* aCx,
+                   JS::HandleValue aValue,
+                   nsAString& aSerializedValue);
+
+nsresult
 DeserializeToJSObject(const nsAString& aSerializedObject,
                       JSContext* aCx,
                       JS::MutableHandleObject aObject);
 
+nsresult
+DeserializeToJSValue(const nsAString& aSerializedObject,
+                     JSContext* aCx,
+                     JS::MutableHandleValue aValue);
+
 } // end of namespace dom
 } // end of namespace mozilla
 
 #endif
--- a/dom/payments/PaymentResponse.cpp
+++ b/dom/payments/PaymentResponse.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "mozilla/dom/PaymentResponse.h"
+#include "mozilla/dom/BasicCardPaymentBinding.h"
+#include "BasicCardPayment.h"
+#include "PaymentAddress.h"
 #include "PaymentRequestUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PaymentResponse, mOwner,
                                       mShippingAddress, mPromise)
 
@@ -68,17 +71,35 @@ void
 PaymentResponse::GetMethodName(nsString& aRetVal) const
 {
   aRetVal = mMethodName;
 }
 
 void
 PaymentResponse::GetDetails(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const
 {
-  DeserializeToJSObject(mDetails, aCx, aRetVal);
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+  if (!service->IsBasicCardPayment(mMethodName)) {
+    DeserializeToJSObject(mDetails, aCx, aRetVal);
+  } else {
+    BasicCardResponse response;
+    nsresult rv = service->DecodeBasicCardData(mDetails, mOwner, response);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    MOZ_ASSERT(aCx);
+    JS::RootedValue value(aCx);
+    JS::MutableHandleValue handleValue(&value);
+    if (NS_WARN_IF(!response.ToObjectInternal(aCx, handleValue))) {
+      return;
+    }
+    aRetVal.set(&handleValue.toObject());
+  }
 }
 
 void
 PaymentResponse::GetShippingOption(nsString& aRetVal) const
 {
   aRetVal = mShippingOption;
 }
 
--- a/dom/payments/moz.build
+++ b/dom/payments/moz.build
@@ -17,16 +17,17 @@ EXPORTS.mozilla.dom += [
     'PaymentAddress.h',
     'PaymentRequest.h',
     'PaymentRequestManager.h',
     'PaymentRequestUpdateEvent.h',
     'PaymentResponse.h',
 ]
 
 UNIFIED_SOURCES += [
+    'BasicCardPayment.cpp',
     'PaymentActionRequest.cpp',
     'PaymentActionResponse.cpp',
     'PaymentAddress.cpp',
     'PaymentRequest.cpp',
     'PaymentRequestData.cpp',
     'PaymentRequestManager.cpp',
     'PaymentRequestModule.cpp',
     'PaymentRequestService.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BasicCardPayment.webidl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this WebIDL file is
+ *   https://www.w3.org/TR/payment-request/#paymentrequest-interface
+ */
+enum BasicCardType {
+  "credit",
+  "debit",
+  "prepaid"
+};
+
+dictionary BasicCardRequest {
+  sequence<DOMString> supportedNetworks;
+  sequence<BasicCardType> supportedTypes;
+};
+
+dictionary BasicCardResponse {
+           DOMString cardholderName;
+  required DOMString cardNumber;
+           DOMString expiryMonth;
+           DOMString expiryYear;
+           DOMString cardSecurityCode;
+           PaymentAddress? billingAddress;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -406,16 +406,17 @@ WEBIDL_FILES = [
     'AudioStreamTrack.webidl',
     'AudioTrack.webidl',
     'AudioTrackList.webidl',
     'AudioWorkletGlobalScope.webidl',
     'AutocompleteInfo.webidl',
     'BarProp.webidl',
     'BaseAudioContext.webidl',
     'BaseKeyframeTypes.webidl',
+    'BasicCardPayment.webidl',
     'BatteryManager.webidl',
     'BeforeUnloadEvent.webidl',
     'BiquadFilterNode.webidl',
     'Blob.webidl',
     'BoxObject.webidl',
     'BroadcastChannel.webidl',
     'BrowserElement.webidl',
     'BrowserElementDictionaries.webidl',