SagePay: Inconsistency in IFrame 'Low profile' option on Server solution - ** Workaround **

I've recently been integrating the SagePay Server solution into a client site and it was decided to utilise the new 'Low Profile' option (introduced in protocol 2.23). This new option allows the SagePay payment pages to appear within an Iframe placed inside the main site template. The obvious benefit of this is that the customer is not redirected to another site to complete payment and confidence in the vendor is improved.

The integration went pretty well with the SagePay XSLT templating system being relatively easy to use and customise. I soon had a template which nicely matched the style of the vendor's website.

During testing everything worked great. The payment form appeared within the IFrame, the card details could be entered and the 3D Secure page worked as expected. After the payment side had been handled SagePay redirect back to the vendor website outside of the IFrame - just what we need.

However, during testing I tested an AMEX card. AMEX cards are not part of the 3D Secure system and so the customer is not shown a 3D Secure page - just the card input form. Now this shouldn't really be a problem but when the card details have been entered and validated, SagePay redirect the customer back to the vendor site but inside the IFrame!

This is definetely NOT what we want as once redirected to our payment processing template the full site design is shown within the IFrame (header, sidebar and footer).

At this point I hit the HTTP debugger to find out what was causing this inconsistency. Here is what I found:

When 3DS authentication is required, the following HTTP requests are triggered:

https://test.sagepay.com/gateway/service/carddetails                302         POST     test.sagepay.com                /gateway/service/carddetails   
https://test.sagepay.com/gateway/service/cardconfirmation    302         GET        test.sagepay.com                /gateway/service/cardconfirmation       
https://test.sagepay.com/gateway/service/authentication         200         GET        test.sagepay.com                /gateway/service/authentication            
https://test.sagepay.com/mpitools/accesscontroler?action=pareq         200         POST     test.sagepay.com                /mpitools/accesscontroler?action=pareq                            
https://test.sagepay.com/mpitools/accesscontroler?action=auth            200         POST     test.sagepay.com                /mpitools/accesscontroler?action=auth               
https://test.sagepay.com/gateway/service/authentication?action=callback        200         POST     test.sagepay.com                /gateway/service/authentication?action=callback           
https://test.sagepay.com/gateway/service/authentication?action=completion                302         POST                test.sagepay.com            /gateway/service/authentication?action=completion
REDIRECT
http://dev.jamesallen.name/index.cfm/page/orders.order.cfm             200         GET                dev.jamesallen.name /index.cfm/page/orders.order.cfm

In this case, the final SagePay request is a POST (assumng it uses target="_top" which jumps the customer out of the Iframe)
The redirect back to the site is thus outside of the Iframe and all is good.


If 3DS authentication is NOT required the following HTTP requests are triggered:

https://test.sagepay.com/gateway/service/cardselection?vpstxid={283FE985-CE13-A510-B9E6-840531*****} 302                GET        test.sagepay.com            /gateway/service/cardselection?vpstxid={283FE985-CE13-A510-B9E6-840531*****}
https://test.sagepay.com/gateway/service/carddetails                200         GET        test.sagepay.com                /gateway/service/carddetails   
https://test.sagepay.com/gateway/service/carddetails                302         POST     test.sagepay.com                /gateway/service/carddetails   
https://test.sagepay.com/gateway/service/cardconfirmation    302         GET        test.sagepay.com                /gateway/service/cardconfirmation       
https://test.sagepay.com/gateway/service/authentication         302         GET        test.sagepay.com                /gateway/service/authentication
REDIRECT            
http://dev.jamesallen.name/index.cfm/page/orders.order.cfm             200         GET                dev.jamesallen.name/index.cfm/page/orders.order.cfm

In this case the last request inside SagePay is a GET which means the customer is left in the Iframe.

So there is a fundamental inconsistency in the implementation of the new 'Low Profile' option in the SagePay Server product. So, how do we solve this?

After some head scratching I came up with a solution which I feel is fairly straight forward and will ensure that when SagePay fix the problem, the site will not suddenly change it's behaviour and will be easy to modify to remove the Javascript redirect.

