Skip to content

Instantly share code, notes, and snippets.

@sobstel
Last active February 25, 2016 10:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sobstel/b1304d26503c6ef0114e to your computer and use it in GitHub Desktop.
Save sobstel/b1304d26503c6ef0114e to your computer and use it in GitHub Desktop.
Component\AdyenCreditCard
<?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);
}
}
<?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'
];
}
}
<?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;
}
}
<?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';
}
<?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;
}
<?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