Problem/Motivation

The Authorize.net module has the most strict PCI compliance requirements. We can improve this to SAQ-A-EP by implementing Accept.js. The Accept.js implementation allows you to embed input fields which are then submitted to the Authorize.net servers directly via JavaScript and return a nonce payment token. This is similar to how Braintree, Stripe, and Square payment gateways work.

Using Accept.js and nonce based payment workflow fixes #2892467: Does not support anonymous checkout.

Proposed resolution

Implement Accept.js

Integrating the Accept.js library into your application helps minimize your PCI compliance because it sends payment data directly to Authorize.Net. Payment data submitted through Accept.js does not reach your server. Accept.js also provides the ease of flexibility of your own design and form.

API: https://developer.authorize.net/api/reference/features/acceptjs.html

Remaining tasks

While Accept.js is SAQ A-EP and Braintree/Square/Stripe are SAQ A, the implementation is the same. Each module can be used as a reference point. It's just that the latter modules use div which turn into iframes rather than direct inputs.

User interface changes

The credit card form will change appearance due to class name changes.

API changes

No direct API changes. Only internal method logic changes.

Data model changes

None.

Comments

mglaman created an issue. See original summary.

shootersolutions’s picture

Based on my understanding, this is a critical issue for Authorize.net standard, where only communication is to be from the Customer to them:

As Authorize.net does not in any way their service to Ever be used where communication goes Anywhere else but from the customer to Authorize.net and then back to the server;

As standard exists to have the credit card data not to go through any in between server, and should be the standard option of an Auth.net module.

jenlampton’s picture

I'm also interested in switching to accept.js on a Drupal 7 commerce site. Is there another issue where that's being worked on already, or should I create one?

pjcdawkins’s picture

Assigned: Unassigned » pjcdawkins

Having a look at this in a sprint...

mglaman’s picture

Assigned: pjcdawkins » Unassigned
Issue tags: +MidCamp2017

pjcdawkins, any update? Tagging for MidCamp sprint.

mglaman’s picture

Assigned: Unassigned » mglaman
Priority: Normal » Critical
Issue tags: -MidCamp2017

Bumping priority and documenting path forward.

mglaman’s picture

Issue summary: View changes
mglaman’s picture

Issue summary: View changes
mglaman’s picture

Issue summary: View changes
mglaman’s picture

Assigned: mglaman » Unassigned

Unassigning, done updating summary.

mglaman’s picture

Issue summary: View changes

Added OpaqueData object to the SDK. https://github.com/commerceguys/authnet/commit/7c351f60b942c9fd08a53e32b...

When working on the patch, make sure SDK is checked out to that commit. This will be passed to the payment property instead of the CreditCard object.

nikathone’s picture

Status: Active » Needs work

Started some work on this at https://github.com/drupalcommerce/commerce_authnet/pull/2 still need to refactor the js and update the tests. Any feedback are welcome.

mglaman’s picture

With this we need to update ludwig.json to target beta2

mglaman’s picture

StatusFileSize
new36.06 KB

Putting nikathone's latest patch here.

czigor’s picture

Status: Needs work » Needs review
StatusFileSize
new28.21 KB

Fixed the issues raised in the PR review. Changed the SDK lib version in ludwig.json to beta2.

Could you have a look, @nikathone?

chrisrockwell’s picture

+++ b/src/Plugin/Commerce/PaymentGateway/AuthorizeNet.php
@@ -193,26 +223,37 @@ class AuthorizeNet extends OnsitePaymentGatewayBase implements AuthorizeNetInter
+    $transaction_request->addOrder(new OrderDataType([
       'invoiceNumber' => $order->getOrderNumber(),
     ]));

Maybe shouldn't be fixed here, but this is likely to be NULL. See #2918851: invoiceNumber is null

czigor’s picture

StatusFileSize
new28.15 KB

Just removing the already commented 'state' => $address->getAdministrativeArea(), line.

subhojit777’s picture

@czigor I applied the patch #17 but it is giving an error

Notice: Undefined index: customer_email in Drupal\commerce_authnet\Plugin\Commerce\PaymentGateway\AuthorizeNet->doCreatePaymentMethod() (line 489 of modules/contrib/commerce_authnet/src/Plugin/Commerce/PaymentGateway/AuthorizeNet.php)

mglaman’s picture

+++ b/src/Plugin/Commerce/PaymentGateway/AuthorizeNet.php
@@ -427,7 +485,7 @@ class AuthorizeNet extends OnsitePaymentGatewayBase implements AuthorizeNetInter
-          'email' => $owner->getEmail(),
+          'email' => $payment_details['customer_email'],

+++ b/src/PluginForm/AuthorizeNet/PaymentMethodAddForm.php
@@ -0,0 +1,138 @@
+      $payment_details['customer_email'] = $values['contact_information']['email'];
+      $form_state->setValue(['payment_information', 'add_payment_method', 'payment_details'], $payment_details);

Because we're not defaulting the value anywhere, from quick search.

Why did this change from order->getEmail?

subhojit777’s picture

I am also getting a warning Unsupported credit card type "XXXX1111". I am using a sandbox authorize.net account. I don't know whether I am missing some setting, or the patch is not working.

subhojit777’s picture

Okay. So I figured the error in #18. I am using a custom payment pane and that is why it is unable to get the provided email id.

Re: #19 I noticed that $owner is the current order's user. I am trying to do anonymous checkout, and in that case it is empty.

