tags:

views:

4181

answers:

10

Hi,
I'm working on a PHP application that links into the Protx VSP Direct payment gateway. To handle "3D Secure" requests from the credit card processing company, I need to forward the user to a different website, mimicking a form that has been posted. I'm trying to use the cURL libraries, but seem to have hit a problem. My code is the following:

<?php  
$ch = curl_init();  
// Set the URL  
curl_setopt($ch, CURLOPT_URL, 'http://www.google.com/');  
// Perform a POST  
curl_setopt($ch, CURLOPT_POST, 1);  
// If not set, curl prints output to the browser  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);   
// Set the "form fields"  
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);  
$output = curl_exec($ch);  
curl_close($ch);  
?>

All this does is grab the content of the URL passed through, and doesn't forward the user anywhere. I've tried Googling and reading up as much as I can, but can't figure out what i'm missing. Any ideas? I don't want to have to create a HTML form that auto-submits itself if I can avoid it.

Thanks for any help :-)

+1  A: 

The 3D Secure API doesn't allow you to do the request in the background. You need to forward the user to the 3D secure site. Use javascript to automatically submit your form. Here's what our provider suggests:

<html> 
<head> 
<title>Processing your request...</title> 
</head> 
<body OnLoad="OnLoadEvent();"> 
<form name="downloadForm" action="<%=RedirURL%>" method="POST"> 
<noscript> 
<br> 
<br> 
<div align="center"> 
<h1>Processing your 3-D Secure Transaction</h1> 
<h2>JavaScript is currently disabled or is not supported by your browser.</h2><BR> 
<h3>Please click Submit to continue the processing of your 3-D Secure transaction.</h3><BR> 
<input type="submit" value="Submit"> 
</div> 
</noscript> 
<input type="hidden" name="PaReq" value="<%=PAREQ%>"> 
<input type="hidden" name="MD" value="<%=TransactionID%>"> 
<input type="hidden" name="TermUrl" value="<%=TermUrl%>"> 
</form> 
<SCRIPT LANGUAGE="Javascript"> 
<!-- 
function OnLoadEvent() { 
 document.downloadForm.submit(); 
} 
//--> 
</SCRIPT> 
</body> 
</html>
neu242
A: 

I think you are a bit confused as to what curl does. It does exactly what you explained, it acts sort of like a browser and makes the call to the site and returns the content of that post. I don't know of any way you can actually redirect the browser server side and represent a post. I would actually create a Javascript solution to do such a thing.

stephenbayer
A: 

To redirect a user to another page in PHP you can send a header("Location: http://example.com/newpage");. However, unfortunately for your program redirection cause all POST variables to be removed (for security reasons). If you want to cause the user's browser to send a POST request to a different URL, you would have to create a form that submits itself. :(

Jeremy Banks
A: 

I'd much rather use something behind the scenes like cURL, as I can't guarantee my users will have JS enabled, and displaying a form causes some other problems I'd rather avoid. It's my plan B though ;-)

fistameeny
You cannot do 3D secure "behind the scenes", since the user needs to be forwarded. The solution I outlined above is the best way to do it, at least that's what our payment provider thinks.
neu242
A: 

You can use fsockopen() to redirect to the new web site while preserving your POST variables. There is a good tutorial on how to accomplish this here.

In case that web site gets eaten by the Internet monster, I've copied and pasted the function but I suggest checking out the original web site for the context.

function sendToHost($host,$method,$path,$data,$useragent=0)
{
    // Supply a default method of GET if the one passed was empty
    if (empty($method)) {
        $method = 'GET';
    }
    $method = strtoupper($method);
    $fp = fsockopen($host, 80);
    if ($method == 'GET') {
        $path .= '?' . $data;
    }
    fputs($fp, "$method $path HTTP/1.1\r\n");
    fputs($fp, "Host: $host\r\n");
    fputs($fp,"Content-type: application/x-www-form- urlencoded\r\n");
    fputs($fp, "Content-length: " . strlen($data) . "\r\n");
    if ($useragent) {
        fputs($fp, "User-Agent: MSIE\r\n");
    }
    fputs($fp, "Connection: close\r\n\r\n");
    if ($method == 'POST') {
        fputs($fp, $data);
    }

    while (!feof($fp)) {
        $buf .= fgets($fp,128);
    }
    fclose($fp);
    return $buf;
}
Michael Ridley
A: 

Ah, if I've misunderstood what cURL does, I guess i'll settle for a HTML form with JS auto-submit. Creates quite a bit more work for me, but if it's the only way, i'll have to do it.

Thanks for the help all.

fistameeny
A: 

I think in this case you need to use a form. The card payment companies web site needs to be visited by the user's browser, not your server-side code.

Yes, 3dsecure etc, are quite annoying to integrate with, but they do provide a real security boost - use it as it's intended.

MarkR
A: 

Maybe I'm missing something; it looks like you're trying to receive the user's data, forward it via cURL to the payment processor, and then redirect the user somewhere. Right?

If so, all you need to do is just put this at the end of your code:

header("Location: http://www.yoursite.com/yoururl");

However, for this to work, the script CANNOT send anything to the client while it is processing; that means that the script should not be executed in the middle of a template, and another gotcha is whitespace at the beginning of the file before the opening tag.

If you need to save the data from the original POST transaction, use a session to save it.

That should work for a JS-free solution.

Nathan Strong
I can't use a session to store data, as the data needs to be send to another website, and the user has to be shown a form, enter text etc. Thinking about it, another form prompting the user to carry on is the best option as at least they are aware of what's happening.
fistameeny
A: 

I may be utterly wrong but it sounds like you're trying to do something similar to the Proxy Pattern.

I have implemented similar patterns for other sites I've worked with and it does require a bit of tinkering around to get right.

I'd set the RETURN_TRANSFER to TRUE so that you can analyse the retrieved response and make application level actions depending on it.

http://en.wikipedia.org/wiki/Proxy_pattern

Good luck.

flungabunga
A: 

I found the sendToHost() function above very useful, but there is a typo in the source that took some time to debug: the Content-type header value contains a space between 'form-' and 'urlencoded' - should be 'application/x-www-form-urlencoded'

Chip Kaye