Using the Accept.js library and methodology alone results in a SAQ A-EP level of compliance. While a step in the right direction, Authnet offers something called the Accept.js UI, which would integrate with the existing work already done to enable this module to use the Accept.js library. The primary difference is that this module would no longer be responsible for building and presenting the Card Information form elements to the user. Instead, the user clicks a "Pay" button, and a lightbox appears where the user enters ONLY their card number, expiration date, and security code. This lightbox is generated and displayed entirely by Authorize.net and counts as an entirely separate page, ensuring the sensitive card data never touches the merchant's server. This would result in SAQ A compliance.
Once the client does this, Authorize.net returns a payment nonce to the site, which is then submitted to Authnet with the rest of the payment information just like in the existing Accept.js framework. The end-user never feels like they never left the merchant's website, and PCI compliance overhead is reduced significantly. Yes, this would reduce the ability of Drupal to perform certain functions like allow transactions from the admin interface, and it may not completely work with Card on File, but for many merchants? These sacrifices pale in comparison to the benefit of the reduction to PCI compliance level SAQ-A.
It's easy for me, a non-developer, to say that implementation would simply be a matter of creating a "drop-in replacement" for the card information form, but I'm sure there's a lot more work to it than that. Still though, the heavy lifting has already been done as the Accept.js framework is already committed as the rest of the callbacks are largely similar.
| Comment | File | Size | Author |
|---|---|---|---|
| #51 | commerce_authnet-acceptjs_ui-3019539-51.patch | 46.39 KB | tkiehne |
| #47 | commerce_authnet-acceptjs_ui-3019539-47.patch | 71.67 KB | tkiehne |
| #44 | commerce_authnet-acceptjs_ui-3019539-44.patch | 69.71 KB | tkiehne |
| #41 | commerce_authnet-acceptjs_ui-3019539-41.patch | 91.03 KB | tkiehne |
| #39 | commerce_authnet-acceptjs_ui-3019539-39.patch | 94.4 KB | tkiehne |
Issue fork commerce_authnet-3019539
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
morbus iffComment #3
dscl commentedI've been working on a patch for adding Accept.js UI to this module and it is looking good so far.
I'm assigning this issue to me and I hope to get a working patch in the next week or two.
Comment #4
dscl commentedHey there,
It did take a bit longer than I expected, especially I had to update a lot of stuff after the latest release of the module, but here it is.
I've tried my best to follow the same naming concepts and re-use the code you already had for Accept.js.
As part of the effort of reusing code, I've moved a lot of code into a new class called
AcceptJsBasefrom whichAcceptJsandAcceptJsUiinherit everything that was common between them.This change made this patch much bigger, but I believe it was necessary.
Looking forward to your reviews. :)
Cheers
Comment #5
ericchew commentedIn the PaymentMethodAddForm PluginForm, there needs to be validation for checking whether a payment method was added. If you try to add a payment method via user/{user}/payment-methods/add and submit the form without adding a card, the form crashes.
Proposed Fix:
I'm headed to lunch...will turn it into a patch afterwards.
Comment #6
ericchew commentedHere is the patch that incorporates #5.
Comment #7
ericchew commentedAcceptJsBase should be abstract.
Comment #8
ericchew commentedI tested adding card through the user pages and via the admin order payment area, seems to work really well. I did both test and live credentials. I have not tested cart/checkout as it is not implemented on our site, yet. I would say RTBC once someone confirms it looks good in cart/checkout.
Comment #9
mglaman@ericchew thank for your being able to test this! I've been a bit busy and I really appreciate the assistance in review and testing. I'm going to give it a quick code eyeball.
Comment #10
mglamanHere's a round of code adjustments. Nice work thus far!
We should move this outside of the attach and just within the scoped function.
Like right after 'use strict'.
technically these could just be functions within our scope, since we're not using behaviors per the above.
Not really a big deal, but something we could do to prevent adding stuff to Drupal.behaviors when we don't use any behavior methods.
😬 we need to use Drupal.t here for translations
Overridden without doing anything.
This interface name should stay the same.
If AcceptUI has specific methods, it can extend this one?
Are we manually building JSON when we could encode it? before setting it on the data attribute?
Comment #11
ericchew commentedHere's a patch for the comments in #10
Comment #12
ericchew commentedComment #13
tonytheferg commentedAny chance this could get back ported to 7.x? 😆
Comment #14
Ted Milker commented@ericchew, et al There's an issue with the patch in #11. If you click "Continue to Review" before clicking the button for the AcceptJS UI pop-up for credit card info, then you get an error and a hard stop from Drupal:
InvalidArgumentException: $payment_details must contain the data_descriptor key. in Drupal\commerce_authnet\Plugin\Commerce\PaymentGateway\AcceptJsBase->createPaymentMethod() (line 340 of .../web/modules/contrib/commerce_authnet/src/Plugin/Commerce/PaymentGateway/AcceptJsBase.php).Instead, it should direct the user to click the button to fill out their credit card info before clicking Continue to Review.
Also, how about an option to not save credit card info/a customer profile in the gateway?
Comment #15
brightboldSounds like #14 meant to set this to Needs Work.
Comment #16
Ted Milker commentedAnother issue with the patch in #11 is that if you add a coupon and it fails(probably if it succeeds too) and you click the "Enter Credit Card" button, it doesn't trigger the Accept.JS pop-up, instead it triggers the form validation.
Comment #17
Ted Milker commentedYet another issue with the patch in #11, it doesn't work with Authorize.Net's Enhanced Card Code Verification Handling Filter enabled because there's no prompt for the CCV number.
I really wish this patch just used the mode of Accept.JS where it takes in the credit card info, generates a token in javascript, then uses that token to authorize the transaction at checkout instead of all this CIM storage stuff that doesn't function right.
Comment #18
morbus iffFundamentally, it *can't* do that, because that's already what Accept.js does. To be SAQ A complaint, Accept.js UI requires more complex interactions between server/client, and that includes the inter-modal thingy (literally the "UI" of Accept.js UI). If you think, however, that that's bupkis, certainly and please fork the patch and show us The Right Way While Also Being SAQ A complaint.
Comment #19
Ted Milker commentedThe built-in Accept.Js functionality of Drupal Commerce Authorize.Net doesn't appear to use the Authorize.Net hosted payment form from Accept.Js(Option 2 in the documentation* and what I'm describing) that is SAQ A compliant. It appears to use an inline form which is a SAQ A-EP compliance that I definitely don't want to try to meet.
The #11 patch uses a hybrid of Option 2 Accept.Js AND storage of the credit card authorization in the Authorize.Net CIM and the token in the Drupal user's payment info. I don't want that either. I just want the simple Option 2 that hosts the payment form on Authorize.Net and then uses the payment nonce to submit the transaction.
I would absolutely love to write this functionality myself but I have to do a lot more learning about Drupal Commerce and this patch to even get started, plus have the time to do it. Hopefully I get that time soon.
* https://developer.authorize.net/api/reference/features/acceptjs.html
Comment #20
Ted Milker commentedAnother bug with patch #11: If you delete a user's payment info in Drupal and remove their profile from Authorize.Net's Customer Information Manager, that user will no longer be able to add payment info with Accept.Js UI. It always fails with a "Received response with code Error from Authorize.net: E00040: The record cannot be found." However, if you add a new CIM profile with the Customer ID set to the Drupal user ID, they can add their payment info again *and* it creates another profile entry in the Authorize.Net CIM with the same Profile ID and the new CC Payment Profile.
Seems like a check is happening before a CIM profile exists or maybe there's some internal data that's not exposed relating to CIM profiles.
Comment #21
morbus iffNew patch is attached, which fixes #14.
Comment #22
morbus iff#17 also appears fixed (or, at least, I'm seeing requests for CVV in my test account).
Comment #23
morbus iffAttempting to fix bad patch for testbot.
Comment #24
sah62 commentedFor what it's worth, the patch in #23 appears to conflict with patch #24 from #3154908: E00003 The paymentProfileId element is invalid. When I try to apply them both whichever one is listed second fails to apply.
Comment #25
rhovlandWill this be an option in the payment gateway config or completely replacing the existing implementation? Having this as an option would be a win for merchants that need it but some of us would prefer the AcceptJS implementation over the AcceptJS UI implementation.
Comment #26
morbus iff@rhovland: the existing AcceptJS approach remains.
Comment #27
morbus iffUpdating patch against current dev.
Comment #29
vipin.j commentedThanks for this AcceptjsUI patch.
I'm attaching the revised patch based on #27
As we were facing following issues with the provided patch, and fixed the same:
Case #1: (this happens when both Shipping information and Payment information blocks configured on same Checkout flows page)
We encountered an unexpected error processing your payment method. Please try again later.$payment_details must contain the data_descriptor key.Case #2:
This patch is updated against master.
Hopefully this patch will help anyone who was facing the same issues.
Comment #30
kedarjimage commentedThis patch is not working with new version of commerce_authnet
Comment #31
vipin.j commentedUpdated patch #29 for 8.x-1.7 release.
Comment #32
brightboldThe issue mentioned in #14 is still occurring:
Other than that the patch seems to be working very well.
Comment #33
tkiehne commentedI applied this patch and it seems to work splendidly, but I have discovered a possible lapse.
In the event that Authnet holds a transaction for authorization (which can happen when triggering a fraud filter), the transaction is shown as needing review and shows the Approve/Decline operations; unfortunately these operations fail:
The AcceptJs gateway plugin defines approve-payment and decline-payment forms, but AcceptJsUI does not. Simply copying the form declarations from one to the other works, but I wanted to see if there was any reason for these not being included initially before proposing a patch.
Comment #34
morbus iff@tkiehne: There was no reason for them not being included. Likely an oversight. Patch away. Can they be moved to AcceptJsBase.php to reduce duplication?
Comment #35
tkiehne commentedUnfortunately the forms are defined in the gateway annotation, so it can't be consolidated into the base class.
There is also a question of organization: the subject forms are Drupal\commerce_authnet\PluginForm\AcceptJs\PaymentApproveForm and Drupal\commerce_authnet\PluginForm\AcceptJs\PaymentDeclineForm - While these work just fine for both AcceptJs and AcceptJsUi would we want to create a copy of each in src/PluginForm/AcceptJsUi just to keep things separate and futureproof? Or maybe refactor those to be in src/PluginForm and make them common to both gateways?
Comment #36
tkiehne commentedI've found one more issue with this patch. When a customer is selecting a payment method during checkout, there is a script for the UI patch that attempts to reload the AcceptJS-UI script after an AJAX action. The script checks for a specific context id set to a presumed default checkout flow, e.g., in commerce_authnet.acceptjs_ui.form.js:
if (context.id == 'commerce-checkout-flow-multistep-default' && document.getElementsByClassName('button AcceptUI').length > 0)This is all fine and good if you happen to be using this presumed default checkout flow, or if the Authnet payment method is selected by default and the user doesn't select a different payment method, but in our case we have custom checkout flows so this check is never triggered, meaning the AcceptJS-UI script is not reloaded when the user changes the payment method and the "Add payment information" button doesn't have events attached. Clicking the button will reload the screen and display an error. Ironically, since the Authnet payment method is preselected on the reload, the button will work correctly on second press.
Not sure what the exact fix is, but we shouldn't be presuming a fixed checkout flow id here. Perhaps just check that the context id starts with "commerce-checkout-flow-"?
Comment #37
tkiehne commentedI've re-rolled the patch in #31 against 8.x-1.8 and added code to address #33 & #36. This checks out against my environments, but it would be nice to have additional verification.
Seems like #32 is the only remaining outstanding issue with this patch. What will it take to get this feature on the release roadmap?
Comment #38
vipin.j commentedI've updated patch #37 in order to fix a bug with this patch.
to reproduce:
The old patch overrides Commerce Payment's default expected expiry year storage format i.e. YYYY to YY. so, the payment methods created by Authorize.net (Accept.js UI) gateway will store year in YY format instead of YYYY. This because the Accept.js UI form allows YY for the expiration year, and that value followed in the whole processing of validation and storing to database.
Comment #39
tkiehne commentedRe-rolled patch #38 against 8.x-1.10. Also added some modifications to the UI js to disable the pane submit form if no payment info has been added to address #32; I've had this feature in my production site for over a month with no issues, but I have a fairly standard checkout flow so caveat emptor if your checkout flow is highly customized. I also added a few coding standards fixes.
(edit: Tests are failing in PHP 8 due to https://www.drupal.org/project/commerce_authnet/issues/3319388)
Comment #40
tkiehne commentedOnly remaining issue that I can see is that in the new AcceptJsBase class, the createPaymentMethod() method has code to handle CCA logic which calls a method $this->getCcaApiKey() which was left behind in the AcceptJs plugin. This isn't likely an issue since the AcceptJsUi plugin will not trigger that code, but I figure this ought to be refactored to have the AcceptJs plugin override createPaymentMethod() to add this logic before calling the Base method. But, I don't use the CCA feature and would appreciate some confirmation before taking that step from someone in the know.
Otherwise, I've been using this patch in its various states in a production environment for almost 18 months with no issues - would love to get some RTBC focus here and move this into the main branch.
Comment #41
tkiehne commentedRe-rolled #39 to apply to 8.x-1.11
Issue in #40 still unresolved
Comment #42
tkiehne commentedThere's some movement in a similar direction in #3531056; haven't assessed the overlap yet
Comment #43
tkiehne commented#3531056 was merged and released with 8.x-1.12 in addition to some other significant changes including removing Cardinal Cruise/3DS and creating a new service to consolidate some tasks. Therefore, the patch in 41 will not apply. The good news, though, is that there may be some efficiencies created with the new release that will make things a bit cleaner.
I will explore a re-roll
Comment #44
tkiehne commentedPatch for 8.x-1.12
Comment #45
vmarchuk@tkiehne
I don't see a difference between Accept JS UI and Accept Hosted, as they are both part of the Accept Suite.
Anyway, if you and others are interested in making this part of commerce_authnet, we can make it happen.
After a quick look at this patch https://www.drupal.org/files/issues/2025-11-21/commerce_authnet-acceptjs..., we don't need to touch the AcceptJs payment gateway, but instead we need to create a new payment gateway plugin for the Accept JS UI (this is what we usually do for new payment gateways).
Also, please create an MR for the 2.x-dev branch so we can leave comments and discuss there.
Comment #46
tkiehne commentedHi @vmarchuk
Yes, it seems we have the same use case with different implementations. As you can see, this issue is quite old, and when I needed an SAQ-A solution several years ago this was the only option. I provided patch 44 as a bridge to continue this solution as originally developed as a bridge while assessing the new Hosted form feature.
While there was once a high degree of interest in this thread, I think I may be one of the few to still be using this solution so it may be abandoned unless others can chime in.
Comment #47
tkiehne commentedReroll versus 8.x-1.13 while we await strategy
Comment #48
damienmckennaFWIW I've found that the iframe is inconsistent in how it loads on page, most specifically sometimes it doesn't load with the correct height, so the "pay" button isn't visible, or even the recaptcha isn't visible:
Comment #49
brightbold@tkiehne
Weighing in that I'm using this patch still and I appreciate all the work you've put into it.
Comment #50
morbus iff@tkiehne: we require SAQ A compliance too and are still running an older version of this patch.
Comment #51
tkiehne commentedRe-rolled the patch to implement advice from @vmarchuk to keep the gateway plugins separate; not very DRY but will apply cleaner than refactoring the AcceptJs gateway. Tested with 1.14 but should work for other recent commerce_authnet releases.
Since it looks like there's still interest in this gateway I will look at opening an MR in 2.x.
(as an aside, I still need to assess the new Hosted gateway, mostly in terms for the implications of coming off the AcceptJsUi approach, especially concerning saved payment methods and user experience in changing gateways)