ModPay付款网关HowTo

UPDATE: I've narrowed it down to the Ajax call. I can use a normal form post submission, still using the same PHP code and it works. The Ajax does not. So if anyone familiar with Ajax has an idea... Or if you have a better idea of how to do a POST from code without user interaction...

UPDATE 2: If I replace the GetPaymentSetup() function with the below code, it also works, using the PostTo. It seems to be something with the redirection...

    public function GetPaymentSetup() {
        $script1 = "<script type=\"text/javascript\">
            function post_to_url() {
                var form = document.createElement('form');
                form.action = '" . self::PROXY_URL . "';
                form.method = 'POST';

                var input = document.createElement('input');
                input.type = 'hidden';
                input.name = 'clientId';
                input.value = '" . self::CLIENTID . "';
                document.body.appendChild(input);
                form.appendChild(input);

                var input1 = document.createElement('input');
                input1.type = 'hidden';
                input1.name = 'clientCode';
                input1.value = '" . self::CLIENTCODE . "';
                document.body.appendChild(input1);
                form.appendChild(input1);

                var input2 = document.createElement('input');
                input2.type = 'hidden';
                input2.name = 'postTo';
                input2.value = '" . self::POSTTO_URL . "';
                document.body.appendChild(input2);
                form.appendChild(input2);

                var input3 = document.createElement('input');
                input3.type = 'hidden';
                input3.name = 'target';
                input3.value = 'GetPaymentSetup';
                document.body.appendChild(input3);
                form.appendChild(input3);

                form.submit();
            }
        </script>";
        $html2 = "<br><button id='hidformbtn' onclick=\"post_to_url()\">Click 3</button><br>";

        echo $script1;
        echo $html2;
}

I guess this will work. I need to automate it in some cases, but that's not a problem. I'd just like to know why the jQuery/Ajax fails.


I have searched everywhere, but cannot find any questions related to this system. So I need some guidance, and perhaps as a reference to anyone in the future using this. I've been tasked to integrate with ModPay.com's payment system. It's also known as TotalTransact. For testing purposes, they have a simple enough page you can do a POST to and get back a response without any real data exchange. I have written code that works with this, but when I add an option to "PostTo a different url", I don't get back a response. First, here is how the option is described in their API documentation:

If you supply a PostTo=URL argument, the results of the call to the xxxxx page will post its results to the url provided rather than returning a web page with the results.

I want to post the results to a different url for splitting out parsing more easily. If I don't use this parameter, I get the results back ok from the page sending out the request. If I use the parameter, I still get a successful POST, but nothing back. Their developers are scratching their heads, so I am more inclined to think it is something with my code.

I have verified that they can post directly to my PostTo url. So I know that's not the issue. I have also added "Access-Control-Allow-Origin" headers to make sure they can post back ok to avoid cross-domain issues.

Here is the relevant code in PHP:

function post($url, $data) {
    $header = array("Content-Type: application/x-www-form-urlencoded");
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

    //curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ) ;
    //curl_setopt($curl, CURLOPT_SSLVERSION,3);
    //curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
    //curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
    curl_setopt($curl, CURLOPT_HEADER, true);  //This option returns some data to the screen
    curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
    //curl_setopt($curl, CURLOPT_COOKIEFILE, ''); //write cookies.  empty string to ignore.
    //curl_setopt($curl, CURLOPT_COOKIEJAR, ''); //read cookies
    //curl_setopt($curl, CURLOPT_VERBOSE, '1');
    //curl_setopt($curl, CURLOPT_NOPROXY, 1); // causes POST to fail
    //curl_setopt($curl, CURLOPT_HTTPHEADER,array("Expect:"));
    //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

    $response = curl_exec($curl);
    curl_close($curl);
    return $response;
}

where

$data = http_build_query($_POST, '', '&');

and $_POST is coming from an AJAX call on another page (proxy to avoid browser CORS issues):

public function GetPaymentSetup($useButton) {
    $script = "<script type=\"text/javascript\">";
    if($useButton == true) {
        $script .= "
        $(document).ready(function() {
        $(\"button\").click(function() {
        ";
    }
    $script .= "
        var request = jQuery.ajax ({
            type:   'POST',
            url:    '" . self::PROXY_URL . "',
            crossDomain: true,
            data:       { \"clientId\"      : \"" . self::CLIENTID . "\",
                          \"clientCode\"    : \"" . self::CLIENTCODE . "\",
                          \"postTo\"        : \"" . self::POSTTO_URL . "\" 
                        }
            success: function(responseData, textStatus, jqXHR) {
                        $('#dresponse').html(responseData);
                     },
            error:   function(responseData, textStatus, errorThrown) {
                        $('#dresponse').html('Error. POST failed.');
                     }
        });";
    if($useButton == true) {
        $script .= "
        });});";
    }
    $script .= " </script>";
    if($useButton == true)
        $html = "<button>click</button><div id='dresponse'></div>";
    else
        $html = "<div id='dresponse'></div>";
    echo $script;
    echo $html;
}

