The WooCommerce integrations for Authorize.Net we’ve built at SkyVerge are used by tens of thousands of merchants every year to process online payments.
Over the six-year timespan in which we’ve maintained these integrations, we’ve worked with several iterations of the Authorize.net APIs, from the query string-based legacy APIs to the latest version of the Accept suite. The latest Accept suite improvements have helped us unify legacy integrations and offer seamless, PCI compliant payment forms for our merchants.
We’ve recently adding support for Accept.js with UI to our integrations, and wanted to share our experience in implementing several different versions of the Accept suite, and what we learned in working with each Accept suite feature.
Migrating to Accept suite
Round One: Adding Accept.js support
We initially added support for Accept.js to our ANet AIM extension in July of 2016, and later ported the same implementation over to our CIM extension in September of the same year. In both extensions, we added a couple of settings to use the new feature:
- A toggle to enable Accept.js - this allowed existing customers to continue with their current setup, in case they hadn’t generated a Public Client Key
- Input for a Public Client Key
At this point, we opted not to make Accept.js available by default since it required additional action by the merchant to enable it for their account. However, that’s no longer the case — keep reading to see what we’ve done with our latest update. 🙂 If you’re looking to add Accept.js support, this step is no longer even necessary.
The bulk of the work at the time was on the client-side in generating the opaqueData for the payment nonce. Fortunately, this implementation was very smooth following the documentation provided by Authorize.net, so we were able to create a very simple flow for all merchants:
- If the merchant has not yet configured a Public Client Key, continue using the built-in form to send payment information server-side.
- Once the merchant sets the Client Key, adjust our transaction submission handlers:
- Get the opaqueData by sending the payment information via JS in the browser.
- Now use this opaqueData in the payment node for the request XML, rather than including card details (as you would otherwise when creating a transaction).
Since the rest of the transaction request was the same, this was a very easy change for us to make in a backwards-compatible way. If you haven’t implemented Accept.js at all, this is a very simple and quick win in helping to meet SAQ A-EP levels of PCI compliance.
If other code may be running alongside your implementation, be sure to add additional logging to help you identify the breakdown in process (for us, having detailed logs tends to help more than searching through console errors on some sites). Since there are now multiple steps for creating a transaction, it’s helpful to know which step experienced a breakdown to further troubleshoot issues.
So, we implemented AJAX logging to send the Accept.js request and response, minus any sensitive payment details, to the server to be logged in the same way as traditional API requests:
This helps our support team see where a breakdown happens when attempting a transaction: do we see issues before the opaqueData can be generated, between generating this and attempting to create a transaction, or is the issue while creating a transaction?
Round Two: Unifying our integrations
Until now, we’ve offered three distinct WooCommerce extensions for accepting payments through Authorize.Net:
- Authorize.net AIM - Accept payments directly or via Accept.js
- Authorize.net CIM - The same feature-set as AIM, but with the addition of card tokenization for saving cards using the CIM API. This integration also offers support for WooCommerce Subscriptions and Pre-Orders modules.
- Authorize.net SIM - Originally integrated with the SIM hosted (off-site) form, and later added support for Accept Hosted as an iframe payment form.
These extensions allowed merchants to choose a solution based on their needs: if they wanted a direct inline payment form on their checkout page, AIM or CIM did the trick, and if they also wanted card storage, then CIM it is. And if they wanted much-reduced PCI compliance scope, SIM / Accept Hosted allowed them to get up and running quickly with secure payment forms.
When we’d originally upgraded to the Accept suite, tokenizing cards wasn’t available with Accept Hosted, so users would need to choose between meeting SAQ-A compliance or leveraging saved payment profiles and inline payment forms. Once Accept Hosted added support for payment profiles, a choice still existed: use an iframed payment form or an inline form. There was no approach that let users have everything.
While there were historically tradeoffs between different Authorize.net APIs, this is no longer an issue! Using Accept.js with UI, we’re able to offer a solution that meets SAQ A levels of compliance, offers inline and seamless checkout, and supports using payment profiles.
So in January of this year, we set out to unify our different Authorize.net integrations into a single, complete solution. We can offer inline checkout for backwards-compatibility (which uses Accept.js) by default, or lightbox checkout, which uses Accept.js with UI.
The result is a single, complete solution for all of the options Authorize.net’s APIs and SDKs have to offer. This means a well-built plugin with focused support, and a single codebase for quicker feature addition and iteration. And because we allow merchants to set how they want their payment forms to behave, they have the same degree of choice as with the previous three extensions.
They can keep the form inline, with credit card fields generated by the site and allowing for greater control over the form’s appearance, or switch to the lightbox for enhanced PCI compliance. Either way, both forms result in the same opaque data so handling payments server-side remains the same.
If you’re upgrading an integration of your own, the lightbox checkout is definitely the way to go. While it requires a bit more handling to process the response of the lightbox form, it provides increased security and the request to create a transaction remains the same. In our integration, this was a pretty nominal change to make since we already supported Accept.js.
Small optimizations that make a big difference
We focusing on making upgrades to the Accept suite within our integrations, but while we’re making changes, we always look for additional improvements to the user experience. Here are some things we’ve done in conjunction with Accept Suite upgrades that have helped to improve the user experience within our integrations.
Support Authorize.net webhooks
We don’t support all webhooks available from Authorize.net yet. However, we knew that supporting at least a few webhooks would help with one of the major issues we see in our support desk: changes being made to customer profiles in the Authorize.Net account that are not synced back to the WooCommerce site.
Here’s where we see issues: let’s say a merchant deletes a payment profile in Authorize.net when assisting a customer via phone. Now, the local profile record in WooCommerce will be removed next time we try to do something with that customer to ensure the customer data is synced with Authorize.net. This usually poses no issues, except when something on the WooCommerce site (such as a subscription record, handled by the WooCommerce Subscriptions plugin) still expects the payment profile to exist.
This can be painful to troubleshoot because there are no logs of the payment profile being deleted since it happened off-site. However, now that we’ve added support for customer event webhooks, we can log the profile deletion on the site as well, and notify the user that action is needed to avoid billing disruptions.
Webhook support is extremely helpful to our support team already, and we’re looking to expand the webhooks we listen to in the future as well.
Detailed customer-facing messages
While adding Accept.js support, we also improved customer-facing messaging. Most of our payment integrations allow you to enable what we call Detailed Decline Messages, which present clear and actionable feedback to customers when something goes wrong and there’s something they can do about it.
So for example, if the credit card number they entered at checkout is malformed, rather than show them a generic “Something went wrong, please try again” message, we can tell them that the card number is the problem.
Authorize.net’s documentation helpfully provides a list of error codes that may be encountered, so that lets us listen for specific codes and display a nicely formatted message to customers to help them resolve payment problems. If you’re already adjusting your payment form handling to add support for the Accept suite, why not improve customer error messaging at the same time?
Streamlining your integration
There have been many improvements to the Accept suite since our initial implementation, and we’re working to improve our use of those tools even further.
One of the biggest pain points we’ve seen from our support conversations is confusion around the extra steps to get Accept.js enabled and working on a merchant’s site. We always aim to reduce the amount of configuration required, especially when it involves copy and pasting API credentials.
We can’t avoid the first steps of the merchant setting their account’s API Login and Transaction Key (OAuth isn’t a viable option for open source software, as we can’t expose our Client Key and Secret in the source code), but recently we’ve been able to eliminate the step of configuring an additional Public Client Key to enable Accept.js.
The Authorize.net API allows us to retrieve the client key using a getMerchantDetails call. So once an initial connection is confirmed, we can use the client key right away. Taking it even further, the API will now generate a Client Key if one doesn’t already exist. This eliminates the need for the merchant to generate one themselves, further enhancing their experience and allowing us to use Accept.js by default for all merchants.
Handling the client key in this way allowed us to remove the original direct server-side handling of payment details altogether, making PCI-DSS SAQ A-EP compliance the standard out of the box for all merchants who use our plugin.
So, when upgrading your own integration, you can make this transition completely seamless! In our upgrade, we’ll now set the “inline” form as the default so the merchant sees no changes upon upgrade, and we transparently generate the client key if not set so every merchant now uses Accept.js.
Since we’re already making a call to generate the public client key, we can also use the getMerchantDetails call to detect problems right away on the gateway configuration page in WooCommerce. If the values the merchant sets for Login or Transaction Key aren’t valid, we can display an error message to them so they know to double-check these values. Or, if for some reason a Public Client Key is not available we can let them know about that as well. All of this helps reduce the confusion a merchant may have if they try and configure the extension but aren’t able to accept payments.
There are still features we want to add to enhance the Authorize.net experience on WooCommerce even further!
We already support a couple of the webhooks that are offered to listen for important events such as the removal of a customer profile. In the future we hope to take this further by adding support for payments events so that WooCommerce orders can update themselves if a payment is captured from the merchant account, for instance.
There are also things that are not yet supported, but we hope to see added to the set of tools that Authorize.Net provides. For instance, currently the Accept.js hosted / lightbox payment form only supports a credit card form, but we’d loved to see eChecks added as an option so our eCheck gateway can have the same checkout page experience.
Hopefully, this inspires you to upgrade your own credit card integrations to the Accept.js with UI offering—minimal changes are needed since transaction creation is largely unchanged, and you can generate a client key for every merchant transparently, so even existing users can use Accept.js without requiring any action upon upgrade.