protected function sendRequest($parameters = array(), $curlOptOverride = array())
    {
        if (!empty($this->shopNumber)) {
            $parameters['shop_number'] = $this->shopNumber;
        }
        $parameters             = !empty($parameters)
            ? array_merge($this->authService->getAuthPostParams(), $parameters)
            : $this->authService->getAuthPostParams();
        $parameters['trace_id'] = 'spa-' . uniqid();

        $this->log(
            'Sending request to "' . $this->apiUrl . '": ' . ShopgateLogger::getInstance()->cleanParamsForLog(
                $parameters
            ),
            ShopgateLogger::LOGTYPE_REQUEST
        );

        // init new auth session and generate cURL options
        $this->authService->startNewSession();
        $curlOpt = $this->getCurlOptArray($curlOptOverride);

        // init cURL connection and send the request
        $curl = curl_init($this->apiUrl);
        curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($parameters));
        curl_setopt_array($curl, $curlOpt);
        $response = curl_exec($curl);
        curl_close($curl);

        // check the result
        if (!$response) {
            // exception without logging - this might cause spamming your logs and we will know when our API is offline anyways
            throw new ShopgateLibraryException(
                ShopgateLibraryException::MERCHANT_API_NO_CONNECTION,
                null,
                false,
                false
            );
        }

        $decodedResponse = $this->jsonDecode($response, true);

        if (empty($decodedResponse)) {
            // exception without logging - this might cause spamming your logs and we will know when our API is offline anyways
            throw new ShopgateLibraryException(
                ShopgateLibraryException::MERCHANT_API_INVALID_RESPONSE,
                'Response: ' . $response,
                true,
                false
            );
        }

        $responseObject = new ShopgateMerchantApiResponse($decodedResponse);

        if ($decodedResponse['error'] != 0) {
            throw new ShopgateMerchantApiException(
                $decodedResponse['error'],
                $decodedResponse['error_text'],
                $responseObject
            );
        }

        return $responseObject;
    }