Saturday, May 6, 2017

Create Contact in Xero Via API using PHP

The very first step you make a connection between your application and Xero API. To do so you need to create and application in Xero and establish a connection. Follow the below link to see how PHP communicates with Xero Application:

http://pritomkumar.blogspot.com/2017/05/connect-to-xero-private-application.html

And below is full PHP script which will create Contacts in Xero:


<?php
define('BASE_PATH', dirname(__FILE__));
$configs = array(
    "consumer_key" => "OVLVISLORWW72YRDYAV3KI........",
    "consumer_secret" => "HRTPKPBSSBNN4TA32BKW.......",
    "core_version" => "2.0",
    "payroll_version" => "1.0",
    "file_version" => "1.0",
    "rsa_private_key" => BASE_PATH . "/XeroCerts/privatekey.pem",
    "application_type" => "Private",
    "oauth_callback" => "oob",
    "user_agent" => "Trial #3"
);

$parameters = array();

createContact($configs, $parameters);

function createContact($configs, $parameters)
{
    $post = array(
        "Contacts" => array(
            "Contact" => array(
                "Name" => "Xero Contact 1"
            ),
            "Contact 2" => array(
                "Name" => "Xero Contact 2"
            )
        )
    );
    $xero_connector = new XeroConnector($configs, $parameters);
    $contact_data = $xero_connector->execute("POST", "Contact", $post);
    XeroConnector::prettyPrint($contact_data);
}

class XeroConnector
{
    private $configs = null;
    private $headers = array();
    private $parameters = array();
    private $nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    function __construct($configs, $parameters)
    {
        $this->configs = $configs;
        $this->parameters = $parameters;
    }

    function execute($method, $target, $post_body = "")
    {
        if (is_array($post_body)) {
            $this->post_body = $this->arrayToXml($post_body);
        } else {
            $this->post_body = $post_body;
        }
        $this->method = strtoupper($method);
        $this->path = "https://api.xero.com/api.xro/2.0/$target";
        $this->buildParameters();
        $this->sign();
        return $this->curlRequest($this->sign["signed_url"]);
    }

    private function sign()
    {
        $this->parameters["oauth_signature"] = $this->generateSignature();
        $this->sign = array(
            'parameters' => $this->parameters,
            'signature' => $this->escape($this->parameters["oauth_signature"]),
            'signed_url' => $this->path . '?' . $this->normalizeParameters('true'),
            'header' => $this->getHeaderString(),
            'sbs' => $this->sbs
        );
    }

    private function curlRequest($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_USERAGENT, "Test APP");
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $this->headers['Accept'] = 'application/json';
        switch ($this->method) {
            case "GET":
                $this->content_length = 0;
                break;
            case 'POST':
                $this->headers['Content-Length'] = strlen($this->post_body);
                curl_setopt($ch, CURLOPT_POST, TRUE);
                curl_setopt($ch, CURLOPT_POSTFIELDS, $this->post_body);
                $this->headers['Content-Type'] = 'application/xml';
                break;
            case 'PUT' :
                $this->headers['Content-Length'] = strlen($this->post_body);
                $fh = tmpfile();
                fwrite($fh, $this->post_body);
                rewind($fh);
                curl_setopt($ch, CURLOPT_PUT, true);
                curl_setopt($ch, CURLOPT_INFILE, $fh);
                curl_setopt($ch, CURLOPT_INFILESIZE, $this->headers ['Content-Length']);
                $this->headers['Content-Type'] = 'application/x-www-form-urlencoded';
                break;
            default :
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
        }

        if (count($this->headers) > 0) {
            $headers = array();
            foreach ($this->headers as $k => $v) {
                $headers [] = trim($k . ': ' . $v);
            }
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }

        $response = curl_exec($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        curl_close($ch);

        return array('body' => json_decode($response), 'code' => $code);
    }

    private function safe_encode_xml_entity($data)
    {
        return str_ireplace(
            array("<",    ">",    "'",      "\"",    "&"),
            array("&lt;", "&gt;", "&apos;", "&quot", "&amp;"),
            $data
        );
    }

    private function getHeaderString($args = array())
    {
        $result = 'OAuth ';

        foreach ($this->parameters as $pName => $pValue) {
            if (strpos($pName, 'oauth_') !== 0)
                continue;
            if (is_array($pValue)) {
                foreach ($pValue as $val) {
                    $result .= $pName . '="' . $this->escape($val) . '", ';
                }
            } else {
                $result .= $pName . '="' . $this->escape($pValue) . '", ';
            }
        }
        return preg_replace('/, $/', '', $result);
    }

