Last active
February 25, 2016 10:31
-
-
Save sobstel/b1304d26503c6ef0114e to your computer and use it in GitHub Desktop.
Component\AdyenCreditCard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Component\AdyenCreditCard; | |
use Component\AdyenCreditCard\DataService\ResponseLogDataService; | |
use GuzzleHttp\Client; | |
use GuzzleHttp\Exception\RequestException; | |
class PaymentHandler | |
{ | |
/** | |
* @var array | |
*/ | |
protected $params = []; | |
/** | |
* @var Client | |
*/ | |
protected $client; | |
/** | |
* @var ResponseLogDataService | |
*/ | |
protected $responseLogDataService; | |
/** | |
* @param array $params | |
*/ | |
public function __construct(array $params) | |
{ | |
foreach (['authoriseUrl', 'username', 'password', 'merchantAccount'] as $name) { | |
if (!isset($params[$name])) { | |
throw new \Exception(sprintf('Missing param: "%s"', $name)); | |
} | |
} | |
$this->params = $params; | |
} | |
/** | |
* @param Client $client | |
*/ | |
public function setClient(Client $client) | |
{ | |
$this->client = $client; | |
} | |
/** | |
* @return Client | |
*/ | |
public function getClient() | |
{ | |
if (!$this->client) { | |
$this->client = new Client; | |
} | |
return $this->client; | |
} | |
/** | |
* @param ResponseLogDataService | |
*/ | |
public function setResponseLogDataService(ResponseLogDataService $responseLogDataService) | |
{ | |
$this->responseLogDataService = $responseLogDataService; | |
} | |
/** | |
* @param array $data | |
* @return PaymentResponse | |
*/ | |
public function handle(array $data) | |
{ | |
$authoriseUrl = $this->params['authoriseUrl']; | |
$authData = [$this->params['username'], $this->params['password']]; | |
$merchantAccount = $this->params['merchantAccount']; | |
$paymentData = [ | |
'merchantAccount' => $merchantAccount, | |
'amount' => [ | |
'currency' => 'EUR', | |
// adyen amount format: x/100 = x EUR, eg. 1500 = 15 EUR | |
'value' => $data['amount'] * 100, | |
], | |
'reference' => $data['reference'], | |
'shopperIP' => $data['userIp'], | |
'shopperEmail' => $data['userEmail'], | |
'additionalData' => [ | |
'card.encrypted.json' => $data['encryptedData'] | |
], | |
]; | |
try { | |
$response = $this->getClient()->post($authoriseUrl, ['auth' => $authData, 'json' => $paymentData]); | |
} catch (RequestException $e) { | |
$response = $e->getResponse(); | |
} | |
$responseBody = $response->getBody(); | |
$responseData = json_decode($responseBody, true); | |
$statusCode = $response->getStatusCode(); | |
$resultCode = (isset($responseData['resultCode']) ? $responseData['resultCode'] : null); | |
if ($this->responseLogDataService) { | |
$this->responseLogDataService->logResponse( | |
$data['reference'], | |
(isset($responseData['pspReference']) ? $responseData['pspReference'] : null), | |
$statusCode, | |
$responseBody | |
); | |
} | |
if ($statusCode != PaymentStatusEnum::OK) { | |
$errorCode = (isset($responseData['errorCode']) ? $responseData['errorCode'] : null); | |
return new PaymentResult(false, $errorCode); | |
} | |
if ($resultCode != PaymentStateEnum::AUTHORISED) { | |
return new PaymentResult(false, $resultCode); | |
} | |
return new PaymentResult(true); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Component\AdyenCreditCard; | |
use Component\AdyenCreditCard\PaymentHandler; | |
use Hamcrest; | |
use Mockery; | |
class PaymentHandlerTest extends \PHPUnit_Framework_TestCase | |
{ | |
public function testDataCorrectlyFormattedForAdyen() | |
{ | |
$params = $this->getParams(); | |
$data = $this->getData(); | |
$expectedPaymentData = [ | |
'merchantAccount' => $params['merchantAccount'], | |
'amount' => [ | |
'currency' => 'EUR', | |
'value' => 6600, | |
], | |
'reference' => $data['reference'], | |
'shopperIP' => $data['userIp'], | |
'shopperEmail' => $data['userEmail'], | |
'additionalData' => [ | |
'card.encrypted.json' => $data['encryptedData'] | |
], | |
]; | |
$responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface'); | |
$responseMock | |
->shouldReceive('getBody') | |
->andReturn('{}') | |
->shouldReceive('getStatusCode') | |
->andReturn(null); | |
$clientMock = Mockery::mock('GuzzleHttp\Client'); | |
$clientMock | |
->shouldReceive('post') | |
->withArgs([$params['authoriseUrl'], ['auth' => [$params['username'], $params['password']], 'json' => $expectedPaymentData]]) | |
->andReturn($responseMock) | |
; | |
$paymentHandler = new PaymentHandler($params); | |
$paymentHandler->setClient($clientMock); | |
$paymentHandler->handle($data); | |
} | |
public function testInvalidTransaction() | |
{ | |
$params = $this->getParams(); | |
$data = $this->getData(); | |
$responseBody = '{"status":422,"errorCode":"101","message":"Invalid card number","errorType":"validation","pspReference":"8814393041104109"}'; | |
$responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface'); | |
$responseMock | |
->shouldReceive('getBody') | |
->andReturn($responseBody) | |
->shouldReceive('getStatusCode') | |
->andReturn(422); | |
$clientMock = Mockery::mock('GuzzleHttp\Client'); | |
$clientMock | |
->shouldReceive('post') | |
->andReturn($responseMock) | |
; | |
$paymentHandler = new PaymentHandler($params); | |
$paymentHandler->setClient($clientMock); | |
$paymentResult = $paymentHandler->handle($data); | |
assertThat($paymentResult->isSuccess(), equalTo(false)); | |
assertThat($paymentResult->getCode(), equalTo(101)); | |
} | |
public function testRefusedTransaction() | |
{ | |
$params = $this->getParams(); | |
$data = $this->getData(); | |
$responseBody = '{"pspReference":"7914393044485464","refusalReason":"Refused","resultCode":"Refused"}'; | |
$responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface'); | |
$responseMock | |
->shouldReceive('getBody') | |
->andReturn($responseBody) | |
->shouldReceive('getStatusCode') | |
->andReturn(200); | |
$clientMock = Mockery::mock('GuzzleHttp\Client'); | |
$clientMock | |
->shouldReceive('post') | |
->andReturn($responseMock) | |
; | |
$paymentHandler = new PaymentHandler($params); | |
$paymentHandler->setClient($clientMock); | |
$paymentResult = $paymentHandler->handle($data); | |
assertThat($paymentResult->isSuccess(), equalTo(false)); | |
assertThat($paymentResult->getCode(), equalTo('Refused')); | |
} | |
public function testSuccessfulTransaction() | |
{ | |
$params = $this->getParams(); | |
$data = $this->getData(); | |
$responseBody = '{"pspReference":"7914388654786011","resultCode":"Authorised","authCode":"32682"}'; | |
$responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface'); | |
$responseMock | |
->shouldReceive('getBody') | |
->andReturn($responseBody) | |
->shouldReceive('getStatusCode') | |
->andReturn(200); | |
$clientMock = Mockery::mock('GuzzleHttp\Client'); | |
$clientMock | |
->shouldReceive('post') | |
->andReturn($responseMock) | |
; | |
$paymentHandler = new PaymentHandler($params); | |
$paymentHandler->setClient($clientMock); | |
$paymentResult = $paymentHandler->handle($data); | |
assertThat($paymentResult->isSuccess(), equalTo(true)); | |
} | |
protected function getParams() | |
{ | |
return [ | |
'authoriseUrl' => 'http://url', | |
'username' => 'user', | |
'password' => 'secretpass', | |
'merchantAccount' => 'BestMerchantEver' | |
]; | |
} | |
protected function getData() | |
{ | |
return [ | |
'encryptedData' => 'adyenjs_0_1_13$abcxyz0123456789', | |
'amount' => 66, | |
'reference' => uniqid(), | |
'userIp' => '127.0.0.1', | |
'userEmail' => 'protipster@localhost' | |
]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Component\AdyenCreditCard; | |
class PaymentResult | |
{ | |
/** | |
* @param bool | |
*/ | |
protected $success; | |
/** | |
* @param int | |
*/ | |
protected $code; | |
/** | |
* @param string $message | |
* @param int $errorCode | |
*/ | |
public function __construct($success, $code = null) | |
{ | |
$this->success = (bool)$success; | |
$this->code = $code; | |
} | |
/** | |
* @return bool | |
*/ | |
public function isSuccess() | |
{ | |
return $this->success; | |
} | |
/** | |
* @return bool | |
*/ | |
public function isError() | |
{ | |
return (!$this->success); | |
} | |
/** | |
* @return int | |
*/ | |
public function getCode() | |
{ | |
return $this->code; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Component\AdyenCreditCard; | |
/** | |
* State of nomrally processed request | |
*/ | |
class PaymentStateEnum | |
{ | |
/** | |
* Authorised (approved by the financial institution) | |
*/ | |
const AUTHORISED = 'Authorised'; | |
/** | |
* Refused (payment declined by the financial institution) | |
*/ | |
const REFUSED = 'Refused'; | |
/** | |
* Error (communication error with the financial institution) | |
*/ | |
const ERROR = 'Error'; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Component\AdyenCreditCard; | |
/** | |
* Adyen payment status (it's HTTP code) | |
*/ | |
class PaymentStatusEnum | |
{ | |
/** | |
* Request processed normally | |
*/ | |
const OK = 200; | |
/** | |
* Problem reading or understanding request | |
*/ | |
const BAD_REQUEST = 400; | |
/** | |
* Authentication required | |
*/ | |
const UNAUTHORIZED = 401; | |
/** | |
* Insufficient permission to process request | |
*/ | |
const FORBIDDEN = 403; | |
/** | |
* Request validation error | |
*/ | |
const VALIDATION_ERROR = 422; | |
/** | |
* Server could not process request | |
*/ | |
const SERVER_ERROR = 500; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Component\AdyenCreditCard; | |
use Doctrine\ORM\EntityManager; | |
class ResponseLogDataService | |
{ | |
/** | |
* @var EntityManager | |
*/ | |
protected $entityManager; | |
/** | |
* @param EntityManager $entityManager | |
*/ | |
public function __construct(EntityManager $entityManager) | |
{ | |
$this->setEntityManager($entityManager); | |
} | |
/** | |
* @param EntityManager $entityManager | |
*/ | |
protected function setEntityManager(EntityManager $entityManager) | |
{ | |
$this->entityManager = $entityManager; | |
} | |
/** | |
* @return EntityManager | |
*/ | |
protected function getEntityManager() | |
{ | |
return $this->entityManager; | |
} | |
/** | |
* Log response returned by Adyen API | |
* | |
* @param string $externalId | |
* @param int $pspReference | |
* @param int $statusCode | |
* @param string $responseBody | |
*/ | |
public function logResponse($externalId, $pspReference, $statusCode, $responseBody) | |
{ | |
$conn = $this->getEntityManager()->getConnection(); | |
$qb = $conn->createQueryBuilder(); | |
$qb | |
->insert('transaction_adyen_log') | |
->values([ | |
'external_id' => ':externalId', | |
'psp_reference' => ':pspReference', | |
'status_code' => ':statusCode', | |
'response_body' => ':responseBody', | |
'date' => 'NOW()' | |
]) | |
->setParameter('externalId', $externalId) | |
->setParameter('pspReference', $pspReference) | |
->setParameter('statusCode', $statusCode) | |
->setParameter('responseBody', $responseBody) | |
->execute(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment