Skip to content

Commit

Permalink
Merge pull request #24 from Fyntasia/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Fyntasia authored Dec 18, 2018
2 parents be0d6d8 + 308201b commit 8226a23
Show file tree
Hide file tree
Showing 94 changed files with 3,217 additions and 659 deletions.
146 changes: 85 additions & 61 deletions EffectConnectSDK/ApiCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
namespace EffectConnect\PHPSdk;

use EffectConnect\PHPSdk\Core\Exception\InvalidPropertyException;
use EffectConnect\PHPSdk\Core\Exception\MissingCertificateFileException;
use EffectConnect\PHPSdk\Core\Exception\MissingCertificateLocationException;
use EffectConnect\PHPSdk\Core\Helper\Payload;
use EffectConnect\PHPSdk\Core\Interfaces\ApiModelInterface;
use EffectConnect\PHPSdk\Core\Interfaces\CallTypeInterface;
use EffectConnect\PHPSdk\Core\Abstracts\ApiModel;
use EffectConnect\PHPSdk\Core\Interfaces\ResponseContainerInterface;
use EffectConnect\PHPSdk\Core\Model\Response\ApiResponseContainer;
use EffectConnect\PHPSdk\Core\Model\Response\ErrorContainer;
use EffectConnect\PHPSdk\Core\Model\Response\Request;
use EffectConnect\PHPSdk\Core\Model\Response\Response;

/**
* Class ApiCall
Expand All @@ -16,11 +20,9 @@
* @package EffectConnectSDK
*
*/
class ApiCall
final class ApiCall
{
const API_ENDPOINT = 'https://submit.effectconnect.com/v1';
const CERTIFICATE_LOCATION = '';

const API_ENDPOINT = 'https://submit.effectconnect.com/v1';
/**
* @var \DateTime $_callDate
*/
Expand All @@ -31,16 +33,6 @@ class ApiCall
*/
protected $_callVersion = '1.0';

/**
* @var int $_curlErrNo
*/
protected $_curlErrNo;

/**
* @var array $_curlErrors
*/
protected $_curlErrors = [];

/**
* @var array $_curlInfo
*/
Expand All @@ -51,6 +43,11 @@ class ApiCall
*/
protected $_curlResponse;

/**
* @var array $_errors
*/
protected $_errors = [];

/**
* @var array $_headers
*/
Expand Down Expand Up @@ -79,18 +76,24 @@ class ApiCall

/**
* @var string $_responseType
*
*/
protected $_responseType = CallTypeInterface::RESPONSE_TYPE_XML;

/**
* @var string $_parseCallback
*/
protected $_parseCallback;

/**
* @var string $_secretKey
*/
protected $_secretKey;

/**
* @var int $_timeout
*/
protected $_timeout = 3;

/**
* @var string $_uri
*
Expand All @@ -100,9 +103,6 @@ class ApiCall

/**
* @return ApiCall
*
* @throws MissingCertificateFileException
* @throws MissingCertificateLocationException
*/
public function call()
{
Expand All @@ -118,75 +118,99 @@ public function call()
*/
$postFields = [$this->_payload];
}
if (!self::CERTIFICATE_LOCATION)
{
throw new MissingCertificateLocationException();
}
if (!file_exists(self::CERTIFICATE_LOCATION))
{
throw new MissingCertificateFileException(self::CERTIFICATE_LOCATION);
}
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => $this->_getHeaders(),
CURLOPT_URL => self::API_ENDPOINT.$this->_uri,
CURLOPT_CUSTOMREQUEST => $this->_method,
CURLOPT_TIMEOUT => $this->_timeout,
CURLOPT_POSTFIELDS => $postFields,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CAINFO => self::CERTIFICATE_LOCATION
CURLOPT_RETURNTRANSFER => true
]);
$this->_curlResponse = curl_exec($ch);
$this->_curlErrors[] = curl_error($ch);
$this->_curlErrNo = (int)curl_errno($ch);
$this->_curlInfo = curl_getinfo($ch);
if (($curlError = curl_error($ch)) !== '')
{
$this->_errors[] = curl_error($ch);
}
if (($errNo = (int)curl_errno($ch)) > 0)
{
$this->_errors[] = sprintf('Curl error %d', $errNo);
}
if (($this->_curlResponse = curl_exec($ch)) === '')
{
$this->_errors[] = sprintf('No response received: `%s`',
($errNo === CURLE_OPERATION_TIMEDOUT?'Operation timed out. Extend your timeout (ApiCall::setTimeout()).':'Unknown reason')
);
}
$this->_curlInfo = curl_getinfo($ch);
curl_close($ch);
if ((int)$this->_curlInfo['http_code'] !== 200)
{
$this->_errors[] = sprintf('Invalid http code: `%d`', (int)$this->_curlInfo['http_code']);
}

return $this;
}

/**
* @param string $parseCallback
*
* @return ApiCall
*/
public function setParseCallback($parseCallback)
{
$this->_parseCallback = $parseCallback;

return $this;
}


/**
* @return array
*/
public function getCurlErrors()
public function getErrors()
{
if ($this->_curlErrNo > 0)
{
$this->_curlErrors[] = sprintf('Curl error %d', $this->_curlErrNo);
}
return $this->_curlErrors;
return $this->_errors;
}

/**
* @return string
*/
public function getCurlResponse()
public function getRawResponse()
{
return $this->_curlResponse;
}

/**
* @return ApiResponseContainer
*/
public function getResponseContainer()
{
switch ($this->_responseType)
$payload = json_decode($this->_curlResponse, true);
if (json_last_error() !== JSON_ERROR_NONE)
{
$payload = simplexml_load_string($this->_curlResponse);
}
$responsePayload = Payload::extract($payload, 'Response');
$response = (new Response())->setResult(Payload::extract($responsePayload, 'Result'));
$container = (new ApiResponseContainer())->setRequest(new Request(Payload::extract($payload, 'Request')));
if (($responseContainer = call_user_func_array($this->_parseCallback, [$this->_method, $responsePayload])) instanceof ResponseContainerInterface)
{
case CallTypeInterface::RESPONSE_TYPE_XML:
$dom = new \DOMDocument();
$dom->loadXML($this->_curlResponse);
$dom->formatOutput = true;
$dom->preserveWhiteSpace = false;
return $dom->saveXML();
break;
case CallTypeInterface::RESPONSE_TYPE_JSON:
return '<pre>'.print_r($this->_curlResponse, true).'</pre>';
break;
default:
return $this->_curlResponse;
break;
$response->setResponseContainer($responseContainer);
}
$container
->setResponse($response)
->setErrorContainer(new ErrorContainer(Payload::extract($payload, 'ErrorContainer', true)))
;

return $container;
}

/**
* @return bool
*/
public function isSuccess()
{
return (count($this->_curlErrors) === 0 && $this->_curlErrNo === 0 && $this->_curlResponse !== '');
return (count($this->_errors) === 0);
}

/**
Expand Down Expand Up @@ -238,14 +262,14 @@ public function setMethod($method)
}

/**
* @param \EffectConnect\PHPSdk\Core\Abstracts\ApiModel $payload
* @param ApiModelInterface|\CURLFile|null $payload
*
* @return $this
* @return ApiCall
* @throws InvalidPropertyException
*/
public function setPayload($payload=null)
{
if ($payload instanceof ApiModel)
if ($payload instanceof ApiModelInterface)
{
$this->_payload = $payload->getXml();
} elseif ($payload instanceof \CURLFile)
Expand Down
45 changes: 3 additions & 42 deletions EffectConnectSDK/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@
use EffectConnect\PHPSdk\Core\CallType\ProductsCall;
use EffectConnect\PHPSdk\Core\CallType\ReportCall;
use EffectConnect\PHPSdk\Core\Exception\InvalidApiCallException;
use EffectConnect\PHPSdk\Core\Exception\InvalidKeyException;
use EffectConnect\PHPSdk\Core\Exception\InvalidSignatureException;
use EffectConnect\PHPSdk\Core\Exception\MissingArgumentException;
use EffectConnect\PHPSdk\Core\Helper\Keychain;
use EffectConnect\PHPSdk\Core\Interfaces\CallTypeInterface;
use EffectConnect\PHPSdk\Core\Model\EffectConnectEvent;

/**
* Class Core
Expand Down Expand Up @@ -62,43 +58,6 @@ public function __construct(Keychain $keychain, ApiCall $callClass=null)
$this->_callClass = $callClass;
}

/**
* @return EffectConnectEvent
*
* @throws InvalidKeyException
* @throws InvalidSignatureException
* @throws MissingArgumentException
*/
final public function readEvent()
{
$publicKey = $_SERVER['HTTP_KEY'];
if ($publicKey !== $this->_keychain->getPublicKey())
{
throw new InvalidKeyException('Public');
}
$event = $_SERVER['HTTP_EVENT'];
$timestamp = $_SERVER['HTTP_TIME'];
$signature = $_SERVER['HTTP_SIGNATURE'];
$digest = [$publicKey, $event, $timestamp];
$signatureValidation = base64_encode(hash_hmac('sha512', implode('', $digest), $this->_keychain->getSecretKey(), true));
if ($signature !== $signatureValidation)
{
throw new InvalidSignatureException();
}
if (!array_key_exists('eventPayload', $_POST) || $_POST['eventPayload'] === '')
{
throw new MissingArgumentException('$_POST["eventPayload"]');
}
$message = base64_decode($_POST['eventPayload'], true);
$size = openssl_cipher_iv_length('aes-256-ctr');
$nonce = mb_substr($message, 0, $size, '8bit');
$encrypted = mb_substr($message, $size, null, '8bit');
$decrypted = openssl_decrypt($encrypted, 'aes-256-ctr', hex2bin(md5($signature.$this->_keychain->getSecretKey())), OPENSSL_RAW_DATA, $nonce);
$eventModel = new EffectConnectEvent($event, $decrypted, new \DateTime($timestamp, new \DateTimeZone('Europe/Amsterdam')));

return $eventModel;
}

/**
* @param $name
* @param $arguments
Expand All @@ -111,8 +70,10 @@ final public function __call($name, $arguments)
try
{
$reflection = new \ReflectionClass('EffectConnect\PHPSdk\Core\CallType\\'.$name);
/** @var CallTypeInterface $callType */
$callType = $reflection->newInstanceArgs([$this->_keychain, $this->_callClass]);

return $reflection->newInstanceArgs([$this->_keychain, $this->_callClass]);
return $callType;
} catch (\Exception $exception)
{
throw new InvalidApiCallException($name);
Expand Down
40 changes: 6 additions & 34 deletions EffectConnectSDK/Core/Abstracts/ApiModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,6 @@
*/
abstract class ApiModel
{
/**
* @param array|null $productData
*/
public function __construct($productData=null)
{
if ($productData)
{
$this->_hydrate($productData);
}
}

/**
* @param array $productData
*/
private function _hydrate($productData)
{
foreach ($productData as $property => $value)
{
if (property_exists($this, '_'.$property))
{
call_user_func([$this, 'set'.ucfirst($property)], $value);
}
}
}

/**
* @return string
*
Expand Down Expand Up @@ -140,19 +115,16 @@ public function getXml()
return $xmlPayload->asXML();
}

public function __get($name)
{
if(property_exists($this, $name))
{
return $this->{'_'.$name};
}
return null;
}

/**
* @return bool
*/
protected function isIterator()
{
return false;
}

/**
* @return string
*/
public abstract function getName();
}
Loading

0 comments on commit 8226a23

Please sign in to comment.