    private function generateSignature()
    {
        switch ($this->parameters['oauth_signature_method']) {
            case 'RSA-SHA1':
                $private_key = openssl_pkey_get_private($this->readFile($this->configs['rsa_private_key']));
                $this->sbs = $this->escape($this->method) . "&" . $this->escape($this->path) . "&" .
                    $this->escape($this->normalizeParameters());
                openssl_sign($this->sbs, $signature, $private_key);
                openssl_free_key($private_key);
                return base64_encode($signature);
            case 'PLAINTEXT':
                return urlencode($this->configs["consumer_secret"]);
            case 'HMAC-SHA1':
                $this->sbs = $this->escape($this->method) . '&' . $this->escape($this->path) . '&' . $this->escape($this->normalizeParameters());
                return base64_encode(hash_hmac('sha1', $this->sbs, $this->configs["consumer_secret"], true));
            default:
                throw new OAuthSimpleException('Unknown signature method for OAuthSimple');
        }
    }

    private function normalizeParameters($filter = 'false')
    {
        $elements = array();
        ksort($this->parameters);
        foreach ($this->parameters as $k => $v) {
            if ($k == 'xml') {
                if ($filter == "true")
                    continue;
            }
            if (preg_match('/\w+_secret/', $k))
                continue;
            if (is_array($v)) {
                sort($v);
                foreach ($v as $element)
                    array_push($elements, $this->escape($k) . '=' . $this->escape($element));
                continue;
            }
            array_push($elements, $this->escape($k) . '=' . $this->escape($v));
        }
        return join('&', $elements);
    }

    private function readFile($file_path)
    {
        $fp = fopen($file_path, "r");
        $file_contents = fread($fp, 8192);
        fclose($fp);
        return $file_contents;
    }

    private function arrayToXml($data, $tab = 0)
    {
        $xml = "\r\n";
        foreach ($data as $k => $v)
        {
            $sp = "";
            for ($i = 0; $i < $tab; $i++)
            {
                $sp .= "\t";
            }
            if (strpos($k, " ") !== false) {
                $k = substr($k, 0, strpos($k, " "));
            }
            if (is_array($v)) {
                $tab2 = $tab + 1;
                $v = $this->arrayToXml($v, $tab2);
                $xml .= "$sp<$k>$v". "$sp</$k>\r\n";
            } else {
                $v = $this->safe_encode_xml_entity($v);
                $xml .= "$sp<$k>$v</$k>\r\n";
            }
        }
        return $xml;
    }

    private function escape($string)
    {
        if ($string === 0)
            return 0;
        if (empty($string))
            return '';
        if (is_array($string))
            throw new OAuthSimpleException('Array passed to _oauthEscape');

        $string = rawurlencode($string);
        $string = str_replace('+', '%20', $string);
        $string = str_replace('!', '%21', $string);
        $string = str_replace('*', '%2A', $string);
        $string = str_replace('\'', '%27', $string);
        $string = str_replace('(', '%28', $string);
        $string = str_replace(')', '%29', $string);
        return $string;
    }

    private function buildParameters()
    {
        $this->parameters["oauth_nonce"] = $this->getNonce(20);
        $this->parameters["oauth_token"] = $this->configs["consumer_key"];
        $this->parameters["oauth_version"] = "1.0";
        $this->parameters["oauth_timestamp"] = time();
        $this->parameters["oauth_consumer_key"] = $this->configs["consumer_key"];
        $this->parameters["oauth_signature_method"] = "RSA-SHA1";
    }

    private function getNonce($length)
    {
        $result = '';
        $nonce_length = strlen($this->nonce_chars);
        for ($i = 0; $i < $length; $i++) {
            $index = rand(0, $nonce_length);
            $result .= substr($this->nonce_chars, $index, 1);
        }
        return $result;
    }

    public static function prettyPrint($o)
    {
        echo "<pre>";
        print_r($o);
        echo "</pre>";
    }
}




Below is a screen-shot of my Xero portal:




Output will be as follows:


