Programmatically determine if Sagepay card details form is incomplete - opayo

I'm looking for some help with regards to the drop-in checkout integration.
https://developer-eu.elavon.com/docs/opayo/integrate-our-drop-checkout
In particular I'm following ‘custom flow’ example - see first code example for the Custom Flow section. https://developer-eu.elavon.com/docs/opayo/spec/api-reference#section/Custom-flow
In summary how can I programmatically determine if the sagepay 'card details' form which is injected into the sp-container (hosted in an iframe) is not valid for submission/tokenisation, when a call ‘tokenise’ link is used?
For example when the user has not completed all the required fields; Name, Card Number, Expiry or CVC.
I'm following the flow calling sagepayCheckout.tokenise() when the user clicks our 'Complete Payment' button (referred to as the tokenise link in the custom flow example).
NB: Implementation is part of a SPA app in my particular case I'm not submitting the (outer) form rather handling things using javascipt and calling the tokenise method.
Normally the onTokenise call back method is called on success or when wrong card details have been entered or merchantKey has expired. But when the injected sagepay form fails its own validity the onTokenise call back is never called.
NB: Due to browser security I can't check the state of the fields inside the iframe.
Ok some good points to note:
The sagepay form does feedback to the user about errors that required fields need to be completed.
And sagepayCheckout.tokenise() doesn't try to submit my (outer) form when these errors exist.
But there doesn't seem to be a programmatic way to know if the required fields need to be complete (or the onTokenise call back wont be called).
The main reason I need to know when the form is incompleted or tokenise has failed would be to re-enable our 'Complete Payment' button. Currently our button is disabled as soon as the user clicks it to prevent multiple clicks.

I am having this exact same problem. The only way I have been able to think of to get around this is to use the message event listener on the window. The sagepay.js library registers a 'message' event on the window object probably when the sagepayCheckout method is called.
Side note: this method is also poorly implemented as anything else that uses the window message event will cause the sagpay.js library to throw errors.
I think the only purpose of sagepay.js' message event hander is to resize the iframe window when its content changes. If you were to assume the content of the iframe will only change when validation fails you might be able use that to reset the state of your 'Complete Payment' button.
I would also suggest making a complaint to Opayo. This library is clunky and in desperate need of improvement.

Related

Custom View Clicks not being Recorded

When running either a Robo Script Recorder or Espresso Test Recorder, when clicking on a custom view, the click action is not being recorded. I've checked to make sure that the custom onTouchListener is calling performClick() when Action.UP is called. Is there something else that needs to be done as well to make sure the recorders pickup the click actions from custom views?
For those that might run into this issue in the future, if you have a custom OnTouchListener that is being assigned to the view, make sure that the listener doesn't consume the event (i.e. return true). It should always return false. Also if you call performClick() as part of the ACTION_UP, make sure you break out of the switch for this case and not let it fall through to the bottom if you have another action case to be handled after it.
If you consume the events, then you interrupt the PerformClick#run() flow for the view, and th Espresso Recorder and Robo Script recorders can not register the events.
This information came from the Firebase Slack channel.

How do I programmatically login through Auth0 Lock interface?

I'm trying to write a WebPageTest custom script that involves programmatically logging into my web app's Auth0 Lock interface as the first step but haven't been able to get it working.
I set up a private WebPageTest server to get a closer look at what was happening, and from the server logs it looks like the WebPageTest script is setting the value of the username/password fields and clicking submit using vanilla DOM manipulation (i.e. querySelector, click, etc.) but upon form submission, Auth0 Lock doesn't recognize that anything has been filled out in those fields. There's errors saying those fields can't be blank when submit is clicked.
I've used a local WebPageTest Node agent with my private server to successfully login through the Lock widget but don't know how to get performance logs using that approach (no results show after I get to the test results page). That login approach seems to work because the values going into the input fields get programmatically "typed in" through the WebDriver sendKeys function.
I came across this related post on Auth0 forums but don't know how I can incorporate what's being recommended there in the context of a WebPageTest script.
You can reproduce the problem I'm experiencing by going to the Auth0 Lock sample at the top of this page and running the following code in your devtools console:
document.querySelector('.auth0-lock-input[name=email]').value = 'hello#hello.com';
document.querySelector('.auth0-lock-input[name=password]').value = 'testing';
setTimeout(() => document.querySelector('.auth0-lock-submit').click(), 1000)
I expect to be able to programmatically enter input field info and submit it through the Auth0 Lock widget but haven't been able to do so. Does anyone have a solution to this?
The login page uses javascript/ajax to create the login form and its input elements. You're simply doing stuff too fast, not waiting for the elements to be created first, in order to populate and submit them. Just wait for the form and its input elements to become available/visible and then continue your login process.
Also, avoid using Sleep() / setTimeout() approach to tackle waitings. It's just wrong and it's a problem waiting to materialize itself, as soon as you change the environment in which your code is running. Use a proper waiting methods from your test framework and properly wait for those elements to become available.

