diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..a612c0c5a35974f06e09e2d4d36bfed37630805b
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,15 @@
+tests/ export-ignore
+vendor/ export-ignore
+.editorconfig export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.git/ export-ignore
+.gitlab-ci.yml export-ignore
+.idea export-ignore
+apigen.neon export-ignore
+build-coverage/ export-ignore
+docs/ export-ignore
+LICENSE.md export-ignore
+phpcs.xml.dist export-ignore
+phpunit-integration.xml export-ignore
+phpunit-unit.xml export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..4b86223d6cae8ddb86d7cc3d12e0fcca43717f54
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/vendor/
+.idea
+composer.lock
+build-coverage
+swagger
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..db521c16d8bda63ee8608498ded2b1e6665ea5d0
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,6 @@
+variables:
+  DISABLE_ACCEPTANCE: "1"
+  DISABLE_FUNCTIONAL: "1"
+
+include: 'https://gitlab.com/wpdesk/gitlab-ci/raw/master/gitlab-ci-1.2.yml'
+
diff --git a/README.md b/README.md
index 9874595e331942ba26bbf3bdb8301ec5a4c9ed5f..df38b7d2c3f1d841cac9f886f88779602cade307 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,6 @@
-# wp-http-client
+[![pipeline status](https://gitlab.com/wpdesk/wp-http-client/badges/master/pipeline.svg)](https://gitlab.com/wpdesk/wp-http-client/commits/master) 
+Integration: [![coverage report](https://gitlab.com/wpdesk/wp-http-client/badges/master/coverage.svg?job=integration+test+lastest+coverage)](https://gitlab.com/wpdesk/wp-http-client/commits/master)
+Unit: [![coverage report](https://gitlab.com/wpdesk/wp-http-client/badges/master/coverage.svg?job=unit+test+lastest+coverage)](https://gitlab.com/wpdesk/wp-http-client/commits/master)
 
+HTTP Client
+===========
diff --git a/changelog.txt b/changelog.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dbce6b454757f6d120590ec01412e1dfb6ad0812
--- /dev/null
+++ b/changelog.txt
@@ -0,0 +1,18 @@
+= 1.7.0 - 2019-01-16 =
+* Changed public methods -> protected in ShipmentResponse
+* Changed shipment cost to be nullable
+
+= 1.6.5 - 2019-01-04 =
+* Added messageStock to rate response
+
+= 1.6.4 - 2018-12-18 =
+* Added label file extension
+
+= 1.6.3 - 2018-12-10 =
+* Added support for domain not allowed response code: 462
+
+= 1.6.2 - 2018-12-06 =
+* Added support for platform version hash in response headers
+
+= 1.6 - 2018-12-06 =
+* Added default request headers
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..f1925d225656578ab21562edfdf8e6a7ef410d58
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,36 @@
+{
+  "name": "wpdesk/wp-http-client",
+  "authors": [
+    {
+      "name": "Krzysiek",
+      "email": "krzysiek@wpdesk.pl"
+    }
+  ],
+  "require": {
+    "php": ">=5.5",
+    "ext-curl": "*",
+    "ext-json": "*",
+    "psr/log": "^1.0.1",
+    "psr/simple-cache": "^1.0"
+  },
+  "require-dev": {
+    "phpunit/phpunit": "<7",
+    "wp-coding-standards/wpcs": "^0.14.1",
+    "squizlabs/php_codesniffer": "^3.0.2",
+    "mockery/mockery": "*",
+    "10up/wp_mock": "*"
+  },
+  "autoload": {
+    "psr-4": {
+      "WPDesk\\HttpClient\\": "src/"
+    }
+  },
+  "autoload-dev": {
+  },
+  "scripts": {
+    "phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
+    "phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
+    "phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
+    "phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
+  }
+}
diff --git a/phpunit-integration.xml b/phpunit-integration.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4a342abf125d638d044bafa01cd9a75a771b4b3d
--- /dev/null
+++ b/phpunit-integration.xml
@@ -0,0 +1,28 @@
+<phpunit bootstrap="tests/integration/bootstrap.php"
+         backupGlobals="false"
+     >
+    <testsuites>
+        <testsuite>
+            <directory prefix="Test" suffix=".php">./tests/integration</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory suffix=".php">src</directory>
+        </whitelist>
+    </filter>
+
+    <logging>
+        <log type="junit" target="build-coverage/report.junit.xml"/>
+        <log type="coverage-html" target="build-coverage/coverage" charset="UTF-8" yui="true" highlight="true"/>
+        <log type="coverage-text" target="build-coverage/coverage.txt"/>
+        <log type="coverage-clover" target="build-coverage/clover.xml"/>
+    </logging>
+
+    <php>
+        <env name="WP_DEVELOP_DIR" value="/tmp/wordpress-develop"/>
+        <env name="WC_DEVELOP_DIR" value="/tmp/woocommerce"/>
+    </php>
+
+</phpunit>
\ No newline at end of file
diff --git a/phpunit-unit.xml b/phpunit-unit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..31e5c9f9c202769cac9487ac192f331e827c9489
--- /dev/null
+++ b/phpunit-unit.xml
@@ -0,0 +1,21 @@
+<phpunit bootstrap="tests/unit/bootstrap.php">
+    <testsuites>
+        <testsuite>
+            <directory prefix="Test" suffix=".php">./tests/unit/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory suffix=".php">src</directory>
+        </whitelist>
+    </filter>
+
+    <logging>
+        <log type="junit" target="build-coverage/report.junit.xml"/>
+        <log type="coverage-html" target="build-coverage/coverage" charset="UTF-8" yui="true" highlight="true"/>
+        <log type="coverage-text" target="build-coverage/coverage.txt"/>
+        <log type="coverage-clover" target="build-coverage/clover.xml"/>
+    </logging>
+
+</phpunit>
diff --git a/src/Curl/CurlClient.php b/src/Curl/CurlClient.php
new file mode 100644
index 0000000000000000000000000000000000000000..704ad51cdeb8290032abfdb1581f98df5731bd8b
--- /dev/null
+++ b/src/Curl/CurlClient.php
@@ -0,0 +1,206 @@
+<?php
+
+namespace WPDesk\HttpClient\Curl;
+
+use WPDesk\HttpClient\HttpClient;
+use WPDesk\HttpClient\HttpClientResponse;
+use WPDesk\HttpClient\HttpClientRequestException;
+
+class CurlClient implements HttpClient
+{
+    /** @var resource */
+    private $curlResource;
+
+    /** @var  string|null|boolean */
+    private $rawResponse;
+
+    /** @var int */
+    private $httpResponseCode;
+
+    /**
+     * @param string $url
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @return HttpClientResponse
+     * @throws HttpClientRequestException
+     */
+    public function get($url, $body, array $headers, $timeOut)
+    {
+        return $this->send($url, 'GET', $body, $headers, $timeOut);
+    }
+
+    /**
+     * @param string $url
+     * @param string $method
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @return HttpClientResponse
+     * @throws HttpClientRequestException
+     */
+    public function send($url, $method, $body, array $headers, $timeOut)
+    {
+        $this->initResource();
+        try {
+            $this->prepareConnection($url, $method, $body, $headers, $timeOut);
+            $this->sendRequest();
+            $this->throwExceptionIfError();
+            $this->closeConnection();
+
+            return $this->prepareResponse();
+
+        } catch (\Exception $e) {
+            $this->closeConnection();
+            if ($e instanceof HttpClientRequestException) {
+                throw $e;
+            }
+            throw CurlExceptionFactory::createDefaultException($e->getMessage(), $e->getCode(), $e);
+        }
+    }
+
+    private function initResource()
+    {
+        $this->curlResource = curl_init();
+    }
+
+    /**
+     * Opens a new curl connection.
+     *
+     * @param string $url The endpoint to send the request to.
+     * @param string $method The request method.
+     * @param string $body The body of the request.
+     * @param array $headers The request headers.
+     * @param int $timeOut The timeout in seconds for the request.
+     */
+    private function prepareConnection($url, $method, $body, array $headers, $timeOut)
+    {
+        $options = [
+            CURLOPT_CUSTOMREQUEST => $method,
+            CURLOPT_HTTPHEADER => $this->compileRequestHeaders($headers),
+            CURLOPT_URL => $url,
+            CURLOPT_CONNECTTIMEOUT => 10,
+            CURLOPT_TIMEOUT => $timeOut,
+            CURLOPT_RETURNTRANSFER => true, // Return response as string
+            CURLOPT_HEADER => true, // Enable header processing
+            CURLOPT_SSL_VERIFYHOST => 2,
+            CURLOPT_SSL_VERIFYPEER => true,
+            //CURLOPT_CAINFO => __DIR__ . '/certs/DigiCertHighAssuranceEVRootCA.pem',
+        ];
+        if ($method !== "GET") {
+            $options[CURLOPT_POSTFIELDS] = $body;
+        }
+
+        curl_setopt_array($this->curlResource, $options);
+    }
+
+    /**
+     * Compiles the request headers into a curl-friendly format.
+     *
+     * @param array $headers The request headers.
+     *
+     * @return array
+     */
+    private function compileRequestHeaders(array $headers)
+    {
+        $return = [];
+        foreach ($headers as $key => $value) {
+            $return[] = $key . ': ' . $value;
+        }
+        return $return;
+    }
+
+    /**
+     * Send the request and get the raw response from curl
+     */
+    private function sendRequest()
+    {
+        $this->rawResponse = curl_exec($this->curlResource);
+        $this->httpResponseCode = $this->getHttpResponseCode();
+    }
+
+    /** @return int */
+    private function getHttpResponseCode()
+    {
+        return intval(curl_getinfo($this->curlResource, CURLINFO_HTTP_CODE));
+    }
+
+    private function throwExceptionIfError()
+    {
+        $errorNumber = curl_errno($this->curlResource);
+        if ($errorNumber === 0) {
+            return;
+        }
+
+        $errorMessage = curl_error($this->curlResource);
+        throw CurlExceptionFactory::createCurlException($errorMessage, $errorNumber);
+    }
+
+    /**
+     * Closes an existing curl connection
+     */
+    private function closeConnection()
+    {
+        curl_close($this->curlResource);
+        $this->curlResource = null;
+    }
+
+    private function prepareResponse()
+    {
+        list($rawHeaders, $rawBody) = $this->extractResponseHeadersAndBody();
+        return new HttpClientResponse($rawHeaders, $rawBody, $this->httpResponseCode);
+    }
+
+    /**
+     * Extracts the headers and the body into a two-part array
+     *
+     * @return array
+     */
+    private function extractResponseHeadersAndBody()
+    {
+        $parts = explode("\r\n\r\n", $this->rawResponse);
+        $rawBody = array_pop($parts);
+        $rawHeaders = implode("\r\n\r\n", $parts);
+        return [trim($rawHeaders), trim($rawBody)];
+    }
+
+    /**
+     * @param string $url
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @return HttpClientResponse
+     * @throws HttpClientRequestException
+     */
+    public function post($url, $body, array $headers, $timeOut)
+    {
+        return $this->send($url, 'POST', $body, $headers, $timeOut);
+    }
+
+    /**
+     * @param string $url
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @return HttpClientResponse
+     * @throws HttpClientRequestException
+     */
+    public function delete($url, $body, array $headers, $timeOut)
+    {
+        return $this->send($url, 'DELETE', $body, $headers, $timeOut);
+    }
+
+    /**
+     * @param string $url
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @return HttpClientResponse
+     * @throws HttpClientRequestException
+     */
+    public function put($url, $body, array $headers, $timeOut)
+    {
+        return $this->send($url, 'PUT', $body, $headers, $timeOut);
+    }
+
+}
\ No newline at end of file
diff --git a/src/Curl/CurlExceptionFactory.php b/src/Curl/CurlExceptionFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..8cba48a201e69670ac1eccef666063822eb5b1d0
--- /dev/null
+++ b/src/Curl/CurlExceptionFactory.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace WPDesk\HttpClient\Curl;
+
+use WPDesk\HttpClient\Curl\Exception\CurlException;
+use WPDesk\HttpClient\Curl\Exception\CurlTimedOutException;
+
+class CurlExceptionFactory
+{
+    /**
+     * Convert curl code to appropriate exception class.
+     *
+     * @param int $curlCode Code from https://curl.haxx.se/libcurl/c/libcurl-errors.html
+     * @param string $curlMessage
+     * @return CurlException
+     */
+    public static function createCurlException($curlCode, $curlMessage) {
+        switch ($curlCode) {
+            case CURLE_OPERATION_TIMEDOUT:
+                return new CurlTimedOutException($curlMessage, $curlCode);
+            default:
+                return self::createDefaultException($curlMessage, $curlCode);
+        }
+    }
+
+    /**
+     * Creates default Curl exception
+     *
+     * @param $code
+     * @param $message
+     * @param \Exception|null $prev
+     * @return CurlException
+     */
+    public static function createDefaultException($code, $message, \Exception $prev = null) {
+        return new CurlException('Default exception: ' . $message, $code, $prev);
+    }
+}
\ No newline at end of file
diff --git a/src/Curl/Exception/CurlException.php b/src/Curl/Exception/CurlException.php
new file mode 100644
index 0000000000000000000000000000000000000000..b91149280cf7638046fc1308b43a3eb597012ee9
--- /dev/null
+++ b/src/Curl/Exception/CurlException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace WPDesk\HttpClient\Curl\Exception;
+
+use WPDesk\HttpClient\HttpClientRequestException;
+
+/**
+ * Base class for all curl exceptions.
+ *
+ * @package WPDesk\HttpClient\Curl\Exception
+ */
+class CurlException extends \RuntimeException implements HttpClientRequestException
+{
+
+}
\ No newline at end of file
diff --git a/src/Curl/Exception/CurlTimedOutException.php b/src/Curl/Exception/CurlTimedOutException.php
new file mode 100644
index 0000000000000000000000000000000000000000..dd1dbfd09f3bea307c3de0f5d7d75cdf860d614f
--- /dev/null
+++ b/src/Curl/Exception/CurlTimedOutException.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace WPDesk\HttpClient\Curl\Exception;
+
+class CurlTimedOutException extends CurlException
+{
+
+}
\ No newline at end of file
diff --git a/src/HttpClient.php b/src/HttpClient.php
new file mode 100644
index 0000000000000000000000000000000000000000..42bdae521cdfc74862847e686b3e1a5db24654ab
--- /dev/null
+++ b/src/HttpClient.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace WPDesk\HttpClient;
+
+
+interface HttpClient
+{
+    /**
+     * @param string $url
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @throw HttpClientRequestException
+     * @return HttpClientResponse
+     */
+    public function put($url, $body, array $headers, $timeOut);
+
+    /**
+     * @param string $url
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @throw HttpClientRequestException
+     * @return HttpClientResponse
+     */
+    public function get($url, $body, array $headers, $timeOut);
+
+    /**
+     * @param string $url
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @throw HttpClientRequestException
+     * @return HttpClientResponse
+     */
+    public function post($url, $body, array $headers, $timeOut);
+
+    /**
+     * @param string $url
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @throw HttpClientRequestException
+     * @return HttpClientResponse
+     */
+    public function delete($url, $body, array $headers, $timeOut);
+
+    /**
+     * @param string $url
+     * @param string $method
+     * @param string $body
+     * @param array $headers
+     * @param int $timeOut
+     * @throw HttpClientRequestException
+     * @return HttpClientResponse
+     */
+    public function send($url, $method, $body, array $headers, $timeOut);
+}
\ No newline at end of file
diff --git a/src/HttpClientFactory.php b/src/HttpClientFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2acdae2fa369d6b61cbaddf3a704ce535f727fc
--- /dev/null
+++ b/src/HttpClientFactory.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace WPDesk\HttpClient;
+
+
+class HttpClientFactory
+{
+    /**
+     * @param HttpClientOptions $options
+     * @return HttpClient
+     */
+    public function createClient(HttpClientOptions $options)
+    {
+        $className = $options->getHttpClientClass();
+        return new $className;
+    }
+}
\ No newline at end of file
diff --git a/src/HttpClientOptions.php b/src/HttpClientOptions.php
new file mode 100644
index 0000000000000000000000000000000000000000..c58d70af36dfe9a42f1c57b86085534e31a002b0
--- /dev/null
+++ b/src/HttpClientOptions.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace WPDesk\HttpClient;
+
+interface HttpClientOptions
+{
+    /**
+     * @return string
+     */
+    public function getHttpClientClass();
+}
\ No newline at end of file
diff --git a/src/HttpClientRequestException.php b/src/HttpClientRequestException.php
new file mode 100644
index 0000000000000000000000000000000000000000..50cb789fd220d58e35d9ffb8d8969ef2bf7ae7f0
--- /dev/null
+++ b/src/HttpClientRequestException.php
@@ -0,0 +1,12 @@
+<?php
+namespace WPDesk\HttpClient;
+
+/**
+ * Interface for all exceptions that can be thrown by HttpClient
+ *
+ * @package WPDesk\HttpClient
+ */
+interface HttpClientRequestException
+{
+
+}
\ No newline at end of file
diff --git a/src/HttpClientResponse.php b/src/HttpClientResponse.php
new file mode 100644
index 0000000000000000000000000000000000000000..292e059e6f28b59835650ad88012a8eae28c94c4
--- /dev/null
+++ b/src/HttpClientResponse.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace WPDesk\HttpClient;
+
+
+class HttpClientResponse
+{
+    /** @var  string */
+    private $headers;
+
+    /** @var  string */
+    private $body;
+
+    /** @var int */
+    private $code;
+
+    /**
+     * HttpClientResponse constructor.
+     * @param string $headers
+     * @param string $body
+     * @param int $code
+     */
+    public function __construct($headers, $body, $code)
+    {
+        $this->headers = $headers;
+        $this->body = $body;
+        $this->code = $code;
+    }
+
+    /**
+     * @return array
+     */
+    public function getHeaders()
+    {
+        $headers = array();
+        $headers_rows = explode("\r\n", $this->headers);
+        foreach ($headers_rows as $headers_row) {
+            $header = explode(": ", $headers_row);
+            $headers[$header[0]] = isset($header[1])?$header[1]:$header[0];
+        }
+        return $headers;
+    }
+
+    /**
+     * @return string
+     */
+    public function getBody()
+    {
+        return $this->body;
+    }
+
+    /**
+     * @return int
+     */
+    public function getResponseCode()
+    {
+        return $this->code;
+    }
+}
\ No newline at end of file
diff --git a/tests/docker-compose.yaml b/tests/docker-compose.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..2a86b036f682ed318b27b028d2e96dfcd5979afd
--- /dev/null
+++ b/tests/docker-compose.yaml
@@ -0,0 +1,172 @@
+version: '2.0'
+
+services:
+
+  wordpress:
+    image: wpdesknet/phpunit-woocommerce:0-0
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql0
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql0
+
+  wordpress-0-1:
+    image: wpdesknet/phpunit-woocommerce:0-1
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql1
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql1
+
+  wordpress-0-2:
+    image: wpdesknet/phpunit-woocommerce:0-2
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql2
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql2
+
+  wordpress-0-3:
+    image: wpdesknet/phpunit-woocommerce:0-3
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql3
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql3
+
+  wordpress-0-4:
+    image: wpdesknet/phpunit-woocommerce:0-4
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql4
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql4
+
+  wordpress-0-5:
+    image: wpdesknet/phpunit-woocommerce:0-5
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql5
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql5
+
+  wordpress-1-0:
+    image: wpdesknet/phpunit-woocommerce:1-0
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql0
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql0
+
+  wordpress-2-0:
+    image: wpdesknet/phpunit-woocommerce:2-0
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql0
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql0
+
+  wordpress-3-0:
+    image: wpdesknet/phpunit-woocommerce:3-0
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql0
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql0
+
+  wordpress-4-0:
+    image: wpdesknet/phpunit-woocommerce:4-0
+    volumes:
+    - .././:/opt/project
+    depends_on:
+    - mysql0
+    environment:
+      WORDPRESS_DB_NAME: wptest
+      WORDPRESS_DB_USER: mysql
+      WORDPRESS_DB_PASSWORD: mysql
+      WORDPRESS_DB_HOST: mysql0
+
+  mysql0:
+    image: mysql:5.7
+    environment:
+      MYSQL_ROOT_PASSWORD: mysql
+      MYSQL_DATABASE: wptest
+      MYSQL_USER: mysql
+      MYSQL_PASSWORD: mysql
+
+  mysql1:
+    image: mysql:5.7
+    environment:
+      MYSQL_ROOT_PASSWORD: mysql
+      MYSQL_DATABASE: wptest
+      MYSQL_USER: mysql
+      MYSQL_PASSWORD: mysql
+
+  mysql2:
+    image: mysql:5.7
+    environment:
+      MYSQL_ROOT_PASSWORD: mysql
+      MYSQL_DATABASE: wptest
+      MYSQL_USER: mysql
+      MYSQL_PASSWORD: mysql
+
+  mysql3:
+    image: mysql:5.7
+    environment:
+      MYSQL_ROOT_PASSWORD: mysql
+      MYSQL_DATABASE: wptest
+      MYSQL_USER: mysql
+      MYSQL_PASSWORD: mysql
+
+  mysql4:
+    image: mysql:5.7
+    environment:
+      MYSQL_ROOT_PASSWORD: mysql
+      MYSQL_DATABASE: wptest
+      MYSQL_USER: mysql
+      MYSQL_PASSWORD: mysql
+
+  mysql5:
+    image: mysql:5.7
+    environment:
+      MYSQL_ROOT_PASSWORD: mysql
+      MYSQL_DATABASE: wptest
+      MYSQL_USER: mysql
+      MYSQL_PASSWORD: mysql
+
diff --git a/tests/integration/bootstrap.php b/tests/integration/bootstrap.php
new file mode 100644
index 0000000000000000000000000000000000000000..a422fd9c7fea652a15b2aced0b2e6134a5590201
--- /dev/null
+++ b/tests/integration/bootstrap.php
@@ -0,0 +1,28 @@
+<?php
+
+ini_set('error_reporting', E_ALL); // or error_reporting(E_ALL);
+ini_set('display_errors', '1');
+ini_set('display_startup_errors', '1');
+
+require_once __DIR__ . '/../../vendor/autoload.php';
+
+// disable xdebug backtrace
+if ( function_exists( 'xdebug_disable' ) ) {
+	xdebug_disable();
+}
+
+if ( getenv( 'PLUGIN_PATH' ) !== false ) {
+	define( 'PLUGIN_PATH', getenv( 'PLUGIN_PATH' ) );
+} else {
+	define( 'PLUGIN_PATH', __DIR__ . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR );
+}
+
+require_once( getenv( 'WP_DEVELOP_DIR' ) . '/tests/phpunit/includes/functions.php' );
+
+tests_add_filter( 'muplugins_loaded', function () {
+}, 100 );
+
+putenv('WP_TESTS_DIR=' . getenv( 'WP_DEVELOP_DIR' ) . '/tests/phpunit');
+require_once( getenv( 'WC_DEVELOP_DIR' ) . '/tests/bootstrap.php' );
+
+do_action('plugins_loaded');
\ No newline at end of file
diff --git a/tests/unit/TestHttpClientFactory.php b/tests/unit/TestHttpClientFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b835f8f423ca926d7f226d5fd355ea70214ba80
--- /dev/null
+++ b/tests/unit/TestHttpClientFactory.php
@@ -0,0 +1,6 @@
+<?php
+
+class TestHttpClientFactory extends \PHPUnit\Framework\TestCase
+{
+
+}
\ No newline at end of file
diff --git a/tests/unit/bootstrap.php b/tests/unit/bootstrap.php
new file mode 100644
index 0000000000000000000000000000000000000000..76b8109582ae17560b77a6e0499b232e09047810
--- /dev/null
+++ b/tests/unit/bootstrap.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * PHPUnit bootstrap file
+ */
+
+require_once __DIR__ . '/../../vendor/autoload.php';
+
+WP_Mock::setUsePatchwork( true );
+WP_Mock::bootstrap();