Array
(
    [code] => 200
    [body] => stdClass Object
        (
            [Id] => e614f8ea-07b1-4b69-88b0-c174ffb062d5
            [Status] => OK
            [ProviderName] => Trial #2
            [DateTimeUTC] => /Date(1494045918570)/
            [Contacts] => Array
                (
                    [0] => stdClass Object
                        (
                            [ContactID] => 7de72cb7-7d6f-13f8-9e76-ea191269553a
                            [ContactStatus] => ACTIVE
                            [Name] => Xero Contact 1
                            [EmailAddress] => 
                            [BankAccountDetails] => 
                            [Addresses] => Array
                                (
                                    [0] => stdClass Object
                                        (
                                            [AddressType] => STREET
                                            [City] => 
                                            [Region] => 
                                            [PostalCode] => 
                                            [Country] => 
                                        )

                                    [1] => stdClass Object
                                        (
                                            [AddressType] => POBOX
                                            [City] => 
                                            [Region] => 
                                            [PostalCode] => 
                                            [Country] => 
                                        )

                                )

                            [Phones] => Array
                                (
                                    [0] => stdClass Object
                                        (
                                            [PhoneType] => DEFAULT
                                            [PhoneNumber] => 
                                            [PhoneAreaCode] => 
                                            [PhoneCountryCode] => 
                                        )

                                    [1] => stdClass Object
                                        (
                                            [PhoneType] => DDI
                                            [PhoneNumber] => 
                                            [PhoneAreaCode] => 
                                            [PhoneCountryCode] => 
                                        )

                                    [2] => stdClass Object
                                        (
                                            [PhoneType] => FAX
                                            [PhoneNumber] => 
                                            [PhoneAreaCode] => 
                                            [PhoneCountryCode] => 
                                        )

                                    [3] => stdClass Object
                                        (
                                            [PhoneType] => MOBILE
                                            [PhoneNumber] => 
                                            [PhoneAreaCode] => 
                                            [PhoneCountryCode] => 
                                        )

                                )

                            [UpdatedDateUTC] => /Date(1494044711587+0000)/
                            [ContactGroups] => Array
                                (
                                )

                            [IsSupplier] => 
                            [IsCustomer] => 
                            [SalesTrackingCategories] => Array
                                (
                                )

                            [PurchasesTrackingCategories] => Array
                                (
                                )

                            [ContactPersons] => Array
                                (
                                )

                            [HasValidationErrors] => 
                        )

                    [1] => stdClass Object
                        (
                            [ContactID] => 8eb00757-74f3-4302-99a8-3453dd4d456c
                            [ContactStatus] => ACTIVE
                            [Name] => Xero Contact 2
                            [EmailAddress] => 
                            [BankAccountDetails] => 
                            [Addresses] => Array
                                (
                                    [0] => stdClass Object
                                        (
                                            [AddressType] => STREET
                                            [City] => 
                                            [Region] => 
                                            [PostalCode] => 
                                            [Country] => 
                                        )

                                    [1] => stdClass Object
                                        (
                                            [AddressType] => POBOX
                                            [City] => 
                                            [Region] => 
                                            [PostalCode] => 
                                            [Country] => 
                                        )

                                )

                            [Phones] => Array
                                (
                                    [0] => stdClass Object
                                        (
                                            [PhoneType] => DEFAULT
                                            [PhoneNumber] => 
                                            [PhoneAreaCode] => 
                                            [PhoneCountryCode] => 
                                        )

                                    [1] => stdClass Object
                                        (
                                            [PhoneType] => DDI
                                            [PhoneNumber] => 
                                            [PhoneAreaCode] => 
                                            [PhoneCountryCode] => 
                                        )

                                    [2] => stdClass Object
                                        (
                                            [PhoneType] => FAX
                                            [PhoneNumber] => 
                                            [PhoneAreaCode] => 
                                            [PhoneCountryCode] => 
                                        )

                                    [3] => stdClass Object
                                        (
                                            [PhoneType] => MOBILE
                                            [PhoneNumber] => 
                                            [PhoneAreaCode] => 
                                            [PhoneCountryCode] => 
                                        )

                                )

                            [UpdatedDateUTC] => /Date(1494044711620+0000)/
                            [ContactGroups] => Array
                                (
                                )

                            [IsSupplier] => 
                            [IsCustomer] => 
                            [SalesTrackingCategories] => Array
                                (
                                )

                            [PurchasesTrackingCategories] => Array
                                (
                                )

                            [ContactPersons] => Array
                                (
                                )

                            [HasValidationErrors] => 
                        )

                )

        )

)

1 comment: