diff --git a/src/Action/Api/ApiRequestAction.php b/src/Action/Api/ApiRequestAction.php index 2bae3b9..328cbc5 100644 --- a/src/Action/Api/ApiRequestAction.php +++ b/src/Action/Api/ApiRequestAction.php @@ -2,7 +2,9 @@ namespace Ekyna\Component\Payum\Payzen\Action\Api; +use Ekyna\Component\Payum\Payzen\Api\TransactionIdInterface; use Ekyna\Component\Payum\Payzen\Request\Request; +use Exception; use Payum\Core\Bridge\Spl\ArrayObject; use Payum\Core\Exception\RequestNotSupportedException; use Payum\Core\Reply\HttpRedirect; @@ -14,10 +16,21 @@ */ class ApiRequestAction extends AbstractApiAction { + /** + * @var TransactionIdInterface + */ + private $transactionId; + + public function __construct( + TransactionIdInterface $transactionId + ) { + $this->transactionId = $transactionId; + } + /** * @inheritdoc * - * @throws HttpRedirect + * @throws HttpRedirect|Exception */ public function execute($request): void { @@ -30,12 +43,20 @@ public function execute($request): void return; } - $model['vads_trans_id'] = $this->api->getTransactionId(); - // Current UTC date time - $model['vads_trans_date'] = (new \DateTime('now', new \DateTimeZone('UTC')))->format('YmdHis'); - $data = $model->getArrayCopy(); + // If your application needs to generate a trans_id before this request + if (isset($data['generated_vads_trans_id']) && isset($data['generated_vads_trans_date'])) { + $data['vads_trans_id'] = $data['generated_vads_trans_id']; + $data['vads_trans_date'] = $data['generated_vads_trans_date']; + unset($data['generated_vads_trans_date']); + unset($data['generated_vads_trans_id']); + } else { + //You can generate a trans_id by a file method or a date method, you need to have the same date used to generate the trans_id + $transactionId = $this->transactionId->getTransactionId(); + $data = array_merge($data, $transactionId); + } + $this->logRequestData($data); $url = $this->api->createRequestUrl($data); diff --git a/src/Action/ConvertPaymentAction.php b/src/Action/ConvertPaymentAction.php index 7d905cf..3af4c48 100644 --- a/src/Action/ConvertPaymentAction.php +++ b/src/Action/ConvertPaymentAction.php @@ -36,24 +36,26 @@ public function execute($request) $model = ArrayObject::ensureArrayObject($payment->getDetails()); - if (false == $model['vads_amount']) { + if (!$model['vads_amount']) { $this->gateway->execute($currency = new GetCurrency($payment->getCurrencyCode())); if (2 < $currency->exp) { throw new RuntimeException('Unexpected currency exp.'); } - $divisor = pow(10, 2 - $currency->exp); + // $currecy->exp is the number of decimal required with this currency + $multiplier = pow(10, $currency->exp); $model['vads_currency'] = (string)$currency->numeric; - $model['vads_amount'] = (string)abs($payment->getTotalAmount() / $divisor); + // used to send a non-decimal value to the platform, it can be reverted with currency->exp who be known by Payzen + $model['vads_amount'] = (string)abs($payment->getTotalAmount() * $multiplier); } - if (false == $model['vads_order_id']) { + if (!$model['vads_order_id']) { $model['vads_order_id'] = $payment->getNumber(); } - if (false == $model['vads_cust_id']) { + if (!$model['vads_cust_id']) { $model['vads_cust_id'] = $payment->getClientId(); } - if (false == $model['vads_cust_email']) { + if (!$model['vads_cust_email']) { $model['vads_cust_email'] = $payment->getClientEmail(); } diff --git a/src/Api/Api.php b/src/Api/Api.php index 4e01666..56dd516 100644 --- a/src/Api/Api.php +++ b/src/Api/Api.php @@ -3,7 +3,6 @@ namespace Ekyna\Component\Payum\Payzen\Api; use Payum\Core\Exception\LogicException; -use Payum\Core\Exception\RuntimeException; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -54,55 +53,9 @@ public function setConfig(array $config) ->resolve($config); } - /** - * Returns the next transaction id. - * - * @return string - */ - public function getTransactionId(): string + public function getConfig(): array { - $path = $this->getDirectoryPath() . 'transaction_id'; - - // Create file if not exists - if (!file_exists($path)) { - touch($path); - chmod($path, 0600); - } - - $date = (new \DateTime())->format('Ymd'); - $fileDate = date('Ymd', filemtime($path)); - $isDailyFirstAccess = ($date != $fileDate); - - // Open file - $handle = fopen($path, 'r+'); - if (false === $handle) { - throw new RuntimeException('Failed to open the transaction ID file.'); - } - // Lock File - if (!flock($handle, LOCK_EX)) { - throw new RuntimeException('Failed to lock the transaction ID file.'); - } - - $id = 1; - // If not daily first access, read and increment the id - if (!$isDailyFirstAccess) { - $id = (int)fread($handle, 6); - $id++; - } - - // Truncate, write, unlock and close. - fseek($handle, 0); - ftruncate($handle, 0); - fwrite($handle, (string)$id); - fflush($handle); - flock($handle, LOCK_UN); - fclose($handle); - - if ($this->config['debug']) { - $id += 89000; - } - - return str_pad($id, 6, '0', STR_PAD_LEFT); + return $this->config; } /** @@ -198,26 +151,6 @@ public function generateSignature(array $data, $hashed = true): string return $content; } - /** - * Returns the directory path and creates it if not exists. - * - * @return string - */ - private function getDirectoryPath(): string - { - $path = $this->config['directory']; - - - // Create directory if not exists - if (!is_dir($path)) { - if (!mkdir($path, 0755, true)) { - throw new RuntimeException('Failed to create cache directory'); - } - } - - return $path . DIRECTORY_SEPARATOR; - } - /** * Check that the API has been configured. * diff --git a/src/Api/IdGeneratedByDate.php b/src/Api/IdGeneratedByDate.php new file mode 100644 index 0000000..0231595 --- /dev/null +++ b/src/Api/IdGeneratedByDate.php @@ -0,0 +1,21 @@ +<?php + +namespace Ekyna\Component\Payum\Payzen\Api; + +use Exception; + +class IdGeneratedByDate implements TransactionIdInterface +{ + /** + * @throws Exception + */ + public function getTransactionId(): array + { + $diff = (new \DateTimeImmutable('midnight', new \DateTimeZone('UTC'))) + ->diff(new \DateTimeImmutable('now', new \DateTimeZone('UTC'))); + return [ + 'vads_trans_date' => (new \DateTime('now', new \DateTimeZone('UTC')))->format('YmdHis'), + 'vads_trans_id' => sprintf('%06d', random_int(0, 9) + (($diff->h * 3600 + $diff->i * 60 + $diff->s) * 10)) + ]; + } +} diff --git a/src/Api/IdGeneratedByFile.php b/src/Api/IdGeneratedByFile.php new file mode 100644 index 0000000..6573231 --- /dev/null +++ b/src/Api/IdGeneratedByFile.php @@ -0,0 +1,87 @@ +<?php + +namespace Ekyna\Component\Payum\Payzen\Api; + +use Exception; +use Payum\Core\Exception\RuntimeException; + +class IdGeneratedByFile implements TransactionIdInterface +{ + private $path; + private $debug; + + public function __construct( + string $path, + bool $debug = false + ) { + $this->path = $path; + $this->debug = $debug; + } + + /** + * @throws Exception + */ + public function getTransactionId(): array + { + $this->createDirectoryPath(); + $this->path = rtrim($this->path,DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + $path = $this->path . 'transaction_id'; + + // Create file if not exists + if (!file_exists($path)) { + touch($path); + chmod($path, 0600); + } + + $date = (new \DateTime())->format('Ymd'); + $fileDate = date('Ymd', filemtime($path)); + $isDailyFirstAccess = ($date != $fileDate); + + // Open file + $handle = fopen($path, 'r+'); + if (false === $handle) { + throw new RuntimeException('Failed to open the transaction ID file.'); + } + // Lock File + if (!flock($handle, LOCK_EX)) { + throw new RuntimeException('Failed to lock the transaction ID file.'); + } + + $id = 1; + // If not daily first access, read and increment the id + if (!$isDailyFirstAccess) { + $id = (int)fread($handle, 6); + $id++; + } + + // Truncate, write, unlock and close. + fseek($handle, 0); + ftruncate($handle, 0); + fwrite($handle, (string)$id); + fflush($handle); + flock($handle, LOCK_UN); + fclose($handle); + + if ($this->debug) { + $id += 89000; + } + + return [ + 'vads_trans_date' => (new \DateTime('now', new \DateTimeZone('UTC')))->format('YmdHis'), + 'vads_trans_id' => str_pad($id, 6, '0', STR_PAD_LEFT) + ]; + } + + /** + * Returns the directory path and creates it if not exists. + */ + public function createDirectoryPath(): void + { + // Create directory if not exists + if (!is_dir($this->path)) { + if (!mkdir($this->path, 0755, true)) { + throw new RuntimeException('Failed to create cache directory'); + } + } + } +} diff --git a/src/Api/TransactionIdInterface.php b/src/Api/TransactionIdInterface.php new file mode 100644 index 0000000..1ef68fa --- /dev/null +++ b/src/Api/TransactionIdInterface.php @@ -0,0 +1,9 @@ +<?php + +namespace Ekyna\Component\Payum\Payzen\Api; + +interface TransactionIdInterface +{ + /** @return array{vads_trans_date: string, vads_trans_id: string} */ + public function getTransactionId(): array; +} diff --git a/src/PayzenGatewayFactory.php b/src/PayzenGatewayFactory.php index 534cb0e..2206464 100644 --- a/src/PayzenGatewayFactory.php +++ b/src/PayzenGatewayFactory.php @@ -2,6 +2,8 @@ namespace Ekyna\Component\Payum\Payzen; +use Ekyna\Component\Payum\Payzen\Api\IdGeneratedByFile; +use Ekyna\Component\Payum\Payzen\Api\TransactionIdInterface; use Payum\Core\Bridge\Spl\ArrayObject; use Payum\Core\GatewayFactory; use Payum\Core\GatewayFactoryInterface; @@ -13,17 +15,28 @@ */ class PayzenGatewayFactory extends GatewayFactory { + private $transactionId; + + public function __construct( + array $defaultConfig = array(), + GatewayFactoryInterface $coreGatewayFactory = null, + TransactionIdInterface $transactionId = null + ) { + parent::__construct($defaultConfig, $coreGatewayFactory); + $this->transactionId = $transactionId ?? new IdGeneratedByFile($defaultConfig['directory'] ?? sys_get_temp_dir()); + } + /** * Builds a new factory. * - * @param array $defaultConfig + * @param array $defaultConfig * @param GatewayFactoryInterface|null $coreGatewayFactory - * + * @param TransactionIdInterface|null $transactionId * @return PayzenGatewayFactory */ - public static function build(array $defaultConfig, GatewayFactoryInterface $coreGatewayFactory = null): PayzenGatewayFactory + public static function build(array $defaultConfig, GatewayFactoryInterface $coreGatewayFactory = null, TransactionIdInterface $transactionId = null): PayzenGatewayFactory { - return new static($defaultConfig, $coreGatewayFactory); + return new static($defaultConfig, $coreGatewayFactory, $transactionId); } /** @@ -42,7 +55,7 @@ protected function populateConfig(ArrayObject $config) 'payum.action.refund' => new Action\RefundAction(), 'payum.action.status' => new Action\StatusAction(), 'payum.action.notify' => new Action\NotifyAction(), - 'payum.action.api.request' => new Action\Api\ApiRequestAction(), + 'payum.action.api.request' => new Action\Api\ApiRequestAction($this->transactionId), 'payum.action.api.response' => new Action\Api\ApiResponseAction(), ]); diff --git a/tests/Action/Api/AbstractApiActionTest.php b/tests/Action/Api/AbstractApiActionTest.php index 925fa77..cf6ae70 100644 --- a/tests/Action/Api/AbstractApiActionTest.php +++ b/tests/Action/Api/AbstractApiActionTest.php @@ -6,6 +6,9 @@ use Ekyna\Component\Payum\Payzen\Action\Api\AbstractApiAction; use Ekyna\Component\Payum\Payzen\Api\Api; +use Ekyna\Component\Payum\Payzen\Api\IdGeneratedByDate; +use Ekyna\Component\Payum\Payzen\Api\IdGeneratedByFile; +use Ekyna\Component\Payum\Payzen\Api\TransactionIdInterface; use Ekyna\Component\Payum\Payzen\Tests\Action\AbstractActionTest; use PHPUnit\Framework\MockObject\MockObject; @@ -21,9 +24,14 @@ abstract class AbstractApiActionTest extends AbstractActionTest /** @var MockObject|Api */ protected $api; + /** + * @var MockObject|TransactionIdInterface + */ + protected $transactionIdInterface; + protected function setUp(): void { - $this->action = new $this->actionClass(); + $this->action = new $this->actionClass($this->getIdGeneratedByFile()); $this->action->setApi($this->getApiMock()); } @@ -36,7 +44,7 @@ protected function tearDown(): void /** * @return MockObject|Api */ - protected function getApiMock(): MockObject + protected function getApiMock() { if ($this->api) { return $this->api; @@ -44,4 +52,16 @@ protected function getApiMock(): MockObject return $this->api = $this->getMockBuilder(Api::class)->getMock(); } + + /** + * @return TransactionIdInterface + */ + protected function getIdGeneratedByFile() + { + if ($this->transactionIdInterface) { + return $this->transactionIdInterface; + } + + return $this->transactionIdInterface = new IdGeneratedByFile(dirname(__DIR__, 3) . '/cache/'); + } } diff --git a/tests/Action/Api/ApiRequestActionTest.php b/tests/Action/Api/ApiRequestActionTest.php index 469235b..58c88f1 100644 --- a/tests/Action/Api/ApiRequestActionTest.php +++ b/tests/Action/Api/ApiRequestActionTest.php @@ -6,6 +6,8 @@ use Ekyna\Component\Payum\Payzen\Action\Api\ApiRequestAction; use Ekyna\Component\Payum\Payzen\Request\Request; +use Ekyna\Component\Payum\Payzen\Api\IdGeneratedByFile; +use Ekyna\Component\Payum\Payzen\Api\IdGeneratedByDate; use Payum\Core\Reply\HttpResponse; /** @@ -21,14 +23,15 @@ class ApiRequestActionTest extends AbstractApiActionTest /** * @test + * @throws \Exception */ public function should_set_transaction_id_and_date_and_throw_redirect(): void { $api = $this->getApiMock(); - $api - ->expects(static::once()) - ->method('getTransactionId') - ->willReturn('000001'); + $this->clearCache(); + $transactionIdInterface = $this->getIdGeneratedByFile(); + $transactionId = $transactionIdInterface->getTransactionId(); + $this->assertEquals('000001', $transactionId['vads_trans_id']); $api ->expects(static::once()) @@ -41,4 +44,12 @@ public function should_set_transaction_id_and_date_and_throw_redirect(): void $this->action->execute($request); } + + private function clearCache(): void + { + $path = dirname(__DIR__, 3) . '/cache/transaction_id'; + if (file_exists($path)) { + unlink($path); + } + } } diff --git a/tests/Action/ConvertPaymentActionTest.php b/tests/Action/ConvertPaymentActionTest.php index 444abfd..f0b57e8 100644 --- a/tests/Action/ConvertPaymentActionTest.php +++ b/tests/Action/ConvertPaymentActionTest.php @@ -48,7 +48,7 @@ public function should_convert_payment_to_array(): void $result = $request->getResult(); - $this->assertEquals('1234', $result['vads_amount']); + $this->assertEquals('123400', $result['vads_amount']); $this->assertEquals('978', $result['vads_currency']); $this->assertEquals('O01', $result['vads_order_id']); $this->assertEquals(123, $result['vads_cust_id']); diff --git a/tests/Api/ApiTest.php b/tests/Api/ApiTest.php index 6a9f01a..3161970 100644 --- a/tests/Api/ApiTest.php +++ b/tests/Api/ApiTest.php @@ -39,27 +39,6 @@ public function test_valid_config(): void $this->assertTrue(true); } - public function test_getTransactionId(): void - { - $this->clearCache(); - - $api = $this->createApi(); - - $id = $api->getTransactionId(); - $this->assertEquals('000001', $id); - - $id = $api->getTransactionId(); - $this->assertEquals('000002', $id); - - $id = $api->getTransactionId(); - $this->assertEquals('000003', $id); - - touch(__DIR__ . '/../../cache/transaction_id', time() - 60 * 60 * 24); - - $id = $api->getTransactionId(); - $this->assertEquals('000001', $id); - } - /** * @param string $hashMode * @param array $data @@ -328,7 +307,7 @@ private function createApi(array $config = []): Api 'site_id' => '123456789', 'certificate' => '987654321', 'ctx_mode' => Api::MODE_PRODUCTION, - 'directory' => __DIR__ . '/../../cache', + 'directory' => dirname(__DIR__, 2) . '/cache', ], $config)); return $api; @@ -336,7 +315,7 @@ private function createApi(array $config = []): Api private function clearCache(): void { - $path = __DIR__ . '/../../cache/transaction_id'; + $path = dirname(__DIR__, 2) . '/cache/transaction_id'; if (file_exists($path)) { unlink($path); } diff --git a/tests/Api/IdGeneratedByDateTest.php b/tests/Api/IdGeneratedByDateTest.php new file mode 100644 index 0000000..82dd19e --- /dev/null +++ b/tests/Api/IdGeneratedByDateTest.php @@ -0,0 +1,31 @@ +<?php + +namespace Ekyna\Component\Payum\Payzen\Tests\Api; + +use Ekyna\Component\Payum\Payzen\Api\IdGeneratedByDate; +use Ekyna\Component\Payum\Payzen\Api\TransactionIdInterface; +use Ekyna\Component\Payum\Payzen\Tests\Assert\TransactionIdAssertTrait; +use PHPUnit\Framework\TestCase; + +class IdGeneratedByDateTest extends TestCase +{ + use TransactionIdAssertTrait; + + public function test_getTransactionId(): void + { + $transactionIdInterface = $this->createIdGeneratedByDate(); + + $data = $transactionIdInterface->getTransactionId(); + $this->assertArrayHasVadsTransIdAndDateKeysTypedString($data); + } + + /** + * Returns the TransactionIdInterface instance. + * + * @return TransactionIdInterface + */ + private function createIdGeneratedByDate(): TransactionIdInterface + { + return new IdGeneratedByDate(); + } +} diff --git a/tests/Api/IdGeneratedByFileTest.php b/tests/Api/IdGeneratedByFileTest.php new file mode 100644 index 0000000..cd91025 --- /dev/null +++ b/tests/Api/IdGeneratedByFileTest.php @@ -0,0 +1,56 @@ +<?php + +namespace Ekyna\Component\Payum\Payzen\Tests\Api; + +use Ekyna\Component\Payum\Payzen\Api\IdGeneratedByFile; +use Ekyna\Component\Payum\Payzen\Api\TransactionIdInterface; +use Ekyna\Component\Payum\Payzen\Tests\Assert\TransactionIdAssertTrait; +use PHPUnit\Framework\TestCase; + +class IdGeneratedByFileTest extends TestCase +{ + use TransactionIdAssertTrait; + + public function test_getTransactionId(): void + { + $this->clearCache(); + + $transactionIdInterface = $this->createIdGeneratedByFile(); + + $data = $transactionIdInterface->getTransactionId(); + $this->assertArrayHasVadsTransIdAndDateKeysTypedString($data); + $this->assertEquals('000001', $data['vads_trans_id']); + + $data = $transactionIdInterface->getTransactionId(); + $this->assertArrayHasVadsTransIdAndDateKeysTypedString($data); + $this->assertEquals('000002', $data['vads_trans_id']); + + $data = $transactionIdInterface->getTransactionId(); + $this->assertArrayHasVadsTransIdAndDateKeysTypedString($data); + $this->assertEquals('000003', $data['vads_trans_id']); + + touch(dirname(__DIR__, 2) . '/cache/transaction_id', time() - 60 * 60 * 24); + + $data = $transactionIdInterface->getTransactionId(); + $this->assertArrayHasVadsTransIdAndDateKeysTypedString($data); + $this->assertEquals('000001', $data['vads_trans_id']); + } + + /** + * Returns the TransactionIdInterface instance. + * + * @return TransactionIdInterface + */ + private function createIdGeneratedByFile(): TransactionIdInterface + { + return new IdGeneratedByFile(dirname(__DIR__, 2) . '/cache/'); + } + + private function clearCache(): void + { + $path = dirname(__DIR__, 2) . '/cache/transaction_id'; + if (file_exists($path)) { + unlink($path); + } + } +} diff --git a/tests/Assert/TransactionIdAssertTrait.php b/tests/Assert/TransactionIdAssertTrait.php new file mode 100644 index 0000000..a412126 --- /dev/null +++ b/tests/Assert/TransactionIdAssertTrait.php @@ -0,0 +1,31 @@ +<?php + +namespace Ekyna\Component\Payum\Payzen\Tests\Assert; + +use Ekyna\Component\Payum\Payzen\Tests\Constraint\ArrayItem; +use PHPUnit\Framework\Constraint\ArrayHasKey; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\IsType; +use PHPUnit\Framework\Constraint\LogicalAnd; +use PHPUnit\Framework\Constraint\RegularExpression; + +trait TransactionIdAssertTrait +{ + abstract public static function assertThat($value, Constraint $constraint, string $message = ''): void; + + protected function assertArrayHasVadsTransIdAndDateKeysTypedString($array, string $message = '') + { + $this->assertThat( + $array, + LogicalAnd::fromConstraints( + new ArrayHasKey('vads_trans_id'), + new ArrayHasKey('vads_trans_date'), + new ArrayItem('vads_trans_id', new IsType('string')), + new ArrayItem('vads_trans_date', new IsType('string')), + new ArrayItem('vads_trans_id', new RegularExpression('/\d{6}/')), + new ArrayItem('vads_trans_date', new RegularExpression('/\d{8}/')) + ), + $message + ); + } +} diff --git a/tests/Constraint/ArrayItem.php b/tests/Constraint/ArrayItem.php new file mode 100644 index 0000000..87bba7b --- /dev/null +++ b/tests/Constraint/ArrayItem.php @@ -0,0 +1,97 @@ +<?php declare(strict_types=1); +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann <sebastian@phpunit.de> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Ekyna\Component\Payum\Payzen\Tests\Constraint; + +use ArrayAccess; +use PHPUnit\Framework\Constraint\Constraint; +use SebastianBergmann\RecursionContext\InvalidArgumentException; +use function array_key_exists; +use function is_array; + +/** + * Constraint that asserts that the array it is evaluated for has a given key and match a constraint type. + * + * Uses array_key_exists() to check if the key is found in the input array, if not found the evaluation fails. + * + * The array key and the constraint type are passed in the constructor. + */ +final class ArrayItem extends Constraint +{ + /** + * @var string + */ + private $key; + /** + * @var Constraint + */ + private $constraint; + + /** + * @param string $key + * @param Constraint $constraint + */ + public function __construct(string $key, Constraint $constraint) + { + $this->key = $key; + $this->constraint = $constraint; + } + + /** + * Returns a string representation of the constraint. + * + * @throws InvalidArgumentException + */ + public function toString(): string + { + return 'has the key ' . $this->exporter()->export($this->key) . $this->constraint->toString(); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + if (is_array($other)) { + if (!array_key_exists($this->key, $other)) { + return false; + } + + return $this->constraint->matches($other[$this->key]); + } + + if ($other instanceof ArrayAccess) { + if (!$other->offsetExists($this->key)) { + return false; + } + + return $this->constraint->matches($other[$this->key]); + } + + return false; + } + + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws InvalidArgumentException + */ + protected function failureDescription($other): string + { + return 'an array ' . $this->toString(); + } +} diff --git a/tests/PayzenGatewayFactoryTest.php b/tests/PayzenGatewayFactoryTest.php index 1222935..d431970 100644 --- a/tests/PayzenGatewayFactoryTest.php +++ b/tests/PayzenGatewayFactoryTest.php @@ -38,7 +38,7 @@ public function test_create_gateway() 'ctx_mode' => Api::MODE_PRODUCTION, 'site_id' => '123456', 'certificate' => '123456', - 'directory' => __DIR__ . '/../cache', + 'directory' => dirname(__DIR__, 1) . '/cache', ]); $this->assertInstanceOf('Payum\Core\Gateway', $gateway);