Firefox Extension API - permissions.request may only be called from a user input handler?

I'm using the Firefox permissions API documented HERE
I'm having a problem with the request method, wherein all of my permissions requests result in:
Error: permissions.request may only be called from a user input handler
You can produce this in firefox by debugging any addon or extension and entering browser.permissions.request({origins: ["https://google.com/*"]}) into the console.
I find it hard to swallow that a permissions request must always have a user input event callback in the parent stack trace. I'm using Vue.js, and my Permissions are due to user interaction, but my user interactions are decoupled from the events they trigger.
What counts as a user input handler?
Why does it work like this?
Is there a good work-around?
Is there a good work-around"
I'd like to add onto Andrew's answer with some code examples.
As it turns out, promise chains destroy the browser's notion of what is and isn't triggered by a user input handler. Take the code below, for example:
document.getElementById('foo').addEventListener('click', event => {
browser.permissions.request({origins: ["https://google.com/*"]})
})
This code works as expected. I originally assumed that it was Vue.js's unique event handling framework that was eating my "browser events", such as when you do <div #click="somefunc"></div>. This actually works just fine, as long as you put your permissions request in somefunc.
Now it gets fun. If you replace your permissions request with a promise that resolves and then does a permissions request, VIOLA!
Promise.resolve('foobar').then(foobar => {
browser.permissions.request({origins: ["https://google.com/*"]})
})
Results in:
Error: permissions.request may only be called from a user input handler
Why does this happen?
I'm going to guess it has to do with stack traces. Firefox can't detect that a permission came from a stack with a user input event at the root if the permissions request happens in a promise chain.
I consider this to be a pretty egregious design choice. My app is large (>4K LoC) and to keep it simple I rely on promise chains to keep the spaghetti away. This has crippled my ability to write clean code, and as a result, I've moved from asking for optional_permissions and then prompting the user for permissions only when needed to just being overly permissive at the time of installation.
GG, Firefox.
What counts as a user input handler?
A DOM event handler that corresponds to user input (e.g., target.addEventHandler("click", ...) or a WebExtension event listener that corresponds to user input (e.g., browser.browserAction.onClicked.addListener(...)
Why does it work like this?
Partly for basic UX (if a user is not directly interacting with an extension and a prompt for the extension suddenly prompts up, it can easily confuse them), but also to avoid clickjacking attacks where the prompt is put up at a carefully chosen moment when the user is likely to be expecting some unrelated prompt.
Is there a good work-around?
I think just organizing your code so that you request permissions from a user input handler is probably your best bet.

executing portlet event phase without render phase

I need to cancel renderProcessing (doView method) from executing after processing Action or Event phase (As i don't want the whole page or any portlets to be refreshed). Something like ajax resource acquiring which is not leading to refresh all portlets (I mean serveResource method). Can we use "destroy()" method at the end of ProcessAction or ProcessEvent to prevent renderPhase from executing. I'm using MVCPortlet framework and events ipc extensively in my portlets. Thanks for your help.
As Georgy Gobozov stated in the comment: The answer to your question is "No".
If you are using the standard portlet request handling and rely on event handling, you're bound to a full page reload. There's nothing that keeps you from implementing custom event handling (e.g. with JS on the browser, through your business layer etc.) but unfortunately you'll have to do exactly this.
When you start the original request, e.g. through an action handler, the page has already started to reload (from the browser perspective). Any attempt to cancel the processing server side will result in the stream to break and the browser signalling an error on the page (e.g. "can't load": The result must come with an HTTP status - and it will most likely be an error code (e.g. 50x), or it must contain the whole page's HTML.

Stop back button during data pull?

Is it possible to stop the back button from working during a data call? For instance, when registering, I don't want someone to press the back button otherwise they may register for my service and not know it (other than confirmation email)? (And the registration will fail the next time they try)
Handle the BackKeyPress event or override the OnBackKeyPress method in your page class, and then set e.Handled = true; when you want to prevent backwards navigation.
Note that if you do this, then you should provide the user with a way to cancel your long-running process so they can back out if they want to.
Please note that if you stop the Back button from working your application will fail marketplace submission.
See section 5.2.4 Use of Back Button.
If a user has the situation where they try to reregister (becuase they don't realise they have registered previously) then you should handle this in your app as the situation may come up anyway.

Resources