I am dynamically loading Accept.js only if it's needed during our checkout process. My initial approach was to just add the script to the <head> tag, listen for the script's load event, and then call Accept.dispatchData. Unfortunately, this results in E_WC_03: Accept.js is not loaded correctly.
Relevant code:
async function loadScript(source, charset) { let script = document.createElement('script'); script.setAttribute('src', source); if (charset) { script.setAttribute('charset', charset); } let onLoad = null; let onError = null; try { return await new Promise((resolve, reject) => { onLoad = e => resolve(e); onError = e => reject(e); script.addEventListener('load', onLoad); script.addEventListener('error', onError); document.head.appendChild(script); }); } finally { if (onLoad) { script.removeEventListener('load', onLoad); } if (onError) { script.removeEventListener('error', onError); } } } async loadAcceptApi() { if (typeof Accept != 'undefined') { return; } await loadScript('https://jstest.authorize.net/v1/Accept.js'); } async getPaymentNonce(paymentDetails) { await loadAcceptApi(); return await new Promise((resolve, reject) => { Accept.dispatchData(paymentDetails, response => { // ... }); } }
Digging into the source for Accept.js, I think I see why this results in an error:
I dug into AcceptCore.js and found a workaround. AcceptCore.js appears to emit a handshake event on the body element when it is loaded. Knowing this, I updated our code to wait for Accept.js to load and for the handshake event to fire before calling Accept.dispatchData.
Updated relevant code:
async loadAcceptApi() { if (typeof Accept != 'undefined') { return; } let onHandshake = null; try { // Accept.js loads AcceptCore.js, so we have to wait for the // "handshake" event that AcceptCore.js emits after loading. let handshake = new Promise(resolve => { onHandshake = () => resolve(); document.body.addEventListener('handshake', onHandshake); }); // Wait for script load and handshake. await Promise.all([loadScript('https://jstest.authorize.net/v1/Accept.js', 'utf-8'), handshake]); } finally { if (onHandshake) { document.body.removeEventListener('handshake', onHandshake); } } }
With the updated code, everything works as expected. We load Accept.js dynamically, wait for it to load, wait for AcceptCore.js to load via the handshake event and only then make our call through Accept.dispatchData.
Questions:
Regards,
Chris
โ06-03-2018 07:12 PM
Accept.js is designed to be loaded when page loads at the starting,
Following should be the sequence:-
In this flow it takes around 1-2 mins for a customer to fill payment form.
Can you pls explain What is your flow, as I see you load accept.js and just after that you call dispatchData.
I feel you should change the load time of Accept script.
Thanks,
โ06-13-2018 06:16 PM
Thanks for the reply. I understand and appreciate what you're saying, but the design feels flawed to me.
We have a Vue-based single page app. It would be wasteful and would increase our vulnerability surface area to load Accept.js on the initial page since it would load for all of our pages, even though it is only used on a couple of them.
This isn't a problem, of course, since we can load Accept.js dynamically on just the routes that use it. Ultimately, to keep all of the Authorize.net front-end code together, I decided to only load the library right when we need it (when the user checks out). I could move it to when the route is loaded to reduce latency on checkout, but regardless, there is no clean way of knowing for sure that Accept.js is loaded.
The current suggested way to load Accept.js is basically:
I understand that this works fine in the majority of situations, I just don't care for the "hope that Accept.js has loaded" step (e.g. What about slow connections? What about prefilled forms that the user submits quickly? What about refreshing and submitting immediately? What about this scenario of loading at the last second?). There should be a simple, deterministic way of knowing that the library is ready.
The ways I can think of knowing when it's loaded off the top of my head:
#1 doesn't work since Accept.js loads AcceptCore.js, which is the issue I ran into. #4 is hacky, inelegant, and wasteful. #2 and #3 seem fine to me.
As an example, the Google Maps API accepts a callback parameter that is executed when the library is finished loading.
I understand that "load early, wait a while" is good enough for most scenarios, I just expect a bit more from a payment handling library.
โ06-19-2018 08:04 PM
Thanks for your suggestion!
I hope you are able to solve the issue now by following currently supported integration way :-
Till now we found it works fine. we want to keep it simple and easy to integrate,
We are taking your feedback and will think about implementing a new event(optional), Script at merchant page can listen to this if needed.
Thanks!
โ06-22-2018 03:59 PM - edited โ06-22-2018 04:01 PM