diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..36ea091 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Auto detect text files and perform LF normalization +* text=auto +* text eol=lf + +# Convert images to binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.tiff binary +*.ico binary \ No newline at end of file diff --git a/composer.json b/composer.json index 9e91e0b..fece440 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "paynl/php-sdk", "description": "Software Development Kit for implementing Pay.'s API version 3", - "version": "0.2.0", + "version": "0.2.1", "type": "library", "require": { "php": "^8.1", diff --git a/config/config.global.php b/config/config.global.php index cd9a18c..e6ff123 100644 --- a/config/config.global.php +++ b/config/config.global.php @@ -7,5 +7,6 @@ 'username' => '', # Use AT-Code or SL-Code. Use AT-code together with API-Token. 'password' => '', # Use API token or secret. Use Secret in combination with SL-Code. ], - 'debug' => false -]; \ No newline at end of file + 'debug' => false, + 'useFileCaching' => true +]; diff --git a/samples/OrderCreate.php b/samples/OrderCreate.php index d70ddf2..56cb440 100644 --- a/samples/OrderCreate.php +++ b/samples/OrderCreate.php @@ -106,7 +106,6 @@ try { $request->setReference('SDK0123456789'); $payOrder = $request->start(); - echo get_class($payOrder); } catch (PayException $e) { echo '
'; echo 'Technical message: ' . $e->getMessage() . PHP_EOL; diff --git a/samples/ServiceGetConfig.php b/samples/ServiceGetConfig.php index b24da48..cc17263 100644 --- a/samples/ServiceGetConfig.php +++ b/samples/ServiceGetConfig.php @@ -13,10 +13,9 @@ $config->setUsername($_REQUEST['username'] ?? ''); $config->setPassword($_REQUEST['password'] ?? ''); - try { $slCode = $_REQUEST['slcode'] ?? ''; - $config = (new ServiceGetConfigRequest($slCode))->setConfig($config)->start(); + $serviceConfig = (new ServiceGetConfigRequest($slCode))->setConfig($config)->start(); } catch (PayException $e) { echo ''; echo 'Technical message: ' . $e->getMessage() . PHP_EOL; @@ -28,18 +27,18 @@ echo ''; -echo $config->getCode() . ' - ' . $config->getName() . PHP_EOL; +echo $serviceConfig->getCode() . ' - ' . $serviceConfig->getName() . PHP_EOL; -$banks = $config->getBanks(); +$banks = $serviceConfig->getBanks(); print_r($banks); -$terminals = $config->getTerminals(); +$terminals = $serviceConfig->getTerminals(); print_r($terminals); -$tguList = $config->getCores(); +$tguList = $serviceConfig->getCores(); print_r($tguList); -$paymentMethods = $config->getPaymentMethods(); +$paymentMethods = $serviceConfig->getPaymentMethods(); foreach ($paymentMethods as $method) { echo $method->getId() . ' - '; echo $method->getName() . ' - '; @@ -51,6 +50,6 @@ echo PHP_EOL; } -foreach ($config->getCheckoutOptions() as $checkoutOption) { +foreach ($serviceConfig->getCheckoutOptions() as $checkoutOption) { echo '=> TAG: ' . $checkoutOption->getTag() . PHP_EOL; } diff --git a/src/Config/Config.php b/src/Config/Config.php index d9573e0..a987305 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -371,6 +371,14 @@ public function isEmpty() return false; } + /** + * @return boolean + */ + public function isCacheEnabled() + { + return ($this->data['useFileCaching'] ?? 0) == 1; + } + /** * @return string */ @@ -389,6 +397,16 @@ public function setPassword(string $password): self return $this; } + /** + * @param boolean $caching + * @return self + */ + public function setCaching(bool $caching): self + { + $this->data['useFileCaching'] = $caching; + return $this; + } + /** * @return string */ diff --git a/src/Helpers/StaticCacheTrait.php b/src/Helpers/StaticCacheTrait.php new file mode 100644 index 0000000..19113f0 --- /dev/null +++ b/src/Helpers/StaticCacheTrait.php @@ -0,0 +1,63 @@ + $this->orderId + 'transactionId' => $this->orderId ]; } @@ -53,9 +57,25 @@ public function getBodyParameters(): array */ public function start(): PayOrder { - # Always use TGU-1 for orderStatus - $this->config->setCore(Config::TGU1); - - return parent::start(); + $cacheKey = 'order_status_' . md5(json_encode([$this->config->getUsername(), $this->orderId])); + + if ($this->hasStaticCache($cacheKey)) { + return $this->getStaticCacheValue($cacheKey); + } + + if ($this->config->isCacheEnabled()) { + $cache = new PayCache(); + return $cache->get($cacheKey, function () use ($cacheKey) { + return $this->staticCache($cacheKey, function () { + $this->config->setCore(Config::TGU1); + return parent::start(); + }); + }, 3); # 3 seconds file caching + } + + return $this->staticCache($cacheKey, function () { + $this->config->setCore(Config::TGU1); + return parent::start(); + }); } -} \ No newline at end of file +} diff --git a/src/Model/Request/ServiceGetConfigRequest.php b/src/Model/Request/ServiceGetConfigRequest.php index 767368e..cee0222 100644 --- a/src/Model/Request/ServiceGetConfigRequest.php +++ b/src/Model/Request/ServiceGetConfigRequest.php @@ -8,6 +8,8 @@ use PayNL\Sdk\Request\RequestData; use PayNL\Sdk\Model\Response\ServiceGetConfigResponse; use PayNL\Sdk\Request\RequestInterface; +use PayNL\Sdk\Util\PayCache; +use PayNL\Sdk\Helpers\StaticCacheTrait; /** * Class ServiceGetConfigRequest @@ -18,12 +20,17 @@ */ class ServiceGetConfigRequest extends RequestData { + use StaticCacheTrait; + + /** + * @var string|mixed + */ private string $serviceId; /** - * @param $serviceId + * @param string $serviceId */ - public function __construct($serviceId = '') + public function __construct(string $serviceId = '') { $this->serviceId = $serviceId; parent::__construct('GetConfig', '/services/config', RequestInterface::METHOD_GET); @@ -53,9 +60,35 @@ public function getBodyParameters(): array * @throws PayException */ public function start(): ServiceGetConfigResponse + { + $cacheKey = 'service_getconfig_' . md5(json_encode([$this->config->getUsername(), $this->config->getPassword(), $this->serviceId])); + + if ($this->hasStaticCache($cacheKey)) { + return $this->getStaticCacheValue($cacheKey); + } + + if ($this->config->isCacheEnabled()) { + $cache = new PayCache(); + return $cache->get($cacheKey, function () use ($cacheKey) { + return $this->staticCache($cacheKey, function () { + return $this->startAPI(); + }); + }, 5); + } + + return $this->staticCache($cacheKey, function () { + return $this->startAPI(); + }); + } + + /** + * @return ServiceGetConfigResponse + * @throws PayException + */ + private function startAPI(): ServiceGetConfigResponse { $this->config->setCore('https://rest.pay.nl'); $this->config->setVersion(2); return parent::start(); } -} \ No newline at end of file +} diff --git a/src/Model/Request/TransactionStatusRequest.php b/src/Model/Request/TransactionStatusRequest.php index b328084..bf6629f 100644 --- a/src/Model/Request/TransactionStatusRequest.php +++ b/src/Model/Request/TransactionStatusRequest.php @@ -4,10 +4,13 @@ namespace PayNL\Sdk\Model\Request; +use PayNL\Sdk\Config\Config; use PayNL\Sdk\Exception\PayException; use PayNL\Sdk\Request\RequestData; use PayNL\Sdk\Model\Pay\PayOrder; use PayNL\Sdk\Request\RequestInterface; +use PayNL\Sdk\Helpers\StaticCacheTrait; +use PayNL\Sdk\Util\PayCache; /** * Class TransactionStatusRequest @@ -17,12 +20,14 @@ */ class TransactionStatusRequest extends RequestData { + use StaticCacheTrait; + private string $orderId; /** - * @param $orderid + * @param string $orderId */ - public function __construct($orderId) + public function __construct(string $orderId) { $this->orderId = $orderId; parent::__construct('TransactionStatus', '/transactions/%transactionId%/status', RequestInterface::METHOD_GET); @@ -48,12 +53,29 @@ public function getBodyParameters(): array /** * @return PayOrder - * @throws PayException + * @throws \Exception */ public function start(): PayOrder { - # Always use rest.pay.nl for this status request - $this->config->setCore('https://rest.pay.nl'); - return parent::start(); + $cacheKey = 'transaction_status_' . md5(json_encode([$this->config->getUsername(), $this->orderId])); + + if ($this->hasStaticCache($cacheKey)) { + return $this->getStaticCacheValue($cacheKey); + } + + if ($this->config->isCacheEnabled()) { + $cache = new PayCache(); + return $cache->get($cacheKey, function () use ($cacheKey) { + return $this->staticCache($cacheKey, function () { + $this->config->setCore('https://rest.pay.nl'); + return parent::start(); + }); + }, 3); # 3 seconds file caching + } + + return $this->staticCache($cacheKey, function () { + $this->config->setCore('https://rest.pay.nl'); + return parent::start(); + }); } -} \ No newline at end of file +} diff --git a/src/Model/Response/ServiceGetConfigResponse.php b/src/Model/Response/ServiceGetConfigResponse.php index 4d8d2c4..be9712a 100644 --- a/src/Model/Response/ServiceGetConfigResponse.php +++ b/src/Model/Response/ServiceGetConfigResponse.php @@ -335,11 +335,30 @@ public function setTguList(array $tguList): void } /** + * Provides a core-list prepared with web protocol. * @return array */ public function getCores(): array { - return $this->getTguList(); + $cores = $this->getTguList(); + + $payDomain = false; + foreach ($cores as &$core) { + $domain = $core['domain']; + + if (in_array($domain, ['pay.nl', 'connect.pay.nl'])) { + $payDomain = true; + } + + $core['domain'] = 'https://' . $domain; + $core['label'] = $domain; + } + + if ($payDomain !== true) { + array_unshift($cores, ['domain' => 'https://connect.pay.nl', 'label' => 'connect.pay.nl']); + } + + return $cores; } /** diff --git a/src/Request/RequestData.php b/src/Request/RequestData.php index f357fad..fa702d1 100644 --- a/src/Request/RequestData.php +++ b/src/Request/RequestData.php @@ -43,7 +43,8 @@ public function __construct(string $mapperName, string $uri, string $requestMeth */ public function setConfig(Config $config): self { - $this->config = $config; + $this->config = (new Config(require __DIR__ . '/../../config/config.global.php')); + $this->config->merge($config); return $this; } @@ -56,16 +57,17 @@ public function setApplication(Application $application): void $this->application = $application; } + /** - * @return mixed + * @return Config|null * @throws PayException - * @throws Exception */ - public function start() + private function getConfig(): Config { - $config = (new Config(require __DIR__ . '/../../config/config.global.php')); - if (!empty($this->config)) { - $config->merge($this->config); + if (empty($this->config)) { + $config = (new Config(require __DIR__ . '/../../config/config.global.php')); + } else { + $config = $this->config; } if (!empty($config->getFailoverUrl())) { @@ -76,6 +78,17 @@ public function start() throw new PayException('Please check your config', 0, 0); } + return $config; + } + + /** + * @return mixed + * @throws PayException + */ + public function start() + { + $config = $this->getConfig(); + try { if (empty($this->application)) { $this->application = Application::init($config); diff --git a/src/Util/Exchange.php b/src/Util/Exchange.php index 06e54a1..7924edb 100644 --- a/src/Util/Exchange.php +++ b/src/Util/Exchange.php @@ -76,7 +76,7 @@ public function eventCapture() * * @param boolean $result * @param string $message - * @param boolean $returnOutput If true, then this method returs the output string + * @param boolean $returnOutput * @return false|string|void */ public function setResponse(bool $result, string $message, bool $returnOutput = false) diff --git a/src/Util/PayCache.php b/src/Util/PayCache.php new file mode 100644 index 0000000..d557db8 --- /dev/null +++ b/src/Util/PayCache.php @@ -0,0 +1,137 @@ +cacheDir = $cacheDir ?? sys_get_temp_dir() . '/cache_pay_phpsdk'; + $this->defaultTtl = $defaultTtl; + + if (!is_dir($this->cacheDir)) { + if (!@mkdir($this->cacheDir, 0777, true) && !is_dir($this->cacheDir)) { + $this->enabled = false; + return; + } + } + + if (!is_writable($this->cacheDir)) { + $this->enabled = false; + } + } + + /** + * @param string $key + * @param callable|null $callback + * @param integer|null $ttl + * @return mixed|null + */ + public function get(string $key, callable $callback = null, int $ttl = null): mixed + { + if (!$this->enabled) { + return $callback ? $callback() : null; + } + + $file = $this->getCacheFile($key); + + if (file_exists($file)) { + $data = @unserialize(file_get_contents($file)); + + if ($data !== false && isset($data['expires'], $data['value'])) { + if ($data['expires'] >= time()) { + return $data['value']; + } + } + + # Cache expired or invalid + @unlink($file); + } + + if ($callback !== null) { + $value = $callback(); + $this->set($key, $value, $ttl); + return $value; + } + + return null; + } + + /** + * @param string $key + * @param mixed $value + * @param integer|null $ttl + * @return void + */ + public function set(string $key, mixed $value, int $ttl = null): void + { + if (!$this->enabled) { + return; + } + + $ttl = $ttl ?? $this->defaultTtl; + $file = $this->getCacheFile($key); + + $data = [ + 'expires' => time() + $ttl, + 'value' => $value + ]; + + @file_put_contents($file, serialize($data), LOCK_EX); + } + + /** + * @param string $key + * @return void + */ + public function delete(string $key): void + { + if (!$this->enabled) { + return; + } + + $file = $this->getCacheFile($key); + if (file_exists($file)) { + @unlink($file); + } + } + + /** + * @return void + */ + public function clear(): void + { + if (!$this->enabled) { + return; + } + + foreach (glob($this->cacheDir . '/*') ?: [] as $file) { + @unlink($file); + } + } + + /** + * @param string $key + * @return string + */ + private function getCacheFile(string $key): string + { + return $this->cacheDir . '/' . md5($key) . '.cache'; + } + + /** + * @return boolean + */ + public function isEnabled(): bool + { + return $this->enabled; + } +} diff --git a/src/Util/Text.php b/src/Util/Text.php index acba9a6..ad15163 100644 --- a/src/Util/Text.php +++ b/src/Util/Text.php @@ -20,6 +20,7 @@ class Text public static function getFriendlyMessage(string $errorMessage) { $friendlyMessages = [ + 'requested payment method is not enabled' => 'This payment method is not available', 'INVALID_TRANSACTION_STAT' => 'Transaction not ready for refund.', 'username can not be empty' => 'Connection error. Please check your connection credentials.', 'bestelling kon niet worden gevonden' => 'Your order could not be found',