Can anyone explain why when I use this line:

                      \"postTo\"        : \"" . self::POSTTO_URL . "\" 

the POSTTO_URL page never gets anything back. But if I remove the line, I get a response back? And again, with the POSTTO_URL, I don't get an error back in the original AJAX call either, so it acts as a successful POST. I can verify this by changing the CLIENTID to something else, then I get a failure.

Is this something I'm doing, or something incorrect on their end? Does anyone have any experience integrating with them?

Thanks in Advance!

No one has had any insight on why the original Ajax/jQuery fails. But I do have a working solution, so here it is below for anyone looking in the future for TotalTransact/ModPay integration:

Create a file: modPayProcessing.php:

<?php
class modPayProcessing {
    const CLIENTCODE = "xxxxxxx";
    const CLIENTID = "xxxxxx";
    const PROXY_URL = "https://....modPayProxy.php";
    const POSTTO_URL = "https://....modPayReceive.php";

    public function GetPaymentSetup($useButton=false, $usePostTo=false) {
        $script = "<script type=\"text/javascript\">
            function post_to_url() {
                var form = document.createElement('form');
                form.action = '" . self::PROXY_URL . "';
                form.method = 'POST';

                var input = document.createElement('input');
                input.type = 'hidden';
                input.name = 'clientId';
                input.value = '" . self::CLIENTID . "';
                document.body.appendChild(input);
                form.appendChild(input);

                var input1 = document.createElement('input');
                input1.type = 'hidden';
                input1.name = 'clientCode';
                input1.value = '" . self::CLIENTCODE . "';
                document.body.appendChild(input1);
                form.appendChild(input1);
        ";
        if($usePostTo === true) {
            $script .= "
                var input2 = document.createElement('input');
                input2.type = 'hidden';
                input2.name = 'postTo';
                input2.value = '" . self::POSTTO_URL . "';
                document.body.appendChild(input2);
                form.appendChild(input2);
            ";
        }
        $script .= "
                var input3 = document.createElement('input');
                input3.type = 'hidden';
                input3.name = 'target';
                input3.value = 'GetPaymentSetup';
                document.body.appendChild(input3);
                form.appendChild(input3);

                form.submit();
            }
        ";
        if($useButton == true) {
            $html = "<button id='hidformbtn' onclick=\"post_to_url()\">Submit</button>";
            $script .= "</script>";
        }
        else {
            $html = "";
            $script .= "
            if(window.onload) {
                var currOnload = window.onload;
                var newOnload = function() {
                    currOnload();
                    post_to_url();
                };
                window.lonload = newOnload;
            }
            else {
                window.onload = post_to_url();
            }
        </script>";
        }

        echo $script;
        echo $html;
    }

    public function PostPayment($useButton, $usePostTo=false) {
    // Same as the above function, but change the input.name/value to be for whatever you want to submit
    // Implementations of how to retrieve the data vary, so I am not listing it exactly here, but the basic format is the same.
    }

Create a file: modPayProxy.php:

//You can use Curl, or you can use php Streams.  Below is a function for each:
<?php
    function postCurl($url, $data) {
        $header = array("Content-Type: application/x-www-form-urlencoded");
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_POST, 3);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_HEADER, false);  //This option returns some data to the screen
        curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);

        $response = curl_exec($curl);
        curl_close($curl);
        return $response;
    }

    function postStreams($url, $data) {
        $params = array(
            'http' => array(
                'method'=>'POST',
                'content'=>$data,
                'header'=>array("Content-Type: application/x-www-form-urlencoded
" . "Content-Length: " . strlen($data) . "
"),
                'user_agent'=>"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)"
            )
        );
        $ctx = stream_context_create($params);
        $fp = @fopen($url, 'rb', false, $ctx);
        if(!$fp) {
            throw new Exception("Problem with $url, $php_errormsg");
        }
        $response = @stream_get_contents($fp);
        fclose($fp);
        if($response === false) {
            throw new Exception("Problem reading data from $url, $php_errormsg");
        }
        return $response;
    }

    if(array_key_exists('target', $_POST)) {
        if($_POST['target'] == 'GetPaymentSetup')
            $url = GETSETUP_URL;
        elseif($_POST['target'] == 'PostPayment')
            $url = POST_URL;
        else
            $url = "";

        unset($_POST['target']);
        $data = http_build_query($_POST, '', '&');

        echo(postStreams($url, $data)); //change to postCurl if you prefer
    }
    else {  // if POSTTO_URL is not used in the calling function.
        $postdata = file_get_contents("php://input");
        parse_str($postdata, $output);
        // Do whatever you want with $output now.
    }

Create a file, modPayReceive.php:

<?php
    $postdata = file_get_contents("php://input");
    parse_str($postdata, $output);
    // Now do whatever you want with $output now.

To initiate a call, make sure you require modPayProcessing.php, then:

$cc = new modPayProcessing();
$cc->GetPaymentSetup(true, false);

If the second argument is false, it won't use the POSTTO_URL / modPayReceive.php file. The first argument can be set to true to have this as a button on a form. Or false to process directly (a better idea so you don't expose your clientID, etc.)