The solution is as follows:

  1. In your notification template (the one that SagePay POSTS back to and you respond with Status, RedirectURL, StatusDetail), set the RedirectURL to a new handler template (e.g http://site.com/orders/go.cfm). Then set a session variable to the URL you want the customer redirected to to continue the payment process.

  2. Create the handler template. This will build a dynamic form whose target is set to the redirect URL set in session in the step above. Javascript will be used to automatically submit the form when the page is loaded. An HTML submit button will be created inside the form in case the user does not have Javascript enabled.

That's the solution in a nutshell. We are modifying our notification template to always redirect to a new handler template which will jump the customer out of the Iframe and direct them to the correct template that continues the order process. The form target on the handler template is set to "_top" which causes the form submission to break out of the IFrame.

To help with this below is the code I use on my handler template (go.cfm). I use JQuery for the automatic form submission and to hide the submit button. You could of course code that functionality without JQuery.

<!--- URL to redirect to (set in session before the jump to here) --->
<cfset targetURL = session.targetURL>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <link rel="stylesheet" href="/styles/styles.css" type="text/css" />
   
    <title>Payment Processor</title>
   
    <script language="javascript" src="/functions/jquery.js"></script>
    
    <script language="javascript">
        $(document).ready(function() {
            $("#jump").hide();
            $("#jump").submit();
        });
    </script>
</head>

<body>
    <cfoutput>
        <form id="jump" action="#targetURL#" target="_top">
            <input type="submit" value="Click to continue your order">
        </form>   
    </cfoutput>
</body>
</html>

With this approach, once SagePay fix the inconsitency I can simply change this template to perform a server redirect to the required URL instead of using the Javascript and self submitting form.
Either that, or to change the notification template so that it goes directly to the required URL's as before.

Hopefully this will help some people who have got this problem when implementing the Server solution.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
James Mulvany's Gravatar Great post, we're just in the process of integrating SagePay and are having exactly the same problem.

We've used your work around method, agreed, its not ideal and I have emailed sage about this - but its not half bad!

Cheers!
# Posted By James Mulvany | 6/30/09 12:42 PM
James Allen's Gravatar Hey there James,

Awesome! Glad this helped you out. I didn't know if this solution would help many people or if anyone would encounter the issue. Great to hear that you were able to make use of it.

I know when I stumbled across this it stumped me to start with as everything was all tied into the payment process logic. My first solution was ultra complex but then when I stepped back I realised there was a much simpler way around it.

Sage are aware of the issue but it's not a high priority to fix at the moment I don't think as they are working on their core infrastructure.
# Posted By James Allen | 6/30/09 12:49 PM
Geoff's Gravatar Hi, I am trying to integrate sagepay using this method but can't find any documentation how to do it. Would you be able to help me out and point me in the right direction at all please? All the accounts and everything are set up I just don't know what URL to put as the src of my iframe if it is as simple as that?
# Posted By Geoff | 9/13/11 7:29 PM
James Allen's Gravatar Hey there Geoff,

Sorry for the delay - I only just saw I had a comment to approve.

Unfortunately integrating SagePay server requires *alot* more than just pointing at a URL. Unless you are using the 'Form' method, not the 'Server' method?

The server method requires a properly setup HTTPS API call to SagePay and then creating an iFrame based upon a URL they return in the API reply. There are a number of other things you need to setup, like a notification URL to handle the postback from SagePay once the customer has successfully paid.

All the documentation is on the SagePay site, but you'll need to sign up as a developer (it's free I believe):

http://www.sagepay.com/developers/sign_up
# Posted By James Allen | 9/27/11 5:44 PM
Heloise's Gravatar Hi
Could you tell me where do i need to put the inframe?
# Posted By Heloise | 2/3/12 10:33 AM
James Allen's Gravatar Hi Heloise,

You need to create the iFrame on your payment page where you want the customer to see the SagePay payment form. You first issue your HTTPS request to SagePay to register the transaction and they will return a URL that you point the iframe src="" to.
# Posted By James Allen | 2/17/12 1:05 PM
Anuj Gakhar's Gravatar Hi James, do you happen to have the code for this integration published somewhere? I notice SAgePay haven't provided a ColdFusion Integration Kit. I know it's a task that involves going through their documentation and then coding based on that, but I was wondering if there is something out there already. Let me know if this code is published somewhere. Cheers.
# Posted By Anuj Gakhar | 3/5/12 11:18 AM
James Allen's Gravatar Hey there Anuj,

I'm afraid I don't have anything published on this at the moment.
# Posted By James Allen | 3/5/12 12:48 PM
© 2014 James Allen | Contact Me
This blog runs on the awesome power of BlogCFC - created by Raymond Camden. This blog is running version 5.9.