Skip to content

Commit 2d6e3ce

Browse files
committed
Add an interface to generate the transaction Id, inject her in the ApiRequestAction.php
Fix the interface and add a second method to generate transaction Id Change calculation for the amount send to Payzen, Payzen need a non-decimal value for amount Fork from ekyna/payum-payzen branch 1.6
1 parent 4fe4222 commit 2d6e3ce

7 files changed

+171
-85
lines changed

Diff for: src/Action/Api/ApiRequestAction.php

+26-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Ekyna\Component\Payum\Payzen\Action\Api;
44

5+
use Ekyna\Component\Payum\Payzen\Api\TransactionIdInterface;
56
use Ekyna\Component\Payum\Payzen\Request\Request;
7+
use Exception;
68
use Payum\Core\Bridge\Spl\ArrayObject;
79
use Payum\Core\Exception\RequestNotSupportedException;
810
use Payum\Core\Reply\HttpRedirect;
@@ -14,10 +16,21 @@
1416
*/
1517
class ApiRequestAction extends AbstractApiAction
1618
{
19+
/**
20+
* @var TransactionIdInterface
21+
*/
22+
private $transactionId;
23+
24+
public function __construct(
25+
TransactionIdInterface $transactionId
26+
) {
27+
$this->transactionId = $transactionId;
28+
}
29+
1730
/**
1831
* @inheritdoc
1932
*
20-
* @throws HttpRedirect
33+
* @throws HttpRedirect|Exception
2134
*/
2235
public function execute($request): void
2336
{
@@ -30,12 +43,20 @@ public function execute($request): void
3043
return;
3144
}
3245

33-
$model['vads_trans_id'] = $this->api->getTransactionId();
34-
// Current UTC date time
35-
$model['vads_trans_date'] = (new \DateTime('now', new \DateTimeZone('UTC')))->format('YmdHis');
36-
3746
$data = $model->getArrayCopy();
3847

48+
// If your application needs to generate a trans_id before this request
49+
if (isset($data['generated_vads_trans_id']) && isset($data['generated_vads_trans_date'])) {
50+
$data['vads_trans_id'] = $data['generated_vads_trans_id'];
51+
$data['vads_trans_date'] = $data['generated_vads_trans_date'];
52+
unset($data['generated_vads_trans_date']);
53+
unset($data['generated_vads_trans_id']);
54+
} else {
55+
//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
56+
$transactionId = $this->transactionId->getTransactionId();
57+
$data = array_merge($data, $transactionId);
58+
}
59+
3960
$this->logRequestData($data);
4061

4162
$url = $this->api->createRequestUrl($data);

Diff for: src/Action/ConvertPaymentAction.php

+8-6
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,26 @@ public function execute($request)
3636

3737
$model = ArrayObject::ensureArrayObject($payment->getDetails());
3838

39-
if (false == $model['vads_amount']) {
39+
if (!$model['vads_amount']) {
4040
$this->gateway->execute($currency = new GetCurrency($payment->getCurrencyCode()));
4141
if (2 < $currency->exp) {
4242
throw new RuntimeException('Unexpected currency exp.');
4343
}
44-
$divisor = pow(10, 2 - $currency->exp);
44+
// $currecy->exp is the number of decimal required with this currency
45+
$multiplier = pow(10, $currency->exp);
4546

4647
$model['vads_currency'] = (string)$currency->numeric;
47-
$model['vads_amount'] = (string)abs($payment->getTotalAmount() / $divisor);
48+
// used to send a non-decimal value to the platform, it can be reverted with currency->exp who be known by Payzen
49+
$model['vads_amount'] = (string)abs($payment->getTotalAmount() * $multiplier);
4850
}
4951

50-
if (false == $model['vads_order_id']) {
52+
if (!$model['vads_order_id']) {
5153
$model['vads_order_id'] = $payment->getNumber();
5254
}
53-
if (false == $model['vads_cust_id']) {
55+
if (!$model['vads_cust_id']) {
5456
$model['vads_cust_id'] = $payment->getClientId();
5557
}
56-
if (false == $model['vads_cust_email']) {
58+
if (!$model['vads_cust_email']) {
5759
$model['vads_cust_email'] = $payment->getClientEmail();
5860
}
5961

Diff for: src/Api/Api.php

+2-69
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Ekyna\Component\Payum\Payzen\Api;
44

55
use Payum\Core\Exception\LogicException;
6-
use Payum\Core\Exception\RuntimeException;
76
use Symfony\Component\OptionsResolver\Options;
87
use Symfony\Component\OptionsResolver\OptionsResolver;
98

@@ -54,55 +53,9 @@ public function setConfig(array $config)
5453
->resolve($config);
5554
}
5655

57-
/**
58-
* Returns the next transaction id.
59-
*
60-
* @return string
61-
*/
62-
public function getTransactionId(): string
56+
public function getConfig(): array
6357
{
64-
$path = $this->getDirectoryPath() . 'transaction_id';
65-
66-
// Create file if not exists
67-
if (!file_exists($path)) {
68-
touch($path);
69-
chmod($path, 0600);
70-
}
71-
72-
$date = (new \DateTime())->format('Ymd');
73-
$fileDate = date('Ymd', filemtime($path));
74-
$isDailyFirstAccess = ($date != $fileDate);
75-
76-
// Open file
77-
$handle = fopen($path, 'r+');
78-
if (false === $handle) {
79-
throw new RuntimeException('Failed to open the transaction ID file.');
80-
}
81-
// Lock File
82-
if (!flock($handle, LOCK_EX)) {
83-
throw new RuntimeException('Failed to lock the transaction ID file.');
84-
}
85-
86-
$id = 1;
87-
// If not daily first access, read and increment the id
88-
if (!$isDailyFirstAccess) {
89-
$id = (int)fread($handle, 6);
90-
$id++;
91-
}
92-
93-
// Truncate, write, unlock and close.
94-
fseek($handle, 0);
95-
ftruncate($handle, 0);
96-
fwrite($handle, (string)$id);
97-
fflush($handle);
98-
flock($handle, LOCK_UN);
99-
fclose($handle);
100-
101-
if ($this->config['debug']) {
102-
$id += 89000;
103-
}
104-
105-
return str_pad($id, 6, '0', STR_PAD_LEFT);
58+
return $this->config;
10659
}
10760

10861
/**
@@ -198,26 +151,6 @@ public function generateSignature(array $data, $hashed = true): string
198151
return $content;
199152
}
200153

201-
/**
202-
* Returns the directory path and creates it if not exists.
203-
*
204-
* @return string
205-
*/
206-
private function getDirectoryPath(): string
207-
{
208-
$path = $this->config['directory'];
209-
210-
211-
// Create directory if not exists
212-
if (!is_dir($path)) {
213-
if (!mkdir($path, 0755, true)) {
214-
throw new RuntimeException('Failed to create cache directory');
215-
}
216-
}
217-
218-
return $path . DIRECTORY_SEPARATOR;
219-
}
220-
221154
/**
222155
* Check that the API has been configured.
223156
*

Diff for: src/Api/IdGeneratedByDate.php

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Ekyna\Component\Payum\Payzen\Api;
4+
5+
use Exception;
6+
7+
class IdGeneratedByDate implements TransactionIdInterface
8+
{
9+
/**
10+
* @throws Exception
11+
*/
12+
public function getTransactionId(): array
13+
{
14+
$diff = (new \DateTimeImmutable('midnight', new \DateTimeZone('UTC')))
15+
->diff(new \DateTimeImmutable('now', new \DateTimeZone('UTC')));
16+
return [
17+
'vads_trans_date' => (new \DateTime('now', new \DateTimeZone('UTC')))->format('YmdHis'),
18+
'vads_trans_id' => sprintf('%06d', random_int(0, 9) + (($diff->h * 3600 + $diff->i * 60 + $diff->s) * 10))
19+
];
20+
}
21+
}

Diff for: src/Api/IdGeneratedByFile.php

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace Ekyna\Component\Payum\Payzen\Api;
4+
5+
use Exception;
6+
use Payum\Core\Exception\RuntimeException;
7+
8+
class IdGeneratedByFile implements TransactionIdInterface
9+
{
10+
private $path;
11+
private $debug;
12+
13+
public function __construct(
14+
string $path,
15+
bool $debug = false
16+
) {
17+
$this->path = $path;
18+
$this->debug = $debug;
19+
}
20+
21+
/**
22+
* @throws Exception
23+
*/
24+
public function getTransactionId(): array
25+
{
26+
$this->createDirectoryPath();
27+
$this->path = rtrim($this->path,DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
28+
$path = $this->path . 'transaction_id';
29+
30+
// Create file if not exists
31+
if (!file_exists($path)) {
32+
touch($path);
33+
chmod($path, 0600);
34+
}
35+
36+
$date = (new \DateTime())->format('Ymd');
37+
$fileDate = date('Ymd', filemtime($path));
38+
$isDailyFirstAccess = ($date != $fileDate);
39+
40+
// Open file
41+
$handle = fopen($path, 'r+');
42+
if (false === $handle) {
43+
throw new RuntimeException('Failed to open the transaction ID file.');
44+
}
45+
// Lock File
46+
if (!flock($handle, LOCK_EX)) {
47+
throw new RuntimeException('Failed to lock the transaction ID file.');
48+
}
49+
50+
$id = 1;
51+
// If not daily first access, read and increment the id
52+
if (!$isDailyFirstAccess) {
53+
$id = (int)fread($handle, 6);
54+
$id++;
55+
}
56+
57+
// Truncate, write, unlock and close.
58+
fseek($handle, 0);
59+
ftruncate($handle, 0);
60+
fwrite($handle, (string)$id);
61+
fflush($handle);
62+
flock($handle, LOCK_UN);
63+
fclose($handle);
64+
65+
if ($this->debug) {
66+
$id += 89000;
67+
}
68+
69+
return [
70+
'vads_trans_date' => (new \DateTime('now', new \DateTimeZone('UTC')))->format('YmdHis'),
71+
'vads_trans_id' => str_pad($id, 6, '0', STR_PAD_LEFT)
72+
];
73+
}
74+
75+
/**
76+
* Returns the directory path and creates it if not exists.
77+
*/
78+
public function createDirectoryPath(): void
79+
{
80+
// Create directory if not exists
81+
if (!is_dir($this->path)) {
82+
if (!mkdir($this->path, 0755, true)) {
83+
throw new RuntimeException('Failed to create cache directory');
84+
}
85+
}
86+
}
87+
}

Diff for: src/Api/TransactionIdInterface.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Ekyna\Component\Payum\Payzen\Api;
4+
5+
interface TransactionIdInterface
6+
{
7+
/** @return array{vads_trans_date: string, vads_trans_id: string} */
8+
public function getTransactionId(): array;
9+
}

Diff for: src/PayzenGatewayFactory.php

+18-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Ekyna\Component\Payum\Payzen;
44

5+
use Ekyna\Component\Payum\Payzen\Api\IdGeneratedByFile;
6+
use Ekyna\Component\Payum\Payzen\Api\TransactionIdInterface;
57
use Payum\Core\Bridge\Spl\ArrayObject;
68
use Payum\Core\GatewayFactory;
79
use Payum\Core\GatewayFactoryInterface;
@@ -13,17 +15,28 @@
1315
*/
1416
class PayzenGatewayFactory extends GatewayFactory
1517
{
18+
private $transactionId;
19+
20+
public function __construct(
21+
array $defaultConfig = array(),
22+
GatewayFactoryInterface $coreGatewayFactory = null,
23+
TransactionIdInterface $transactionId = null
24+
) {
25+
parent::__construct($defaultConfig, $coreGatewayFactory);
26+
$this->transactionId = $transactionId ?? new IdGeneratedByFile($defaultConfig['directory'] ?? sys_get_temp_dir());
27+
}
28+
1629
/**
1730
* Builds a new factory.
1831
*
19-
* @param array $defaultConfig
32+
* @param array $defaultConfig
2033
* @param GatewayFactoryInterface|null $coreGatewayFactory
21-
*
34+
* @param TransactionIdInterface|null $transactionId
2235
* @return PayzenGatewayFactory
2336
*/
24-
public static function build(array $defaultConfig, GatewayFactoryInterface $coreGatewayFactory = null): PayzenGatewayFactory
37+
public static function build(array $defaultConfig, GatewayFactoryInterface $coreGatewayFactory = null, TransactionIdInterface $transactionId = null): PayzenGatewayFactory
2538
{
26-
return new static($defaultConfig, $coreGatewayFactory);
39+
return new static($defaultConfig, $coreGatewayFactory, $transactionId);
2740
}
2841

2942
/**
@@ -42,7 +55,7 @@ protected function populateConfig(ArrayObject $config)
4255
'payum.action.refund' => new Action\RefundAction(),
4356
'payum.action.status' => new Action\StatusAction(),
4457
'payum.action.notify' => new Action\NotifyAction(),
45-
'payum.action.api.request' => new Action\Api\ApiRequestAction(),
58+
'payum.action.api.request' => new Action\Api\ApiRequestAction($this->transactionId),
4659
'payum.action.api.response' => new Action\Api\ApiResponseAction(),
4760
]);
4861

0 commit comments

Comments
 (0)