There might be a better way to get the email provided during checkout, that would be independent of the payment pane.

subhojit777’s picture

Re: #20

I am getting confirmation mails from ADN that the payment has been approved. So it looks like the patch is partially working. Is able to trigger the payment, but somehow the patch is unable to catch the correct reponse and throwing the warning? Maybe.. due the custom payment pane that I am using this is happening.

mglaman’s picture

Maybe.. due the custom payment pane that I am using this is happening.

Please test on vanilla 2.x

subhojit777’s picture

Sorry 2.x version of what? I checked that there is only 1.x of commerce_authnet available.

travis-bradbury’s picture

Vanilla seems fine. It worked fine with the default checkout panes.

Subhojit, the issue appears to be from overriding the payment_information and contact_information panes. Drupal\commerce_authnet\PluginForm\AuthorizeNet\PaymentMethodAddForm::submitCreditCardForm() uses the IDs of the original panes.

nikathone’s picture

Status: Needs review » Needs work

The accept.js feature has been recently updated by Authorize.net to include using the javascript with the hosted form. I am wondering if we shouldn't update our js to use this approach instead since the hosted form sample response object:

{
  "opaqueData": {
    "dataDescriptor": "COMMON.ACCEPT.INAPP.PAYMENT",
    "dataValue": "eyJjb2RlIjoiNTBfMl8wNjAwMDUzNUE1OTkzREQ1NEM1NzY0OTgwNTZDQzY5MEVBRjY5MTU3RjBEQThEMjU2N0EyQkUwMUNBNzQ5QkY0ODRDMTgyMjRGN0IzMEE4REI2MUJDQUI1NDMxMTZCNzkwOUM3OUMwIiwidG9rZW4iOiI5NTA3OTE3MzE1NTg5OTYzOTA0NjA0IiwidiI6IjEuMSJ9"
  },
  "messages": {
    "resultCode": "Ok",
    "message": [{
      "code": "I_WC_01",
      "text": "Successful."
    }]
  },
  "encryptedCardData": {
    "cardNumber": "XXXXXXXXXXXX1111",
    "expDate": "12/22",
    "bin": "411111"
  },
  "customerInformation": {
    "Ellen": "",
    "Johnson": ""
  }
}

include some of the fields I was struggling to get when doing the initial patch.

mglaman’s picture

Those are two different methods: Accept.js and Accept UI. We will be implementing the later as well.

subhojit777’s picture

As per the patch in #17 if there is any error in the payment, then instead of showing the error it throws more errors like this:

Notice: Undefined index: #parents in Drupal\Core\Form\FormState->getError() (line 1112 of core/lib/Drupal/Core/Form/FormState.php).

I tried adding a #parents to the credit card elements but it does not seem to work.

subhojit777’s picture

subhojit777’s picture

StatusFileSize
new28.16 KB
new904 bytes

This patch prevents error

Notice: Undefined index: #parents in Drupal\Core\Form\FormState->getError() (line 1112 of core/lib/Drupal/Core/Form/FormState.php).

showing up if any error occurs during payment.

subhojit777’s picture

High five to Shawn McCabe https://www.drupal.org/u/smccabe for helping me.

mglaman’s picture

+++ b/js/commmerce_authnet.form.js
@@ -0,0 +1,153 @@
+        cardData.cardNumber = $('#credit-card-number').val();
+        cardData.month = $('#expiration-month').val();
+        cardData.year = $('#expiration-year').val();
+        cardData.cardCode = $('#cvv').val();
+        secureData.cardData = cardData;

We need to clear these values on submit. Then I'll merge the patch.

A follow up issue is the fact Accept.js does not validate the card number, or it silently (or something) fails. I need to push forward #2790551: Implement a JS library for the credit card form. And until Commerce 2.2 is released, we can host a version of it in Auth.net to provide credit card validation.

smccabe’s picture

Status: Needs work » Needs review
StatusFileSize
new28.09 KB
new809 bytes

Added code to clear values on submit, as well as a little documentation explaining why.

czigor’s picture

Status: Needs review » Reviewed & tested by the community

Looks good from here.

mglaman’s picture

Status: Reviewed & tested by the community » Fixed

Committed, thanks everyone! Now for follow ups.

  • mglaman committed a2a808b on 8.x-1.x authored by nikathone
    Issue #2813401 by czigor, subhojit777, smccabe, mglaman, nikathone,...
adrian83’s picture

Thank you, everyone!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

vasantha raja’s picture

Our site is running on Drupal 8.5.4 and the Commerce version of 8.2.6.

The JS library from the authorized.net is loaded into the site. (For sandbox account).
<script src="//jstest.authorize.net/v1/Accept.js" charset="utf-8"></script>

But we are seeing the error in the console.
"E_WC_01: Please include Accept.js library from cdn".

Any assist in this will be really appreciated.

hockey2112’s picture

Same here: "Please include Accept.js library from cdn." Is it necessary to upload the Accept.js file to my libraries? If so, what is the proper directory structure? This module has no readme file, so this is a bit confusing.

adrian83’s picture

Not sure if this is a helpful answer, but here goes: I'm still running rc1, required via my project's composer.json. Here is a path to some accept code: /modules/contrib/commerce_authnet/js/commerce_authnet.accept.form.js. I may be wrong, but seems to me this is the only Accept.js code needed?