1

I would like to integrate with ChannelAdvisor REST API using the SOAP Credentials Flow.

Based on their documentation, I have setup the following in PostMan (rest client in Chrome browser) like this:

enter image description here

enter image description here

When I make the rest; the rest api server returns the expected response:

enter image description here

So, I tried to replicate this in PHP with the following class:

<?php

class ChannelAdvisorREST {

    /**
     * ChannelAdvisor constants & properties
     */
    const BASE_URL = 'https://api.channeladvisor.com/v1';
    private $config;

    /**
     * Class constructor
     */
    public function __construct()
    {
        $this->config = \Config::get('channeladvisor');
    }

    // TEST
    public function test($accountId)
    {
        // var_dump($this->config);

        var_dump(self::getAccessToken($accountId));
    }
    // TEST

    /**
     * Method to get access token from rest server.
     *
     * @param $accountId
     * @return string
     */
    private function getAccessToken($accountId)
    {
        return self::curlPOST('/oauth2/token', [
            'client_id' => $this->config['api_app_id'],
            'grant_type' => 'soap',
            'scope' => 'inventory',
            'developer_key' => $this->config['api_developer_key'],
            'password' => $this->config['api_password'],
            'account_id' => $accountId
        ]);
    }

    /**
     * Method to generate a HTTP POST request
     *
     * @param $endpoint
     * @param $fields
     * @return string
     */
    private function curlPOST($endpoint, $fields = array())
    {
        // Open connection
        $ch = curl_init();

        // Set the url, number of POST vars, POST data
        curl_setopt($ch, CURLOPT_USERPWD, $this->config['api_app_id'] .':'. $this->config['api_shared_secret']);
        curl_setopt($ch, CURLOPT_URL, self::BASE_URL . $endpoint);
        curl_setopt($ch, CURLOPT_POST, count($fields));
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields, '', '&'));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/x-www-form-urlencoded'
        ));

        // Execute post request
        $result = curl_exec($ch);

        // Close connection
        curl_close($ch);

        // Finished
        return $result;
    }
}

When I execute the test($accId) method on this class, I get the following response:

boolean false

Any idea why it isn't quite working as same as the PostMan test?

P.S. I have already verified all the config/parms etc... are correct and same as my PostMan test. This class is a snipped version from my original code (created in Laravel 4.2, but this issue is not related to Laravel).

Community
  • 1
  • 1
Latheesan
  • 23,247
  • 32
  • 107
  • 201

2 Answers2

0

Two things:

  1. Make sure that you send the same headers as your browser sends. For example, I don't see the Authorization-header in your code, and that one is probably quite crucial for authorizing the request on the server-side. Also for the scope you use 'inventory' instead of 'orders inventory'. Be very strict in this exercise.

  2. Test the post-data not in an array, but write down the query-string as it should be according to yourself, this way you know there is not some issue by CURL trying to convert your array into a query-string (note, both is possible for CURL, array and query-string).

So most easy to test with:

client_id=1234&grant_type=soap&scope=order%20inventory...etc add other variables...
  • The `Authorisation` header in my post man test is for BASIC AUTH credentials, which are set in php using `CURLOPT_USERPWD` - is this not the same? The `scope` field accepts `orders inventory` as well as just `inventory` (I do not think the issue is here). I have already tested my post data, it's a string. – Latheesan Jan 17 '16 at 21:16
  • Overlooked that CURLOPT_USERPWD, looks good. If you tested the post-data as string as well, then I don't see where it goes south. Then I would suggest to follow the http-stream via e.g., firebug or HTTP-headers and check if there for example is one or more redirects, and that during these redirects a cookie is set with specific values. That is quite common practice as well. If that is the case in the browser, then implement this functionality in your php-code as well. –  Jan 17 '16 at 21:24
  • I have figured it out. See my answer. – Latheesan Jan 17 '16 at 22:27
0

I have found the problem. The issue was caused by my php not being configured with curl.cainfo.

I found this by adding the following debug code to my curlPOST method like this:

private function curlPOST($endpoint, $fields = array())
{
    // Open connection
    $ch = curl_init();

    // Set the url, number of POST vars, POST data
    curl_setopt($ch, CURLOPT_USERPWD, $this->config['api_app_id'] .':'. $this->config['api_shared_secret']);
    curl_setopt($ch, CURLOPT_URL, self::BASE_URL . $endpoint);
    curl_setopt($ch, CURLOPT_POST, count($fields));
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields, '', '&'));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/x-www-form-urlencoded'
    ));
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    $verbose = fopen('php://temp', 'w+');
    curl_setopt($ch, CURLOPT_STDERR, $verbose);

    // Execute post request
    $result = curl_exec($ch);

    // Debug error
    if ($result === FALSE) {
        printf("cUrl error (#%d): %s<br>\n", curl_errno($ch), htmlspecialchars(curl_error($ch)));
        rewind($verbose);
        $verboseLog = stream_get_contents($verbose);
        echo "Verbose information:\n<pre>", htmlspecialchars($verboseLog), "</pre>\n";
    }
    @fclose($verbose);

    // Close connection
    curl_close($ch);

    // Finished
    return $result;
}

This outputted the following error message:

cUrl error (#60): SSL certificate problem: unable to get local issuer certificate
Verbose information:
* Hostname was found in DNS cache
* Hostname in DNS cache was stale, zapped
*   Trying 216.27.89.14...
* Connected to api.channeladvisor.com (216.27.89.14) port 443 (#7)
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 7
boolean false

Which helped me track down the issue with my php.

Latheesan
  • 23,247
  • 32
  • 107
  • 201