Submit form as a server and not the client with cURL

What?
A quick article on how to create a middleware script which accepts the values from a submitted HTML form and sends it to a server on another domain for processing. This applies to Linux Apache MySQL and PHP (LAMP) setups.

Why?
A customer wanted to connect their Mobile App to a third-party API. The third-party only accepts requests from a static and permitted IP address. If the end-user were to make the request, then their own IP address would be the one checked against, and it just wouldn't be manageable to add every new user's IP address to their service. The request has to come from a permitted server with a single IP address.

How?
It's likely that you already know how to submit a HTML form to a server via your Mobile application so the following will only document the process of sending data under the server IP address. We're going to use a PHP script with the cURL function to receive and send the data. cURL is a standard feature on most LAMP setups. If not you can install it from here: http://curl.haxx.se/download.html

I'm demonstrating this with 3 scripts on the intermediary server:
1. to display the HTML form (usually handled by the mobile app)
2. to send the values to the third-party (executable script)
3. to receive the values from the third-party service/server/api

Script #1: The HTML form:
<form id="my_form" method="post" action="./send.php">
	Sender: <input id="my_sender" name="my_sender" value="MyMobile" readonly="readonly" /><br />
	Receiver: <input id="my_receiver" name="my_receiver" value="JoelLipman.com" readonly="readonly" /><br />
	<input type="submit" name="submit" value="Submit" />
</form>

Script #2: Send to third-party in XML format: (because that's the format they accept)
Note that in the following example, the XML is sent as a url encoded value paired with a key (posted variables must be key1=value1&key2=value2...)
<?php
        /*
        * XML Sender/Client.
        */
        header ("Content-Type:text/xml");

        // format submitted values
        $the_sender = trim($_POST['my_sender']);
        $the_receiver = trim($_POST['my_receiver']);

        // set some values
        $the_thirdparty_url = "http://www.joellipman.com/my_api";

        // split out parameters and put in format the third-party is expecting
        $the_message_xml = "the_message=" . urlencode("<?xml version='1.0' encoding='UTF-8' ?>
                <the_message>
                        <header sender='".$the_sender."' receiver='".$the_receiver."' mode='TEST'></header>
                </the_message>");

        // We send XML via CURL using POST with a http header of text/xml.
        $ch = curl_init();

        // set URL and other appropriate options
        curl_setopt($ch, CURLOPT_URL, $the_thirdparty_url); // where to send the variables
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $the_message_xml);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
        curl_setopt($ch, CURLOPT_REFERER, 'receive.php');  // my receiving file
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $ch_result = curl_exec($ch);
        curl_close($ch);

        // Print CURL result.
        echo $ch_result;
?>

Script #3: process returned data
<?php
        /*
        * XML Server.
        */
        // We use php://input to get the raw $_POST results.
        $xml_post = file_get_contents('php://input');

        // If we receive data, save it.
        if ($xml_post) {
                $xml_file = 'received_xml_' . date('Y_m_d-H-i-s') . '.xml';
                $fh       = fopen($xml_file, 'w') or die();
                fwrite($fh, $xml_post);
                fclose($fh);

                // Return, as we don't want to cause a loop by processing the code below.
                return;
        }
?>

UPDATE 2015 - Implementation
Finally I opted with a single PHP script that receives the variables from a mobile application, talks to the third-party API, parses the response, and returns the data to the mobile app. Additionally, the client wanted the reply to mobile in JSON format so we will extract the values with SIMPLEXML_LOAD_STRING (as of PHP 5), store the values in an array for easy encoding to JSON. This is a cut-down version of my final script:
// set headers for JSON file
header('Content-Type: text/javascript; charset=utf8');
header('Access-Control-Allow-Origin: http://mobile.joellipman.com/');
header('Access-Control-Max-Age: 3628800');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');

// the posted XML
$the_message_xml="<?xml version='1.0' encoding='UTF-8' ?><message><request><my_xml_string /></request></message>";

// We send XML via cURL using POST with a http header of text/xml.
$ch = curl_init();

// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://api.myexample.com"); // action where to POST the variables
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $the_message_xml);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($ch, CURLOPT_REFERER, 'receive.php');  // my receiving file
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// execute and store
$ch_result = curl_exec($ch);
curl_close($ch);

// Store cURL result
$api_result = simplexml_load_string($ch_result) or die("Error: Cannot create object");

// Declare main array to store all variables
$reply_array = array();

// parse out some global values we want from XML
if($api_result->reply!=null){

        // our own script reply to the request
        $reply_array["sys"] = array("message"=>"ok", "code"=>200);

        // parse and store the attribute value "entry_count" of element "room_rate_list".
        // NOTE: also declare the datatype or this won't work!
        $entry_count = (int) $api_result->reply->room_rate_list['entry_count'];

        // parse and store the attribute value "currency" of first entry in element "room_rate_list".
        // NOTE: declare the datatype for string as (string) or this won't work!
        $api_currency = (string) $api_result->reply->room_rate_list->room_rate[0]['currency'];

        $reply_array["data"] = array("entry_count"=>$entry_count, "currency"=>$api_currency);

} else {

        // our own script reply to the request
        $reply_array["sys"] = array("message"=>"ERROR", "code"=>404);

}

// return and print the JSON
$json_array = json_encode($reply_array);
echo $json_array;


// field validation, variable formatting/transform rules, and user logging are also done via this script.

Issues?
- Internal Server Error - I can get this when the third-party API doesn't return expected XML. Ensure that CURLOPT_POSTFIELDS is a string of paired values (ie. key1=value1&key2=value2&key3=value3).
- DataType or value is blank - the data that gets parsed with SIMPLEXML_LOAD_STRING needs to have each variable's datatype declared or it will return as an object. Prefix with (int) for numbers/integers and (string) for strings.


Related Articles

Joes Revolver Map

Accreditation

Badge - Certified Zoho Creator Associate
Badge - Certified Zoho Creator Associate

Donate & Support

If you like my content, and would like to support this sharing site, feel free to donate using a method below:

Paypal:
Donate to Joel Lipman via PayPal

Bitcoin:
Donate to Joel Lipman with Bitcoin - Valid till 8 May 2022 3QnhmaBX7LQSRsC9hh6Je9rGQKEGNQNfPb
© 2021 Joel Lipman .com. All Rights Reserved.