This commit is contained in:
2025-04-01 00:38:54 +02:00
parent d4d4c0c09d
commit 87da06293a
22351 changed files with 5168854 additions and 7538 deletions

11
modules/paynow/vendor/pay-now/index.php vendored Normal file
View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 mBank S.A.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,56 @@
{
"name": "pay-now/paynow-php-sdk",
"description": "PHP client library for accessing Paynow API",
"version": "2.2.2",
"keywords": [
"paynow",
"mbank",
"payment processing",
"api"
],
"type": "library",
"homepage": "https://www.paynow.pl",
"license": "MIT",
"authors": [
{
"name": "mBank S.A.",
"email": "kontakt@paynow.pl"
}
],
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0",
"php-http/client-implementation": "^1.0 || ^2.0",
"php-http/message-factory": "^1.0 || ^2.0",
"php-http/discovery": "^1.12",
"php-http/httplug": "^2.2"
},
"require-dev": {
"phpunit/phpunit": "^7.0",
"php-http/mock-client": "^1.3",
"squizlabs/php_codesniffer": "^3.4",
"friendsofphp/php-cs-fixer": "^2.15",
"phpcompatibility/php-compatibility": "^9.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"nyholm/psr7": "^1.2",
"guzzlehttp/psr7": "^1.6",
"php-http/guzzle6-adapter": "^2.0"
},
"suggest": {
"nyholm/psr7": "A super lightweight PSR-7 implementation",
"php-http/curl-client": "PSR-18 and HTTPlug Async client with cURL"
},
"autoload": {
"psr-4": {
"Paynow\\": "src/Paynow/",
"Paynow\\Tests\\": "tests/"
}
},
"scripts": {
"test": "vendor/bin/phpunit",
"cs-check": "php-cs-fixer fix --no-interaction --dry-run --diff",
"cs-fix": "php-cs-fixer fix -v --diff"
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,17 @@
<phpunit bootstrap="tests/bootstrap.php"
colors="false">
<testsuites>
<testsuite name="Paynow PHP Test Suite">
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src</directory>
<exclude>
<directory>vendor</directory>
<directory suffix=".php">tests</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,57 @@
<?php
namespace Paynow;
use Paynow\HttpClient\HttpClient;
use Paynow\HttpClient\HttpClientInterface;
class Client
{
private $configuration;
private $httpClient;
/**
* @param string $apiKey
* @param string $apiSignatureKey
* @param string $environment
* @param string|null $applicationName
*/
public function __construct(
string $apiKey,
string $apiSignatureKey,
string $environment,
?string $applicationName = null
) {
$this->configuration = new Configuration();
$this->configuration->setApiKey($apiKey);
$this->configuration->setSignatureKey($apiSignatureKey);
$this->configuration->setEnvironment($environment);
$this->configuration->setApplicationName($applicationName);
$this->httpClient = new HttpClient($this->configuration);
}
/**
* @return Configuration
*/
public function getConfiguration(): Configuration
{
return $this->configuration;
}
/**
* @param HttpClientInterface $httpClient
*/
public function setHttpClient(HttpClientInterface $httpClient): void
{
$this->httpClient = $httpClient;
}
/**
* @return HttpClientInterface
*/
public function getHttpClient(): HttpClientInterface
{
return $this->httpClient;
}
}

View File

@@ -0,0 +1,131 @@
<?php
namespace Paynow;
class Configuration implements ConfigurationInterface
{
public const API_VERSION = 'v1';
public const API_VERSION_V2 = 'v2';
public const API_PRODUCTION_URL = 'https://api.paynow.pl';
public const API_SANDBOX_URL = 'https://api.sandbox.paynow.pl';
public const USER_AGENT = 'paynow-php-sdk';
/** @var array */
protected $data = [];
/**
* Set a key value pair
*
* @param string $key Key to set
* @param mixed $value Value to set
*/
private function set($key, $value)
{
$this->data[$key] = $value;
}
/**
* Get a specific key value
*
* @param string $key key to retrieve
* @return mixed|null Value of the key or NULL
*/
private function get($key)
{
return isset($this->data[$key]) ? $this->data[$key] : null;
}
/**
* Get an API key
*
* @return mixed|null
*/
public function getApiKey()
{
return $this->get('api_key');
}
/**
* Get Signature key
*
* @return mixed|null
*/
public function getSignatureKey()
{
return $this->get('signature_key');
}
/**
* Get environment name
*
* @return mixed|null
*/
public function getEnvironment()
{
return $this->get('environment');
}
/**
* Get API url
*
* @return mixed|null
*/
public function getUrl()
{
return $this->get('url');
}
/**
* Set an API Key
*
* @param $apiKey
*/
public function setApiKey($apiKey)
{
$this->set('api_key', $apiKey);
}
/**
* Set Signature Key
*
* @param $signatureKey
*/
public function setSignatureKey($signatureKey)
{
$this->set('signature_key', $signatureKey);
}
/**
* Set environment
*
* @param $environment
*/
public function setEnvironment($environment)
{
if (Environment::PRODUCTION === $environment) {
$this->set('environment', Environment::PRODUCTION);
$this->set('url', self::API_PRODUCTION_URL);
} else {
$this->set('environment', Environment::SANDBOX);
$this->set('url', self::API_SANDBOX_URL);
}
}
/**
* Set an application name
*
* @param $applicationName
*/
public function setApplicationName($applicationName)
{
$this->set('application_name', $applicationName);
}
/**
* @return mixed|null
*/
public function getApplicationName()
{
return $this->get('application_name');
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Paynow;
interface ConfigurationInterface
{
public function getApiKey();
public function getSignatureKey();
public function getEnvironment();
public function getUrl();
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Paynow;
class Environment
{
public const PRODUCTION = 'production';
public const SANDBOX = 'sandbox';
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Paynow\Exception;
class ConfigurationException extends PaynowException
{
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Paynow\Exception;
class Error
{
/** @var string */
private $type;
/** @var string */
private $message;
public function __construct(string $type, string $message)
{
$this->type = $type;
$this->message = $message;
}
/**
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Paynow\Exception;
use Exception;
use Throwable;
class PaynowException extends Exception
{
/** @var Error[] */
private $errors = [];
/**
* PaynowException constructor.
* @param string $message
* @param int $code
* @param string|null $body
* @param Throwable|null $previous
*/
public function __construct(string $message, int $code = 0, ?string $body = null, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
if ($body) {
$json = json_decode($body);
if (isset($json->errors) && ! empty($json->errors)) {
foreach ($json->errors as $error) {
$this->errors[] = new Error($error->errorType, $error->message);
}
}
}
}
/**
* @return Error[]
*/
public function getErrors(): array
{
return $this->errors;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Paynow\Exception;
class SignatureVerificationException extends PaynowException
{
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,78 @@
<?php
namespace Paynow\HttpClient;
use Psr\Http\Message\ResponseInterface;
class ApiResponse
{
/**
* Body content
*
* @var string
*/
public $body;
/**
* Http status
*
* @var int
*/
public $status;
/**
* Headers list
*
* @var array
*/
protected $headers;
/**
* @param ResponseInterface $response
* @throws HttpClientException
*/
public function __construct(ResponseInterface $response)
{
$this->body = $response->getBody();
$this->status = $response->getStatusCode();
$this->headers = $response->getHeaders();
if ($response->getStatusCode() >= 400) {
throw new HttpClientException(
'Error occurred during processing request',
$response->getStatusCode(),
$response->getBody()->getContents()
);
}
}
/**
* Parse JSON
*
* @return mixed
*/
public function decode()
{
return json_decode((string)$this->body);
}
/**
* Get status
*
* @return int
*/
public function getStatus(): int
{
return $this->status;
}
/**
* Get headers
*
* @return array
*/
public function getHeaders(): array
{
return $this->headers;
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace Paynow\HttpClient;
use Http\Discovery\Exception\NotFoundException;
use Http\Discovery\HttpClientDiscovery;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use Paynow\Configuration;
use Paynow\Util\SignatureCalculator;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriInterface;
class HttpClient implements HttpClientInterface
{
/** @var ClientInterface */
protected $client;
/** @var RequestFactoryInterface */
protected $messageFactory;
/** @var StreamFactoryInterface */
protected $streamFactory;
/** @var Configuration */
protected $config;
/** @var UriInterface */
private $url;
/** @param Configuration $config */
public function __construct(Configuration $config)
{
$this->config = $config;
$this->messageFactory = Psr17FactoryDiscovery::findRequestFactory();
$this->streamFactory = Psr17FactoryDiscovery::findStreamFactory();
try {
$this->client = Psr18ClientDiscovery::find();
} catch (NotFoundException $exception) {
$this->client = HttpClientDiscovery::find();
}
$this->url = Psr17FactoryDiscovery::findUrlFactory()->createUri((string)$config->getUrl());
}
/**
* @return string
*/
private function getUserAgent(): string
{
if ($this->config->getApplicationName()) {
return $this->config->getApplicationName() . ' (' . Configuration::USER_AGENT . ')';
}
return Configuration::USER_AGENT;
}
/**
* @param RequestInterface $request
* @throws HttpClientException
* @return ApiResponse
*/
private function send(RequestInterface $request): ApiResponse
{
try {
return new ApiResponse($this->client->sendRequest($request));
} catch (ClientExceptionInterface $exception) {
throw new HttpClientException($exception->getMessage(), $exception->getCode());
}
}
/**
* @param string $url
* @param array $data
* @param string|null $idempotencyKey
* @throws HttpClientException
* @return ApiResponse
*/
public function post(string $url, array $data, ?string $idempotencyKey = null): ApiResponse
{
$headers = $this->prepareHeaders($data);
if ($idempotencyKey) {
$headers['Idempotency-Key'] = $idempotencyKey;
}
$request = $this->messageFactory->createRequest(
'POST',
$this->url->withPath($url)
);
foreach ($headers as $name => $value) {
$request = $request->withHeader($name, $value);
}
$request = $request->withBody($this->streamFactory->createStream($this->arrayAsJson($data)));
return $this->send($request);
}
/**
* @param string $url
* @param array $data
* @throws HttpClientException
* @return ApiResponse
*/
public function patch(string $url, array $data): ApiResponse
{
$headers = $this->prepareHeaders($data);
$request = $this->messageFactory->createRequest(
'PATCH',
$this->url->withPath($url)
);
foreach ($headers as $name => $value) {
$request = $request->withHeader($name, $value);
}
$request = $request->withBody($this->streamFactory->createStream($this->arrayAsJson($data)));
return $this->send($request);
}
/**
* @param string $url
* @param string|null $query
* @throws HttpClientException
* @return ApiResponse
*/
public function get(string $url, string $query = null): ApiResponse
{
$request = $this->messageFactory->createRequest(
'GET',
$query ? $this->url->withPath($url)->withQuery($query) : $this->url->withPath($url)
);
foreach ($this->prepareHeaders() as $name => $value) {
$request = $request->withHeader($name, $value);
}
return $this->send($request);
}
/**
* @param array $data
* @return string
*/
private function arrayAsJson(array $data): string
{
return json_encode($data);
}
/**
* @param null|array $data
* @return array
*/
private function prepareHeaders(?array $data = null)
{
$headers = [
'Api-Key' => $this->config->getApiKey(),
'User-Agent' => $this->getUserAgent(),
'Accept' => 'application/json'
];
if ($data) {
$headers['Content-Type'] = 'application/json';
$headers['Signature'] = (string)new SignatureCalculator($this->config->getSignatureKey(), json_encode($data));
}
return $headers;
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Paynow\HttpClient;
use Exception;
class HttpClientException extends Exception
{
/** @var string|null */
private $body;
/** @var array */
private $errors;
/** @var int|null */
private $status;
/**
* @param string $message
* @param int|null $status
* @param string|null $body
*/
public function __construct(string $message, ?int $status = null, ?string $body = null)
{
parent::__construct($message);
$this->status = $status;
$this->body = $body;
}
/**
* @return string|null
*/
public function getBody(): ?string
{
return $this->body;
}
/**
* @return int|null
*/
public function getStatus(): ?int
{
return $this->status;
}
/**
* @return array
*/
public function getErrors()
{
return $this->errors;
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Paynow\HttpClient;
interface HttpClientInterface
{
public function post(string $url, array $data, ?string $idempotencyKey = null): ApiResponse;
public function patch(string $url, array $data): ApiResponse;
public function get(string $url, ?string $query = null): ApiResponse;
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,51 @@
<?php
namespace Paynow\Model\DataProcessing;
/**
* Class Notice
*
* @package Paynow\Model\DataProcessing
*/
class Notice
{
/**
* @var string
*/
private $title;
private $content;
private $locale;
public function __construct($title, $content, $locale)
{
$this->title = $title;
$this->content = $content;
$this->locale = $locale;
}
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @return mixed
*/
public function getContent()
{
return $this->content;
}
/**
* @return mixed
*/
public function getLocale()
{
return $this->locale;
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,16 @@
<?php
namespace Paynow\Model\Payment;
class Status
{
public const STATUS_ABANDONED = 'ABANDONED';
public const STATUS_CANCELLED = 'CANCELLED';
public const STATUS_CONFIRMED = 'CONFIRMED';
public const STATUS_ERROR = 'ERROR';
public const STATUS_EXPIRED = 'EXPIRED';
public const STATUS_NEW = 'NEW';
public const STATUS_PENDING = 'PENDING';
public const STATUS_REJECTED = 'REJECTED';
public const STATUS_WAITING_FOR_CONFIRMATION = 'WAITING_FOR_CONFIRMATION';
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,9 @@
<?php
namespace Paynow\Model\PaymentMethods;
class AuthorizationType
{
public const CODE = 'CODE';
public const REDIRECT = 'REDIRECT';
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Paynow\Model\PaymentMethods;
class PaymentMethod
{
private $id;
private $type;
private $name;
private $description;
private $image;
private $status;
private $authorizationType;
public function __construct($id, $type, $name, $description, $image, $status, $authorizationType)
{
$this->id = $id;
$this->type = $type;
$this->name = $name;
$this->description = $description;
$this->image = $image;
$this->status = $status;
$this->authorizationType = $authorizationType;
}
public function getId()
{
return $this->id;
}
public function getType()
{
return $this->type;
}
public function getName()
{
return $this->name;
}
public function getDescription()
{
return $this->description;
}
public function getImage()
{
return $this->image;
}
public function getStatus()
{
return $this->status;
}
/**
* @return bool
*/
public function isEnabled(): bool
{
return $this->status == Status::ENABLED;
}
public function getAuthorizationType()
{
return $this->authorizationType;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Paynow\Model\PaymentMethods;
class SavedInstrument
{
private $name;
private $expirationDate;
private $brand;
private $image;
private $token;
/**
* @var SavedInstrument\Status
*/
private $status;
public function __construct($name, $expirationDate, $brand, $image, $token, $status)
{
$this->name = $name;
$this->expirationDate = $expirationDate;
$this->brand = $brand;
$this->image = $image;
$this->token = $token;
$this->status = $status;
}
public function getName()
{
return $this->name;
}
public function getExpirationDate()
{
return $this->expirationDate;
}
public function getBrand()
{
return $this->brand;
}
public function getImage()
{
return $this->image;
}
public function getToken()
{
return $this->token;
}
public function getStatus()
{
return $this->status;
}
public function isExpired(): bool
{
return in_array($this->status, [SavedInstrument\Status::EXPIRED_CARD, SavedInstrument\Status::EXPIRED_TOKEN]);
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Paynow\Model\PaymentMethods\SavedInstrument;
class Status
{
public const ACTIVE = 'ACTIVE';
public const EXPIRED_CARD = 'EXPIRED_CARD';
public const EXPIRED_TOKEN = 'EXPIRED_TOKEN';
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,9 @@
<?php
namespace Paynow\Model\PaymentMethods;
class Status
{
public const DISABLED = 'DISABLED';
public const ENABLED = 'ENABLED';
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Paynow\Model\PaymentMethods;
class Type
{
public const BLIK = 'BLIK';
public const CARD = 'CARD';
public const GOOGLE_PAY = 'GOOGLE_PAY';
public const APPLE_PAY = 'APPLE_PAY';
public const PBL = 'PBL';
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,12 @@
<?php
namespace Paynow\Model\Refund;
class Status
{
public const CANCELLED = 'CANCELLED';
public const FAILED = 'FAILED';
public const NEW = 'NEW';
public const PENDING = 'PENDING';
public const SUCCESSFUL = 'SUCCESSFUL';
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,72 @@
<?php
namespace Paynow;
use InvalidArgumentException;
use Paynow\Exception\SignatureVerificationException;
use Paynow\Util\SignatureCalculator;
class Notification
{
/**
* @param $signatureKey
* @param $payload
* @param $headers
* @throws SignatureVerificationException
*/
public function __construct($signatureKey, $payload = null, ?array $headers = null)
{
if (! $payload) {
throw new InvalidArgumentException('No payload has been provided');
}
if (! $headers) {
throw new InvalidArgumentException('No headers have been provided');
}
$this->verify($signatureKey, $payload, $headers);
}
/**
* Verify payload Signature
*
* @param string $signatureKey
* @param string $data
* @param array $headers
* @throws SignatureVerificationException
* @return bool
*/
private function verify(string $signatureKey, string $data, array $headers)
{
$calculatedSignature = (string)new SignatureCalculator($signatureKey, $data);
if ($calculatedSignature !== $this->getPayloadSignature($headers)) {
throw new SignatureVerificationException('Signature mismatched for payload');
}
return true;
}
/**
* Retrieve Signature from payload
*
* @param array $headers
* @throws SignatureVerificationException
* @return string
*/
private function getPayloadSignature(array $headers)
{
if (isset($headers['Signature']) && $headers['Signature']) {
$signature = $headers['Signature'];
}
if (isset($headers['signature']) && $headers['signature']) {
$signature = $headers['signature'];
}
if (empty($signature)) {
throw new SignatureVerificationException('No signature was found for payload');
}
return $signature;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Paynow\Response\DataProcessing;
use Paynow\Model\DataProcessing\Notice;
class Notices
{
/**
* @var Notice[]
*/
private $list;
public function __construct($body)
{
if (! empty($body)) {
foreach ($body as $item) {
$this->list[] = new Notice(
$item->title,
$item->content,
$item->locale
);
}
}
}
/**
* @return array|Notice[]
*/
public function getAll()
{
return $this->list;
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,44 @@
<?php
namespace Paynow\Response\Payment;
use Paynow\Model\Payment\Status;
class Authorize
{
/** @var string */
private $paymentId;
/** @var Status|string */
private $status;
/** @var null|string */
private $redirectUrl;
public function __construct($paymentId, $status, ?string $redirectUrl = null)
{
$this->paymentId = $paymentId;
$this->status = $status;
$this->redirectUrl = $redirectUrl;
}
/**
* @return string|null
*/
public function getRedirectUrl(): ?string
{
return $this->redirectUrl;
}
/** @return string */
public function getPaymentId(): string
{
return $this->paymentId;
}
/** @return string */
public function getStatus(): string
{
return $this->status;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Paynow\Response\Payment;
class Status
{
/** @var string */
private $paymentId;
/** @var string */
private $status;
public function __construct($paymentId, $status)
{
$this->paymentId = $paymentId;
$this->status = $status;
}
/** @return string */
public function getPaymentId(): string
{
return $this->paymentId;
}
/** @return string */
public function getStatus(): string
{
return $this->status;
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,121 @@
<?php
namespace Paynow\Response\PaymentMethods;
use Paynow\Model\PaymentMethods\PaymentMethod;
use Paynow\Model\PaymentMethods\Type;
class PaymentMethods
{
/**
* @var PaymentMethod[]
*/
private $list;
public function __construct($body)
{
if (! empty($body)) {
foreach ($body as $group) {
if (! empty($group->paymentMethods)) {
foreach ($group->paymentMethods as $item) {
$this->list[] = new PaymentMethod(
$item->id,
$group->type,
$item->name,
$item->description,
$item->image,
$item->status,
$item->authorizationType ?? null
);
}
}
}
}
}
/**
* Retrieve all available payment methods
*
* @return PaymentMethod[]
*/
public function getAll()
{
return $this->list;
}
/**
* Retrieve only Blik payment methods
*
* @return PaymentMethod[]
*/
public function getOnlyBlik()
{
$blikPaymentMethods = [];
if (! empty($this->list)) {
foreach ($this->list as $item) {
if (Type::BLIK === $item->getType()) {
$blikPaymentMethods[] = $item;
}
}
}
return $blikPaymentMethods;
}
/**
* Retrieve only Card payment methods
*
* @return PaymentMethod[]
*/
public function getOnlyCards()
{
$cardPaymentMethods = [];
if (! empty($this->list)) {
foreach ($this->list as $item) {
if (Type::CARD === $item->getType()) {
$cardPaymentMethods[] = $item;
}
}
}
return $cardPaymentMethods;
}
/**
* Retrieve only GooglePay payment method
*
* @return PaymentMethod[]
*/
public function getOnlyGooglePay()
{
$cardPaymentMethods = [];
if (! empty($this->list)) {
foreach ($this->list as $item) {
if (Type::GOOGLE_PAY === $item->getType()) {
$cardPaymentMethods[] = $item;
}
}
}
return $cardPaymentMethods;
}
/**
* Retrieve only Pbl payment methods
*
* @return PaymentMethod[]
*/
public function getOnlyPbls()
{
$pblPaymentMethods = [];
if (! empty($this->list)) {
foreach ($this->list as $item) {
if (Type::PBL === $item->getType()) {
$pblPaymentMethods[] = $item;
}
}
}
return $pblPaymentMethods;
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,30 @@
<?php
namespace Paynow\Response\Refund;
class Status
{
/** @var string */
private $refundId;
/** @var string|Status */
private $status;
public function __construct($refundId, $status)
{
$this->refundId = $refundId;
$this->status = $status;
}
/** @return string */
public function getRefundId(): string
{
return $this->refundId;
}
/** @return string */
public function getStatus(): string
{
return $this->status;
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,46 @@
<?php
namespace Paynow\Service;
use Paynow\Configuration;
use Paynow\Exception\PaynowException;
use Paynow\HttpClient\HttpClientException;
use Paynow\Response\DataProcessing\Notices;
class DataProcessing extends Service
{
/**
* Retrieve data processing notice
*
* @param string|null $locale
*
* @throws PaynowException
* @return Notices
*/
public function getNotices(?string $locale): Notices
{
$parameters = [];
if (! empty($locale)) {
$parameters['locale'] = $locale;
}
try {
$decodedApiResponse = $this->getClient()
->getHttpClient()
->get(
Configuration::API_VERSION . "/payments/dataprocessing/notices",
http_build_query($parameters, '', '&')
)
->decode();
return new Notices($decodedApiResponse);
} catch (HttpClientException $exception) {
throw new PaynowException(
$exception->getMessage(),
$exception->getStatus(),
$exception->getBody(),
$exception
);
}
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace Paynow\Service;
use Paynow\Configuration;
use Paynow\Exception\PaynowException;
use Paynow\HttpClient\HttpClientException;
use Paynow\Response\Payment\Authorize;
use Paynow\Response\Payment\Status;
use Paynow\Response\PaymentMethods\PaymentMethods;
class Payment extends Service
{
/**
* Authorize payment
*
* @param array $data
* @param string|null $idempotencyKey
* @throws PaynowException
* @return Authorize
*/
public function authorize(array $data, ?string $idempotencyKey = null): Authorize
{
try {
$decodedApiResponse = $this->getClient()
->getHttpClient()
->post(
'/' . Configuration::API_VERSION . '/payments',
$data,
$idempotencyKey ?? $data['externalId']
)
->decode();
return new Authorize(
$decodedApiResponse->paymentId,
$decodedApiResponse->status,
! empty($decodedApiResponse->redirectUrl) ? $decodedApiResponse->redirectUrl : null
);
} catch (HttpClientException $exception) {
throw new PaynowException(
$exception->getMessage(),
$exception->getStatus(),
$exception->getBody(),
$exception
);
}
}
/**
* Retrieve available payment methods
*
* @param string|null $currency
* @param int|null $amount
* @param bool $applePayEnabled
* @return PaymentMethods
* @throws PaynowException
*/
public function getPaymentMethods(?string $currency = null, ?int $amount = 0, bool $applePayEnabled = true): PaymentMethods
{
$parameters = [
'applePayEnabled' => $applePayEnabled,
];
if (! empty($currency)) {
$parameters['currency'] = $currency;
}
if ($amount > 0) {
$parameters['amount'] = $amount;
}
try {
$decodedApiResponse = $this->getClient()
->getHttpClient()
->get(
Configuration::API_VERSION_V2 . '/payments/paymentmethods',
http_build_query($parameters, '', '&')
)
->decode();
return new PaymentMethods($decodedApiResponse);
} catch (HttpClientException $exception) {
throw new PaynowException(
$exception->getMessage(),
$exception->getStatus(),
$exception->getBody(),
$exception
);
}
}
/**
* Retrieve payment status
*
* @param string $paymentId
* @throws PaynowException
* @return Status
*/
public function status(string $paymentId): Status
{
try {
$decodedApiResponse = $this->getClient()
->getHttpClient()
->get(Configuration::API_VERSION . "/payments/$paymentId/status")
->decode();
return new Status($decodedApiResponse->paymentId, $decodedApiResponse->status);
} catch (HttpClientException $exception) {
throw new PaynowException(
$exception->getMessage(),
$exception->getStatus(),
$exception->getBody(),
$exception
);
}
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace Paynow\Service;
use Paynow\Configuration;
use Paynow\Exception\PaynowException;
use Paynow\HttpClient\HttpClientException;
use Paynow\Response\Refund\Status;
class Refund extends Service
{
/**
* Refund payment
*
* @param string $paymentId
* @param string $idempotencyKey
* @param int $amount
* @param null $reason
* @throws PaynowException
* @return Status
*/
public function create(string $paymentId, string $idempotencyKey, int $amount, $reason = null): Status
{
try {
$decodedApiResponse = $this->getClient()
->getHttpClient()
->post(
'/' . Configuration::API_VERSION . '/payments/' . $paymentId . '/refunds',
[
'amount' => $amount,
'reason' => $reason
],
$idempotencyKey
)
->decode();
return new Status($decodedApiResponse->refundId, $decodedApiResponse->status);
} catch (HttpClientException $exception) {
throw new PaynowException(
$exception->getMessage(),
$exception->getStatus(),
$exception->getBody(),
$exception
);
}
}
/**
* Retrieve refund status
* @param $refundId
* @throws PaynowException
* @return Status
*/
public function status($refundId): Status
{
try {
$decodedApiResponse = $this->getClient()
->getHttpClient()
->get(Configuration::API_VERSION . "/refunds/$refundId/status")
->decode();
return new Status($decodedApiResponse->refundId, $decodedApiResponse->status);
} catch (HttpClientException $exception) {
throw new PaynowException(
$exception->getMessage(),
$exception->getStatus(),
$exception->getBody(),
$exception
);
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Paynow\Service;
use Paynow\Client;
use Paynow\Environment;
use Paynow\Exception\ConfigurationException;
class Service
{
/**
* @var Client
*/
private $client;
/**
* @param Client $client
* @throws ConfigurationException
*/
public function __construct(Client $client)
{
if (! $client->getConfiguration()->getEnvironment()) {
$message = 'Provide correct environment, use '.Environment::PRODUCTION.' or '.Environment::SANDBOX;
throw new ConfigurationException($message);
}
if (! $client->getConfiguration()->getApiKey()) {
throw new ConfigurationException('You did not provide Api Key');
}
if (! $client->getConfiguration()->getSignatureKey()) {
throw new ConfigurationException('You did not provide Signature Key');
}
$this->client = $client;
}
/**
* @return Client
*/
public function getClient(): Client
{
return $this->client;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Paynow\Service;
use Paynow\Configuration;
use Paynow\Exception\PaynowException;
use Paynow\HttpClient\ApiResponse;
use Paynow\HttpClient\HttpClientException;
class ShopConfiguration extends Service
{
/**
* @param string $continueUrl
* @param string $notificationUrl
* @throws PaynowException
* @return ApiResponse
*/
public function changeUrls(string $continueUrl, string $notificationUrl)
{
$data = [
'continueUrl' => $continueUrl,
'notificationUrl' => $notificationUrl,
];
try {
return $this->getClient()
->getHttpClient()
->patch(
'/' . Configuration::API_VERSION.'/configuration/shop/urls',
$data
);
} catch (HttpClientException $exception) {
throw new PaynowException(
$exception->getMessage(),
$exception->getStatus(),
$exception->getBody(),
$exception
);
}
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,11 @@
<?php
namespace Paynow\Util;
class ClientExternalIdCalculator
{
public static function calculate(string $clientId, string $secretKey): string
{
return bin2hex(hash_hmac('sha256', $clientId, $secretKey, true));
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Paynow\Util;
use InvalidArgumentException;
class SignatureCalculator
{
/** @var string */
protected $hash;
/**
* @param string $signatureKey
* @param string $data
* @throws InvalidArgumentException
*/
public function __construct(string $signatureKey, string $data)
{
if (empty($signatureKey)) {
throw new InvalidArgumentException('You did not provide a Signature key');
}
if (empty($data)) {
throw new InvalidArgumentException('You did not provide any data');
}
$this->hash = base64_encode(hash_hmac('sha256', $data, $signatureKey, true));
}
/**
* @return string
*/
public function __toString(): string
{
return (string) $this->getHash();
}
/**
* @return string
*/
public function getHash(): string
{
return $this->hash;
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,81 @@
<?php
namespace Paynow\Tests;
use InvalidArgumentException;
use Paynow\Exception\SignatureVerificationException;
use Paynow\Notification;
class NotificationTest extends TestCase
{
/**
* @dataProvider requestsToTest
* @param $payload
* @param $headers
* @throws SignatureVerificationException
*/
public function testVerifyPayloadSuccessfully($payload, $headers)
{
// given
// when
new Notification('s3ecret-k3y', $payload, $headers);
// then
$this->assertTrue(true);
}
public function requestsToTest()
{
$payload = $this->loadData('notification.json', true);
return [
[
$payload,
['Signature' => 'Aq/VmN15rtjVbuy9F7Yw+Ym76H+VZjVSuHGpg4dwitY=']
],
[
$payload,
['signature' => 'Aq/VmN15rtjVbuy9F7Yw+Ym76H+VZjVSuHGpg4dwitY=']
]
];
}
public function testShouldThrowExceptionOnIncorrectSignature()
{
// given
$this->expectException(SignatureVerificationException::class);
$payload = $this->loadData('notification.json', true);
$headers = ['Signature' => 'Wq/V2N15rtjVbuy9F7Yw+Ym76H+VZjVSuHGpg4dwitY='];
// when
new Notification('s3ecret-k3y', $payload, $headers);
// then
}
public function testShouldThrowExceptionOnMissingPayload()
{
// given
$this->expectException(InvalidArgumentException::class);
$payload = null;
$headers = [];
// when
new Notification('s3ecret-k3y', null, null);
// then
}
public function testShouldThrowExceptionOnMissingPayloadHeaders()
{
// given
$this->expectException(InvalidArgumentException::class);
$payload = $this->loadData('notification.json', true);
$headers = null;
// when
new Notification('s3ecret-k3y', $payload, null);
// then
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Paynow\Tests\Service;
use Paynow\Service\DataProcessing;
use Paynow\Tests\TestCase;
class DataProcessingTest extends TestCase
{
public function testShouldRetrieveAllNoticeListSuccessfully()
{
// given
$this->testHttpClient->mockResponse('data_processing_notices_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$dataProcessing = new DataProcessing($this->client);
// when
$notices = $dataProcessing->getNotices('pl-PL')->getAll();
// then
$this->assertNotEmpty($notices);
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace Paynow\Tests\Service;
use Paynow\Model\PaymentMethods\AuthorizationType;
use Paynow\Model\PaymentMethods\Status;
use Paynow\Model\PaymentMethods\Type;
use Paynow\Service\Payment;
use Paynow\Tests\TestCase;
class PaymentMethodsTest extends TestCase
{
public function testShouldRetrieveAllPaymentMethodsListSuccessfully()
{
// given
$this->testHttpClient->mockResponse('payment_methods_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
// when
$paymentMethods = $paymentService->getPaymentMethods('PLN', 1000)->getAll();
// then
$this->assertNotEmpty($paymentMethods);
$this->assertEquals('2007', $paymentMethods[0]->getId());
$this->assertEquals('BLIK', $paymentMethods[0]->getName());
$this->assertEquals('https://static.sandbox.paynow.pl/payment-method-icons/2007.png', $paymentMethods[0]->getImage());
$this->assertEquals('Płacę z Blikiem', $paymentMethods[0]->getDescription());
$this->assertEquals(Type::BLIK, $paymentMethods[0]->getType());
$this->assertEquals(Status::ENABLED, $paymentMethods[0]->getStatus());
$this->assertTrue($paymentMethods[0]->isEnabled());
}
public function testShouldRetrieveAllPaymentMethodsListSuccessfullyForWhiteLabel()
{
// given
$this->testHttpClient->mockResponse('payment_methods_white_label_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
// when
$paymentMethods = $paymentService->getPaymentMethods('PLN', 1000, true)->getAll();
// then
$this->assertNotEmpty($paymentMethods);
$this->assertEquals('2007', $paymentMethods[0]->getId());
$this->assertEquals('BLIK', $paymentMethods[0]->getName());
$this->assertEquals('https://static.sandbox.paynow.pl/payment-method-icons/2007.png', $paymentMethods[0]->getImage());
$this->assertEquals(Status::ENABLED, $paymentMethods[0]->getStatus());
$this->assertEquals('Płacę z Blikiem', $paymentMethods[0]->getDescription());
$this->assertEquals(Type::BLIK, $paymentMethods[0]->getType());
$this->assertEquals(Status::ENABLED, $paymentMethods[0]->getStatus());
$this->assertEquals(AuthorizationType::CODE, $paymentMethods[0]->getAuthorizationType());
$this->assertTrue($paymentMethods[0]->isEnabled());
}
public function testShouldRetrieveOnlyBlikPaymentMethodsListSuccessfully()
{
// given
$this->testHttpClient->mockResponse('payment_methods_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
// when
$blikPaymentMethods = $paymentService->getPaymentMethods("PLN")->getOnlyBlik();
// then
$this->assertNotEmpty($blikPaymentMethods);
$this->assertEquals(1, sizeof($blikPaymentMethods));
$this->assertEquals('BLIK', $blikPaymentMethods[0]->getName());
$this->assertEquals(Type::BLIK, $blikPaymentMethods[0]->getType());
$this->assertTrue($blikPaymentMethods[0]->isEnabled());
}
public function testShouldRetrieveOnlyCardPaymentMethodsListSuccessfully()
{
// given
$this->testHttpClient->mockResponse('payment_methods_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
// when
$cardPaymentMethods = $paymentService->getPaymentMethods()->getOnlyCards();
// then
$this->assertNotEmpty($cardPaymentMethods);
$this->assertEquals(1, sizeof($cardPaymentMethods));
$this->assertEquals('Karta płatnicza', $cardPaymentMethods[0]->getName());
$this->assertEquals(Type::CARD, $cardPaymentMethods[0]->getType());
$this->assertTrue($cardPaymentMethods[0]->isEnabled());
}
public function testShouldRetrieveOnlyGooglePayPaymentMethodsListSuccessfully()
{
// given
$this->testHttpClient->mockResponse('payment_methods_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
// when
$cardPaymentMethods = $paymentService->getPaymentMethods()->getOnlyGooglePay();
// then
$this->assertNotEmpty($cardPaymentMethods);
$this->assertEquals(1, sizeof($cardPaymentMethods));
$this->assertEquals('Google Pay', $cardPaymentMethods[0]->getName());
$this->assertEquals(Type::GOOGLE_PAY, $cardPaymentMethods[0]->getType());
$this->assertTrue($cardPaymentMethods[0]->isEnabled());
}
public function testShouldRetrieveOnlyPblPaymentMethodsListSuccessfully()
{
// given
$this->testHttpClient->mockResponse('payment_methods_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
// when
$pblPaymentMethods = $paymentService->getPaymentMethods()->getOnlyPbls();
// then
$this->assertNotEmpty($pblPaymentMethods);
$this->assertEquals(3, sizeof($pblPaymentMethods));
$this->assertEquals('mTransfer', $pblPaymentMethods[0]->getName());
$this->assertEquals(Type::PBL, $pblPaymentMethods[0]->getType());
$this->assertTrue($pblPaymentMethods[0]->isEnabled());
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Paynow\Tests\Service;
use Paynow\Exception\PaynowException;
use Paynow\Service\Payment;
use Paynow\Tests\TestCase;
class PaymentTest extends TestCase
{
public function testShouldAuthorizePaymentSuccessfully()
{
// given
$this->testHttpClient->mockResponse('payment_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
$paymentData = $this->loadData('payment_request.json');
// when
$response = $paymentService->authorize($paymentData, 'idempotencyKey123');
// then
$this->assertNotEmpty($response->getRedirectUrl());
$this->assertNotEmpty($response->getPaymentId());
$this->assertNotEmpty($response->getStatus());
}
public function testShouldNotAuthorizePaymentSuccessfully()
{
// given
$this->testHttpClient->mockResponse('payment_failed.json', 400);
$this->client->setHttpClient($this->testHttpClient);
$paymentData = $this->loadData('payment_request.json');
$paymentService = new Payment($this->client);
// when
try {
$response = $paymentService->authorize($paymentData, 'idempotencyKey123');
} catch (PaynowException $exception) {
// then
$this->assertEquals(400, $exception->getCode());
$this->assertEquals('VALIDATION_ERROR', $exception->getErrors()[0]->getType());
$this->assertEquals(
'currency: invalid field value (EUR)',
$exception->getErrors()[0]->getMessage()
);
}
}
public function testShouldRetrievePaymentStatusSuccessfully()
{
// given
$this->testHttpClient->mockResponse('payment_status_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
$paymentId = 'PBYV-3AZ-UPW-DPC';
// when
$response = $paymentService->status($paymentId);
// then
$this->assertEquals($paymentId, $response->getPaymentId());
$this->assertEquals('NEW', $response->getStatus());
}
public function testShouldNotRetrievePaymentStatusSuccesfully()
{
// given
$this->testHttpClient->mockResponse('payment_status_not_found.json', 404);
$this->client->setHttpClient($this->testHttpClient);
$paymentService = new Payment($this->client);
$paymentId = 'PBYV-3AZ-UPW-DPC';
// when
try {
$response = $paymentService->status($paymentId);
} catch (PaynowException $exception) {
// then
$this->assertEquals(404, $exception->getCode());
$this->assertEquals('NOT_FOUND', $exception->getErrors()[0]->getType());
$this->assertEquals(
'Could not find status for payment {paymentId=PBYV-3AZ-UPW-DPC}',
$exception->getErrors()[0]->getMessage()
);
}
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Paynow\Tests\Service;
use Paynow\Exception\PaynowException;
use Paynow\Service\Refund;
use Paynow\Tests\TestCase;
class RefundTest extends TestCase
{
public function testShouldRefundPaymentSuccessfully()
{
// given
$this->testHttpClient->mockResponse('refund_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$refundService = new Refund($this->client);
// when
$response = $refundService->create('NOR3-FUN-D4U-LOL', 'idempotencyKey123', 100, null);
// then
$this->assertNotEmpty($response->getRefundId());
$this->assertNotEmpty($response->getStatus());
}
public function testShouldNotAuthorizePaymentSuccessfully()
{
// given
$this->testHttpClient->mockResponse('refund_failed.json', 400);
$this->client->setHttpClient($this->testHttpClient);
$refundService = new Refund($this->client);
// when
try {
$response = $refundService->create('NOR3-FUN-D4U-LOL', 'idempotencyKey123', 100, null);
} catch (PaynowException $exception) {
// then
$this->assertEquals(400, $exception->getCode());
$this->assertEquals('INSUFFICIENT_BALANCE_FUNDS', $exception->getErrors()[0]->getType());
$this->assertEquals(
'Insufficient funds on balance',
$exception->getErrors()[0]->getMessage()
);
}
}
public function testShouldRetrieveRefundStatusSuccessfully()
{
// given
$this->testHttpClient->mockResponse('refund_status_success.json', 200);
$this->client->setHttpClient($this->testHttpClient);
$refundService = new Refund($this->client);
$refundId = 'R3FU-UND-D8K-WZD';
// when
$response = $refundService->status($refundId);
// then
$this->assertEquals($refundId, $response->getRefundId());
$this->assertEquals('NEW', $response->getStatus());
}
public function testShouldNotRetrievePaymentStatusSuccesfully()
{
// given
$this->testHttpClient->mockResponse('refund_status_not_found.json', 404);
$this->client->setHttpClient($this->testHttpClient);
$refundService = new Refund($this->client);
$refundId = 'R3FU-UND-D8K-WZD';
// when
try {
$response = $refundService->status($refundId);
} catch (PaynowException $exception) {
// then
$this->assertEquals(404, $exception->getCode());
$this->assertEquals('NOT_FOUND', $exception->getErrors()[0]->getType());
$this->assertEquals(
'Could not find status for refund {refundId=R3FU-UND-D8K-WZD}',
$exception->getErrors()[0]->getMessage()
);
}
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Paynow\Tests\Service;
use Paynow\Exception\PaynowException;
use Paynow\Service\ShopConfiguration;
use Paynow\Tests\TestCase;
class ShopConfigurationTest extends TestCase
{
private $continueUrl = 'http://shopdomain.com/return';
private $notificationUrl = 'http://shopdomain.com/notifications';
public function testShouldUpdateShopConfigurationSuccessfully()
{
// given
$this->testHttpClient->mockResponse(null, 204);
$this->client->setHttpClient($this->testHttpClient);
$shopConfigurationService = new ShopConfiguration($this->client);
// when
$response = $shopConfigurationService->changeUrls($this->continueUrl, $this->notificationUrl);
// then
$this->assertEquals(204, $response->status);
}
public function testShouldNotUpdateShopConfigurationSuccessfully()
{
// given
$this->testHttpClient->mockResponse('shop_configuration_urls_failed.json', 400);
$this->client->setHttpClient($this->testHttpClient);
$shopConfigurationService = new ShopConfiguration($this->client);
// when
try {
$response = $shopConfigurationService->changeUrls($this->continueUrl, $this->notificationUrl);
} catch (PaynowException $exception) {
// then
$this->assertEquals(400, $exception->getCode());
$this->assertEquals('VALIDATION_ERROR', $exception->getErrors()[0]->getType());
$this->assertEquals(
'continue_url: invalid field value',
$exception->getErrors()[0]->getMessage()
);
}
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,36 @@
<?php
namespace Paynow\Tests;
use Paynow\Client;
use Paynow\Environment;
use PHPUnit\Framework\TestCase as BaseTestCase;
class TestCase extends BaseTestCase
{
protected $testHttpClient;
protected $client;
public function __construct($name = null, array $data = [], $dataName = '')
{
$this->client = new Client(
'TestApiKey',
'TestSignatureKey',
Environment::SANDBOX,
'PHPUnitTests'
);
$this->testHttpClient = new TestHttpClient($this->client->getConfiguration());
parent::__construct($name, $data, $dataName);
}
public function loadData($fileName, $asString = false)
{
$filePath = dirname(__FILE__).'/resources/'.$fileName;
if (! $asString) {
return json_decode(file_get_contents($filePath), true);
} else {
return file_get_contents($filePath);
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Paynow\Tests;
use GuzzleHttp\Psr7\Response;
use Http\Mock\Client;
use Paynow\HttpClient\HttpClient;
class TestHttpClient extends HttpClient
{
public function mockResponse($responseFile, $httpStatus)
{
$this->client = new Client();
$content = null;
if (null != $responseFile) {
$filePath = dirname(__FILE__).'/resources/'.$responseFile;
$content = file_get_contents($filePath, true);
}
$response = new Response($httpStatus, ['Content-Type' => 'application/json'], $content);
$this->client->addResponse($response);
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Paynow\Tests\Util;
use Paynow\Model\PaymentMethods\SavedInstrument;
use Paynow\Model\PaymentMethods\SavedInstrument\Status as SavedInstrumentStatus;
use Paynow\Tests\TestCase;
class SavedInstrumentTest extends TestCase
{
/**
* @dataProvider dataProvider
*/
public function testIsExpired($status, $isExpired)
{
// given + when
$savedInstrument = new SavedInstrument('test', strtotime('+1 month'), 'VISA', 'test', '1234', $status);
// then
$this->assertEquals($isExpired, $savedInstrument->isExpired());
}
public function dataProvider(): array
{
return [
[SavedInstrumentStatus::ACTIVE, false],
[SavedInstrumentStatus::EXPIRED_CARD, true],
[SavedInstrumentStatus::EXPIRED_TOKEN, true],
];
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Paynow\Tests\Util;
use InvalidArgumentException;
use Paynow\Tests\TestCase;
use Paynow\Util\SignatureCalculator;
class SignatureCalculatorTest extends TestCase
{
public function testNotValidSuccessfully()
{
// given + when
$signatureCalculator = new SignatureCalculator('InvalidSecretKey', json_encode(['key' => 'value']));
// then
$this->assertNotEquals('hash', $signatureCalculator->getHash());
}
public function testShouldValidSuccessfully()
{
// given + when
$signatureCalculator = new SignatureCalculator(
'a621a1fb-b4d8-48ba-a6a3-2a28ed61f605',
json_encode([
'key1' => 'value1',
'key2' => 'value2',
])
);
// then
$this->assertEquals('rFAkhfbUFRn4bTR82qb742Mwy34g/CSi8frEHciZhCU=', $signatureCalculator->getHash());
}
public function testExceptionForEmptyData()
{
// given
$this->expectException(InvalidArgumentException::class);
// when
$signatureCalculator = new SignatureCalculator('a621a1fb-b4d8-48ba-a6a3-2a28ed61f605', "");
// then
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,5 @@
<?php
require_once __DIR__.'/../vendor/autoload.php';
require_once __DIR__.'/TestCase.php';
require_once __DIR__.'/TestHttpClient.php';

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,7 @@
[
{
"title": "notice title",
"content": "notice content",
"locale": "pl-PL"
}
]

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,5 @@
{
"paymentId": "NOLV-8F9-08K-WGD",
"status": "CONFIRMED",
"modifiedAt": "2018-12-12T13:24:52"
}

View File

@@ -0,0 +1,9 @@
{
"statusCode": 400,
"errors": [
{
"errorType": "VALIDATION_ERROR",
"message": "currency: invalid field value (EUR)"
}
]
}

View File

@@ -0,0 +1,64 @@
[
{
"type": "BLIK",
"paymentMethods": [
{
"id": 2007,
"name": "BLIK",
"description": "Płacę z Blikiem",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2007.png",
"status": "ENABLED"
}
]
},
{
"type": "CARD",
"paymentMethods": [
{
"id": 2002,
"name": "Karta płatnicza",
"description": "Płatność kartą",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2002.png",
"status": "ENABLED"
}
]
},
{
"type": "GOOGLE_PAY",
"paymentMethods": [
{
"id": 2002,
"name": "Google Pay",
"description": "Google Pay",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2002.png",
"status": "ENABLED"
}
]
},
{
"type": "PBL",
"paymentMethods": [
{
"id": 1000,
"name": "mTransfer",
"description": "Zapłać przez mTransfer",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/1000.png",
"status": "ENABLED"
},
{
"id": 2031,
"name": "Płacę z IPKO",
"description": "Zapłać przez iPKO",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2031.png",
"status": "DISABLED"
},
{
"id": 2032,
"name": "Płać z ING",
"description": "Zapłać przez ING",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2032.png",
"status": "ENABLED"
}
]
}
]

View File

@@ -0,0 +1,70 @@
[
{
"type": "BLIK",
"paymentMethods": [
{
"id": 2007,
"name": "BLIK",
"description": "Płacę z Blikiem",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2007.png",
"status": "ENABLED",
"authorizationType": "CODE"
}
]
},
{
"type": "CARD",
"paymentMethods": [
{
"id": 2002,
"name": "Karta płatnicza",
"description": "Płatność kartą",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2002.png",
"status": "ENABLED",
"authorizationType": "REDIRECT"
}
]
},
{
"type": "GOOGLE_PAY",
"paymentMethods": [
{
"id": 2002,
"name": "Google Pay",
"description": "Google Pay",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2002.png",
"status": "ENABLED",
"authorizationType": "REDIRECT"
}
]
},
{
"type": "PBL",
"paymentMethods": [
{
"id": 1000,
"name": "mTransfer",
"description": "Zapłać przez mTransfer",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/1000.png",
"status": "ENABLED",
"authorizationType": "REDIRECT"
},
{
"id": 2031,
"name": "Płacę z IPKO",
"description": "Zapłać przez iPKO",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2031.png",
"status": "DISABLED",
"authorizationType": "REDIRECT"
},
{
"id": 2032,
"name": "Płać z ING",
"description": "Zapłać przez ING",
"image": "https://static.sandbox.paynow.pl/payment-method-icons/2032.png",
"status": "ENABLED",
"authorizationType": "REDIRECT"
}
]
}
]

View File

@@ -0,0 +1,9 @@
{
"amount": 100,
"currency": "PLN",
"externalId": "success_1234567",
"description": "Test payment",
"buyer": {
"email": "customer@domain.com"
}
}

View File

@@ -0,0 +1,9 @@
{
"statusCode": 404,
"errors": [
{
"errorType": "NOT_FOUND",
"message": "Could not find status for payment {paymentId=PBYV-3AZ-UPW-DPC}"
}
]
}

View File

@@ -0,0 +1,4 @@
{
"paymentId": "PBYV-3AZ-UPW-DPC",
"status": "NEW"
}

View File

@@ -0,0 +1,5 @@
{
"redirectUrl": "https://paywall.sandbox.paynow.pl/NO0U-5G2-XC1-ESX?token=eyJraWQiOiJhMDAyNjJjYS02NTU3LTRjOTktOGU0NC1kMTFlMTAxYjhhNTIiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJOTzBVLTVHMi1YQzEtRVNYIiwiYXVkIjoicGF5d2FsbC5zYW5kYm94LnBheW5vdy5wbCIsImlzcyI6InNhbmRib3gucGF5bm93LnBsIiwiZXhwIjoxNTY2ODk5MTYwLCJpYXQiOjE1NjY4MTI3NjB9.25TM-BXTDJV3NLEufgYVNZ7W_0JmJjS2Qu-0-d1HIQM",
"paymentId": "NO0U-5G2-XC1-ESX",
"status": "NEW"
}

View File

@@ -0,0 +1,9 @@
{
"statusCode": 400,
"errors": [
{
"errorType": "INSUFFICIENT_BALANCE_FUNDS",
"message": "Insufficient funds on balance"
}
]
}

View File

@@ -0,0 +1,9 @@
{
"statusCode": 404,
"errors": [
{
"errorType": "NOT_FOUND",
"message": "Could not find status for refund {refundId=R3FU-UND-D8K-WZD}"
}
]
}

View File

@@ -0,0 +1,4 @@
{
"refundId": "R3FU-UND-D8K-WZD",
"status": "NEW"
}

View File

@@ -0,0 +1,4 @@
{
"refundId": "REFX-123-435-213",
"status": "NEW"
}

View File

@@ -0,0 +1,9 @@
{
"statusCode": 400,
"errors": [
{
"errorType": "VALIDATION_ERROR",
"message": "continue_url: invalid field value"
}
]
}