diff --git a/EventListener/OrderLineEntityListener.php b/EventListener/OrderLineEntityListener.php
index 063eddd..e642d34 100644
--- a/EventListener/OrderLineEntityListener.php
+++ b/EventListener/OrderLineEntityListener.php
@@ -21,7 +21,6 @@
*/
class OrderLineEntityListener
{
-
public static $MOLLIE_MAPPED_ATTRIBUTES = ['freeFormProduct', 'quantity', 'priceType'];
/**
* @var MollieDtoMapperInterface
diff --git a/Form/EventListener/ChannelSettingsTypeSubscriber.php b/Form/EventListener/ChannelSettingsTypeSubscriber.php
index b64570e..28384f6 100644
--- a/Form/EventListener/ChannelSettingsTypeSubscriber.php
+++ b/Form/EventListener/ChannelSettingsTypeSubscriber.php
@@ -374,8 +374,8 @@ protected function setPaymentMethodConfigurations(ChannelSettings $channelSettin
$paymentMethodConfigs = $this->paymentMethodController->getAll($channelSettings->getWebsiteProfile()->getId());
foreach ($paymentMethodConfigs as $paymentMethodConfig) {
$paymentMethodSetting = null;
- $paymentMethodConfigMollieIds[] = $paymentMethodConfig->getMollieId();
- if (array_key_exists($paymentMethodConfig->getMollieId(), $paymentMethodSettingsMap)) {
+ $paymentMethodConfigMollieIds[] = $paymentMethodConfig->getMollieId();
+ if (array_key_exists($paymentMethodConfig->getMollieId(), $paymentMethodSettingsMap)) {
$paymentMethodSetting = $paymentMethodSettingsMap[$paymentMethodConfig->getMollieId()];
}
@@ -429,12 +429,12 @@ protected function setPaymentMethodConfigurations(ChannelSettings $channelSettin
);
}
- // Remove any saved method setting that is now missing in the Mollie API
- foreach ($paymentMethodSettingsMap as $mollieMethodId => $paymentMethodSetting) {
- if (!in_array($mollieMethodId, $paymentMethodConfigMollieIds)) {
- $channelSettings->removePaymentMethodSetting($paymentMethodSetting);
- }
- }
+ // Remove any saved method setting that is now missing in the Mollie API
+ foreach ($paymentMethodSettingsMap as $mollieMethodId => $paymentMethodSetting) {
+ if (!in_array($mollieMethodId, $paymentMethodConfigMollieIds)) {
+ $channelSettings->removePaymentMethodSetting($paymentMethodSetting);
+ }
+ }
}
/**
diff --git a/Form/Type/PaymentMethodSettingsType.php b/Form/Type/PaymentMethodSettingsType.php
index 3a3b7cd..c5bfd22 100644
--- a/Form/Type/PaymentMethodSettingsType.php
+++ b/Form/Type/PaymentMethodSettingsType.php
@@ -23,7 +23,6 @@
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Validator\Constraints\Range;
-
/**
* Form type for Mollie integration payment methods settings
*/
diff --git a/IntegrationCore/BusinessLogic/Authorization/AuthorizationService.php b/IntegrationCore/BusinessLogic/Authorization/AuthorizationService.php
index 4960ba5..e4cc838 100644
--- a/IntegrationCore/BusinessLogic/Authorization/AuthorizationService.php
+++ b/IntegrationCore/BusinessLogic/Authorization/AuthorizationService.php
@@ -53,6 +53,16 @@ public function reset()
}
}
+ /**
+ * Returns Authorization token
+ *
+ * @return string
+ */
+ public function getAuthToken()
+ {
+ return $this->getConfigService()->getAuthorizationToken();
+ }
+
/**
* @return Configuration
*/
diff --git a/IntegrationCore/BusinessLogic/Authorization/Interfaces/AuthorizationService.php b/IntegrationCore/BusinessLogic/Authorization/Interfaces/AuthorizationService.php
index cc6e13f..54ebe1e 100644
--- a/IntegrationCore/BusinessLogic/Authorization/Interfaces/AuthorizationService.php
+++ b/IntegrationCore/BusinessLogic/Authorization/Interfaces/AuthorizationService.php
@@ -30,4 +30,11 @@ public function validateToken(TokenInterface $token);
* Resets account
*/
public function reset();
+
+ /**
+ * Returns Authorization token
+ *
+ * @return string|null
+ */
+ public function getAuthToken();
}
diff --git a/IntegrationCore/BusinessLogic/Configuration.php b/IntegrationCore/BusinessLogic/Configuration.php
index 6f6f554..73efd40 100644
--- a/IntegrationCore/BusinessLogic/Configuration.php
+++ b/IntegrationCore/BusinessLogic/Configuration.php
@@ -2,6 +2,7 @@
namespace Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Connect\DTO\AuthInfo;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\WebsiteProfile;
use Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Logger\Logger;
@@ -20,18 +21,21 @@ abstract class Configuration extends \Mollie\Bundle\PaymentBundle\IntegrationCor
* @return string Integration version.
*/
abstract public function getIntegrationVersion();
+
/**
* Retrieves extension (plugin) name (for example MollieMagento2).
*
* @return string Extension name.
*/
abstract public function getExtensionName();
+
/**
* Retrieves extension (plugin) version.
*
* @return string Extension version.
*/
abstract public function getExtensionVersion();
+
/**
* Returns URL for checking extension version
*
@@ -62,6 +66,31 @@ public function removeConfigValue($name)
return $entity ? $this->getRepository()->delete($entity) : true;
}
+ /**
+ * Returns authorization token.
+ *
+ * @return AuthInfo|null Authorization token if found; otherwise, NULL.
+ */
+ public function getAuthorizationInfo()
+ {
+ $authInfo = json_decode($this->getConfigValue('authToken'), true);
+ if (empty($authInfo)) {
+ return null;
+ }
+
+ return AuthInfo::fromArray($authInfo);
+ }
+
+ /**
+ * Sets authorization token.
+ *
+ * @param AuthInfo $authInfo Authorization token.
+ */
+ public function setAuthorizationInfo($authInfo)
+ {
+ $this->saveConfigValue('authToken', json_encode($authInfo->toArray()));
+ }
+
/**
* Returns authorization token.
*
@@ -75,11 +104,31 @@ public function getAuthorizationToken()
/**
* Sets authorization token.
*
- * @param string $token Authorization token.
+ * @param string $authToken Authorization token.
+ */
+ public function setAuthorizationToken($authToken)
+ {
+ $this->saveConfigValue('authToken', $authToken);
+ }
+
+ /**
+ * Returns state string.
+ *
+ * @return string|null State string if found; otherwise, NULL.
+ */
+ public function getStateString()
+ {
+ return $this->getConfigValue('state') ?: null;
+ }
+
+ /**
+ * Sets authorization token.
+ *
+ * @param string $state State string.
*/
- public function setAuthorizationToken($token)
+ public function setStateString($state)
{
- $this->saveConfigValue('authToken', $token);
+ $this->saveConfigValue('state', $state);
}
/**
diff --git a/IntegrationCore/BusinessLogic/Connect/AuthorizationService.php b/IntegrationCore/BusinessLogic/Connect/AuthorizationService.php
new file mode 100644
index 0000000..b68c2fb
--- /dev/null
+++ b/IntegrationCore/BusinessLogic/Connect/AuthorizationService.php
@@ -0,0 +1,162 @@
+configuration = ServiceRegister::getService(Configuration::CLASS_NAME);
+ $this->tokenService = ServiceRegister::getService(TokenService::CLASS_NAME);
+ }
+
+
+ /**
+ * Gets string of Mollie permissions that are needed for application
+ *
+ * @return array
+ */
+ abstract public function getApplicationPermissions();
+
+ /**
+ * Gets clients id
+ *
+ * @return string
+ */
+ abstract public function getClientId();
+
+ /**
+ * Gets callback url
+ *
+ * @return string
+ */
+ abstract public function getRedirectUrl();
+
+ /**
+ * Get client secret
+ *
+ * @return string
+ */
+ abstract public function getClientSecret();
+
+ /**
+ * This function should generate Mollie authorize URL in the given language
+ *
+ * @param string $locale
+ *
+ * @return string
+ */
+ public function getAuthorizeUrl($locale, $state = null)
+ {
+ $params = array(
+ 'client_id' => $this->getClientId(),
+ 'redirect_uri' => $this->getRedirectUrl(),
+ 'state' => $state ?: $this->generateStateString(),
+ 'scope' => $this->formatApplicationPermissions(),
+ 'response_type' => 'code',
+ 'approval_prompt' => 'force',
+ 'locale' => $locale,
+ );
+
+ return static::AUTHORIZE_URL . '?' . http_build_query($params);
+ }
+
+ /**
+ * @return AuthInfo|null
+ * @throws UnprocessableEntityRequestException
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ */
+ public function getAuthInfo()
+ {
+ $authInfo = $this->configuration->getAuthorizationInfo();
+ if (!$authInfo) {
+ return null;
+ }
+
+ $currentTime = $this->getCurrentTimeInSeconds();
+ if ($authInfo->getAccessTokenDuration() < $currentTime) {
+ $authInfo = $this->tokenService->refreshToken($authInfo->getRefreshToken());
+ }
+
+ return $authInfo;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return string|null
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ * @throws UnprocessableEntityRequestException
+ */
+ abstract public function getAuthToken();
+
+ /**
+ * Generates state string
+ *
+ * @return string
+ */
+ private function generateStateString()
+ {
+ $state = md5(uniqid('', true));
+ $this->configuration->setStateString($state);
+
+ return $state;
+ }
+
+ /**
+ * @return string
+ */
+ private function formatApplicationPermissions()
+ {
+ $formattedPermission = '';
+ foreach ($this->getApplicationPermissions() as $permission) {
+ $formattedPermission .= $permission . ' ';
+ }
+
+ return trim($formattedPermission);
+ }
+
+ /**
+ * Returns current time in seconds
+ *
+ * @return int
+ */
+ private function getCurrentTimeInSeconds()
+ {
+ $time = new DateTime('now');
+
+ return $time->getTimestamp();
+ }
+}
diff --git a/IntegrationCore/BusinessLogic/Connect/DTO/AuthInfo.php b/IntegrationCore/BusinessLogic/Connect/DTO/AuthInfo.php
new file mode 100644
index 0000000..c86aab7
--- /dev/null
+++ b/IntegrationCore/BusinessLogic/Connect/DTO/AuthInfo.php
@@ -0,0 +1,116 @@
+accessToken = $accessToken;
+ $this->refreshToken = $refreshToken;
+ $this->accessTokenDuration = $accessTokenDuration;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAccessToken()
+ {
+ return $this->accessToken;
+ }
+
+ /**
+ * @param string $accessToken
+ */
+ public function setAccessToken($accessToken)
+ {
+ $this->accessToken = $accessToken;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRefreshToken()
+ {
+ return $this->refreshToken;
+ }
+
+ /**
+ * @param string $refreshToken
+ */
+ public function setRefreshToken($refreshToken)
+ {
+ $this->refreshToken = $refreshToken;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAccessTokenDuration()
+ {
+ return $this->accessTokenDuration;
+ }
+
+ /**
+ * @param int $accessTokenDuration
+ */
+ public function setAccessTokenDuration($accessTokenDuration)
+ {
+ $this->accessTokenDuration = $accessTokenDuration;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return array(
+ 'access_token' => $this->getAccessToken(),
+ 'refresh_token' => $this->getRefreshToken(),
+ 'expires_in' => $this->getAccessTokenDuration(),
+ );
+ }
+
+ /**
+ *
+ * @param array $raw
+ *
+ * @return AuthInfo
+ */
+ public static function fromArray(array $raw)
+ {
+ $authInfo = new AuthInfo('', '', 0);
+ $authInfo->setAccessToken(static::getValue($raw, 'access_token', null));
+ $authInfo->setRefreshToken(static::getValue($raw, 'refresh_token', null));
+ $authInfo->setAccessTokenDuration(static::getValue($raw, 'expires_in', null));
+
+ return $authInfo;
+ }
+}
diff --git a/IntegrationCore/BusinessLogic/Connect/DTO/TokenRequest.php b/IntegrationCore/BusinessLogic/Connect/DTO/TokenRequest.php
new file mode 100644
index 0000000..6db130a
--- /dev/null
+++ b/IntegrationCore/BusinessLogic/Connect/DTO/TokenRequest.php
@@ -0,0 +1,130 @@
+grantType = $grantType;
+ $this->code = $code;
+ $this->refreshToken = $refreshToken;
+ $this->redirectUrl = $redirectUrl;
+ }
+
+ /**
+ * @return string
+ */
+ public function getGrantType()
+ {
+ return $this->grantType;
+ }
+
+ /**
+ * @param string $grantType
+ */
+ public function setGrantType($grantType)
+ {
+ $this->grantType = $grantType;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCode()
+ {
+ return $this->code;
+ }
+
+ /**
+ * @param string $code
+ */
+ public function setCode($code)
+ {
+ $this->code = $code;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRefreshToken()
+ {
+ return $this->refreshToken;
+ }
+
+ /**
+ * @param string $refreshToken
+ */
+ public function setRefreshToken($refreshToken)
+ {
+ $this->refreshToken = $refreshToken;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRedirectUrl()
+ {
+ return $this->redirectUrl;
+ }
+
+ /**
+ * @param string $redirectUrl
+ */
+ public function setRedirectUrl($redirectUrl)
+ {
+ $this->redirectUrl = $redirectUrl;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ $array = array();
+ $array['grant_type'] = $this->getGrantType();
+ if ($this->getGrantType() === 'authorization_code') {
+ $array['code'] = $this->getCode();
+ } else {
+ $array['refresh_token'] = $this->getRefreshToken();
+ }
+
+ if ($this->getRedirectUrl() !== '') {
+ $array['redirect_uri'] = $this->getRedirectUrl();
+ }
+
+ return $array;
+ }
+}
diff --git a/IntegrationCore/BusinessLogic/Connect/TokenProxy.php b/IntegrationCore/BusinessLogic/Connect/TokenProxy.php
new file mode 100644
index 0000000..30309a4
--- /dev/null
+++ b/IntegrationCore/BusinessLogic/Connect/TokenProxy.php
@@ -0,0 +1,117 @@
+call(self::HTTP_METHOD_POST, '', $request->toArray(), $clientId, $clientSecret);
+ $body = json_decode($response->getBody(), true);
+ $body['expires_in'] = $this->getExpiresInTime($body['expires_in']);
+
+ return AuthInfo::fromArray($body);
+ }
+
+ /**
+ * Revoke given token
+ *
+ * @param string $type Type of token (access_token or refresh_token)
+ * @param string $token
+ *
+ * @return void
+ *
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ * @throws UnprocessableEntityRequestException
+ */
+ public function revokeToken($type, $token)
+ {
+ $body = array(
+ 'token_type_hint' => $type,
+ 'token' => $token,
+ );
+
+ $this->call(self::HTTP_METHOD_DELETE, '', $body);
+ }
+
+ /**
+ * @param string $method
+ * @param string $endpoint
+ * @param array $body
+ * @param string $clientId
+ * @param string $clientSecret
+ * @return HttpResponse
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ * @throws UnprocessableEntityRequestException
+ */
+ public function call($method, $endpoint, array $body = array(), $clientId = '', $clientSecret = '')
+ {
+ if ($this->configService->isTestMode()) {
+ $queryParams['testmode'] = 'true';
+ }
+
+ $url = static::TOKEN_URL . '?' . http_build_query($body);
+ $response = $this->client->request(
+ self::HTTP_METHOD_POST,
+ $url,
+ $this->getHeaders($clientId, $clientSecret),
+ $body
+ );
+
+ $this->validateResponse($response);
+
+ return $response;
+ }
+
+ /**
+ * @param integer $expiresIn
+ */
+ private function getExpiresInTime($expiresIn)
+ {
+ $now = new \DateTime('now');
+ $expiresIn -= 60;
+ $now = $now->modify("+ $expiresIn seconds");
+
+ return $now->getTimestamp();
+ }
+
+ /**
+ * Returns header
+ */
+ private function getHeaders($clientId, $clientSecret)
+ {
+ return array('Authorization: Basic ' . base64_encode($clientId . ':' . $clientSecret));
+ }
+}
diff --git a/IntegrationCore/BusinessLogic/Connect/TokenService.php b/IntegrationCore/BusinessLogic/Connect/TokenService.php
new file mode 100644
index 0000000..1b627a8
--- /dev/null
+++ b/IntegrationCore/BusinessLogic/Connect/TokenService.php
@@ -0,0 +1,82 @@
+tokenProxy = $tokenProxy;
+ }
+
+ /**
+ * Gets AuthInfo based on given code
+ *
+ * @param $authCode
+ * @param string $redirectUrl
+ * @param string $clientId
+ * @param string $clinetSecret
+ *
+ * @return AuthInfo
+ *
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ * @throws UnprocessableEntityRequestException
+ */
+ public function generate($authCode, $redirectUrl = '', $clientId = '', $clinetSecret = '')
+ {
+ $tokenRequest = new TokenRequest(static::AUTH_CODE, $authCode, '', $redirectUrl);
+
+ return $this->tokenProxy->retrieveTokens($tokenRequest, $clientId, $clinetSecret);
+ }
+
+ /**
+ * Gets AuthInfo based on given refresh token
+ *
+ * @param $refreshToken
+ * @param string $redirectUrl
+ * @param string $clientId
+ * @param string $clinetSecret
+ * @return AuthInfo
+ *
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ * @throws UnprocessableEntityRequestException
+ */
+ public function refreshToken($refreshToken, $redirectUrl = '', $clientId = '', $clinetSecret = '')
+ {
+ $tokenRequest = new TokenRequest(static::REFRESH_TOKEN, '', $refreshToken, $redirectUrl);
+
+ return $this->tokenProxy->retrieveTokens($tokenRequest, $clientId, $clinetSecret);
+ }
+}
diff --git a/IntegrationCore/BusinessLogic/Http/BaseProxy.php b/IntegrationCore/BusinessLogic/Http/BaseProxy.php
new file mode 100644
index 0000000..f818aac
--- /dev/null
+++ b/IntegrationCore/BusinessLogic/Http/BaseProxy.php
@@ -0,0 +1,235 @@
+client = $client;
+ $this->configService = $configService;
+ $this->transformer = $transformer;
+ }
+
+ /**
+ * Makes a HTTP call and returns response.
+ *
+ * @param string $method HTTP method (GET, POST, PUT, etc.).
+ * @param string $endpoint Endpoint resource on remote API.
+ * @param array $body Request payload body.
+ *
+ * @return HttpResponse Response from request.
+ *
+ * @throws HttpAuthenticationException
+ * @throws UnprocessableEntityRequestException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ */
+ public function call($method, $endpoint, array $body = array())
+ {
+ $endpoint = ltrim($endpoint, '/');
+
+ $response = $this->client->request(
+ $method,
+ $this->getRequestUrl($method, $endpoint),
+ $this->getRequestHeaders(),
+ $this->getBodyAsString($method, $endpoint, $body)
+ );
+
+ $this->validateResponse($response);
+
+ return $response;
+ }
+
+ /**
+ * Creates full request URL for a given endpoint
+ *
+ * @param string $method HTTP method (GET, POST, PUT, etc.).
+ * @param string $endpoint Endpoint resource on remote API.
+ *
+ * @return string
+ */
+ protected function getRequestUrl($method, $endpoint)
+ {
+ $url = static::BASE_URL . static::API_VERSION . $endpoint;
+ $this->transformer->adjustUrl($url, $endpoint, $method);
+
+ return $url;
+ }
+
+ /**
+ * @param string $method HTTP method (GET, POST, PUT, etc.).
+ * @param string $endpoint Endpoint resource on remote API.
+ * @param array $body Request payload body.
+ *
+ * @return false|string
+ */
+ protected function getBodyAsString($method, $endpoint, array $body = array())
+ {
+ if (strtoupper($method) === self::HTTP_METHOD_GET) {
+ return '';
+ }
+
+ $this->transformer->adjustBody($body, $endpoint);
+
+ return empty($body) ? '{}' : json_encode($body);
+ }
+
+ /**
+ * Validates HTTP response.
+ *
+ * @param HttpResponse $response HTTP response returned from API call.
+ *
+ * @throws HttpAuthenticationException
+ * @throws UnprocessableEntityRequestException
+ * @throws HttpRequestException
+ */
+ protected function validateResponse(HttpResponse $response)
+ {
+ if (!$response->isSuccessful()) {
+ $httpCode = $response->getStatus();
+ $error = $response->decodeBodyAsJson();
+ $errorMessage = '';
+ if (is_array($error)) {
+ if (array_key_exists('title', $error)) {
+ $errorMessage = $error['title'];
+ } elseif (array_key_exists('error', $error)) {
+ $errorMessage = $error['error'];
+ }
+
+ if (array_key_exists('detail', $error)) {
+ $errorMessage .= ": {$error['detail']}";
+ }
+ }
+
+ Logger::logInfo(
+ 'Request to Mollie API was not successful.',
+ 'Core',
+ array(
+ 'ApiErrorMessage' => $errorMessage
+ )
+ );
+
+ if ($httpCode === self::HTTP_STATUS_CODE_UNAUTHORIZED) {
+ throw new HttpAuthenticationException($errorMessage, $httpCode);
+ }
+
+ if ($httpCode === self::HTTP_STATUS_CODE_UNPROCESSABLE) {
+ throw new UnprocessableEntityRequestException(array_key_exists('field', $error) ? $error['field'] : '', $errorMessage, $httpCode);
+ }
+
+ throw new HttpRequestException($errorMessage, $httpCode);
+ }
+ }
+
+ /**
+ * Returns headers together with authorization entry.
+ *
+ * @return array Formatted request headers.
+ */
+ protected function getRequestHeaders()
+ {
+ $userAgents = array(
+ 'PHP/' . PHP_VERSION,
+ str_replace(
+ array(' ', "\t", "\n", "\r"),
+ '-',
+ $this->configService->getIntegrationName() . '/' . $this->configService->getIntegrationVersion()
+ ),
+ str_replace(
+ array(' ', "\t", "\n", "\r"),
+ '-',
+ $this->configService->getExtensionName() . '/' . $this->configService->getExtensionVersion()
+ ),
+ );
+
+ return array(
+ 'accept' => 'Accept: application/json',
+ 'content' => 'Content-Type: application/json',
+ 'useragent' => 'User-Agent: ' . implode(' ', $userAgents),
+ 'token' => 'Authorization: Bearer ' . $this->getAuthorizationService()->getAuthToken(),
+ );
+ }
+
+ /**
+ * Returns registered authorization service
+ *
+ * @return AuthorizationService
+ */
+ protected function getAuthorizationService()
+ {
+ return ServiceRegister::getService(AuthorizationService::CLASS_NAME);
+ }
+}
diff --git a/IntegrationCore/BusinessLogic/Http/DTO/Amount.php b/IntegrationCore/BusinessLogic/Http/DTO/Amount.php
index 8f23d32..5eeeb0a 100644
--- a/IntegrationCore/BusinessLogic/Http/DTO/Amount.php
+++ b/IntegrationCore/BusinessLogic/Http/DTO/Amount.php
@@ -32,14 +32,31 @@ class Amount extends BaseDto
/* */
private static $map = array(
'BHD' => 3,
+ 'XOF' => 0,
+ 'BIF' => 0,
+ 'XAF' => 0,
+ 'CLP' => 0,
'CLF' => 4,
+ 'KMF' => 0,
+ 'DJF' => 0,
+ 'XPF' => 0,
+ 'GNF' => 0,
+ 'ISK' => 0,
'IQD' => 3,
+ 'JPY' => 0,
'JOD' => 3,
+ 'KRW' => 0,
'KWD' => 3,
'LYD' => 3,
'OMR' => 3,
+ 'PYG' => 0,
+ 'RWF' => 0,
'TND' => 3,
+ 'UGX' => 0,
+ 'UYI' => 0,
'UYW' => 4,
+ 'VUV' => 0,
+ 'VND' => 0,
);
/* */
@@ -104,7 +121,7 @@ public function getAmountValue()
*/
public function getAmountValueInSmallestUnit()
{
- return (int)((float)$this->value * pow(10, $this->getMinorUnits()));
+ return (int)round((float)$this->value * pow(10, $this->getMinorUnits()), 0);
}
/**
diff --git a/IntegrationCore/BusinessLogic/Http/DTO/Orders/Order.php b/IntegrationCore/BusinessLogic/Http/DTO/Orders/Order.php
index ca7e781..f94b850 100644
--- a/IntegrationCore/BusinessLogic/Http/DTO/Orders/Order.php
+++ b/IntegrationCore/BusinessLogic/Http/DTO/Orders/Order.php
@@ -34,7 +34,7 @@ class Order extends BaseDto
/**
* @var string[]
*/
- protected $method;
+ protected $methods;
/**
* @var Amount
*/
@@ -156,7 +156,7 @@ public static function fromArray(array $raw)
$order->profileId = static::getValue($raw, 'profileId');
$method = static::getValue($raw, 'method', array());
$method = is_array($method) ? $method : array($method);
- $order->method = $method;
+ $order->methods = $method;
$order->amount = Amount::fromArray(static::getValue($raw, 'amount', array()));
$order->amountCaptured = Amount::fromArray(static::getValue($raw, 'amountCaptured', array()));
$order->amountRefunded = Amount::fromArray(static::getValue($raw, 'amountRefunded', array()));
@@ -232,7 +232,7 @@ public function toArray()
'resource' => $this->resource,
'id' => $this->id,
'profileId' => $this->profileId,
- 'method' => $this->method,
+ 'method' => $this->methods,
'amount' => $this->amount->toArray(),
'amountRefunded' => $this->amountRefunded->toArray(),
'amountCaptured' => $this->amountCaptured->toArray(),
@@ -313,17 +313,17 @@ public function setProfileId($profileId)
/**
* @return string[]
*/
- public function getMethod()
+ public function getMethods()
{
- return $this->method;
+ return $this->methods;
}
/**
* @param string[] $method
*/
- public function setMethod($method)
+ public function setMethods($method)
{
- $this->method = $method;
+ $this->methods = $method;
}
/**
diff --git a/IntegrationCore/BusinessLogic/Http/DTO/Payment.php b/IntegrationCore/BusinessLogic/Http/DTO/Payment.php
index dfa91c2..b9fbeb3 100644
--- a/IntegrationCore/BusinessLogic/Http/DTO/Payment.php
+++ b/IntegrationCore/BusinessLogic/Http/DTO/Payment.php
@@ -59,7 +59,7 @@ class Payment extends BaseDto
/**
* @var string[]
*/
- protected $method;
+ protected $methods;
/**
* @var Address
*/
@@ -122,8 +122,7 @@ public static function fromArray(array $raw)
$result->issuer = static::getValue($raw, 'issuer');
$result->setLocale(static::getValue($raw, 'locale'));
$method = static::getValue($raw, 'method', array());
- $method = is_array($method) ? $method : array($method);
- $result->method = $method;
+ $result->methods = is_array($method) ? $method : array($method);
$result->metadata = static::getValue($raw, 'metadata', array());
$result->dueDate = \DateTime::createFromFormat(Order::MOLLIE_DATE_FORMAT, static::getValue($raw, 'dueDate'));
@@ -180,7 +179,7 @@ public function toArray()
'redirectUrl' => $this->redirectUrl,
'webhookUrl' => $this->webhookUrl,
'locale' => $this->locale,
- 'method' => $this->method,
+ 'method' => $this->methods,
'metadata' => $this->metadata,
'cardToken' => $this->cardToken,
'issuer' => $this->issuer,
@@ -378,17 +377,17 @@ public function setLocale($locale)
/**
* @return string[]
*/
- public function getMethod()
+ public function getMethods()
{
- return $this->method;
+ return $this->methods;
}
/**
* @param string[] $method
*/
- public function setMethod($method)
+ public function setMethods($method)
{
- $this->method = $method;
+ $this->methods = $method;
}
/**
diff --git a/IntegrationCore/BusinessLogic/Http/OrgToken/ProxyDataProvider.php b/IntegrationCore/BusinessLogic/Http/OrgToken/ProxyDataProvider.php
index f479d65..e063f8b 100644
--- a/IntegrationCore/BusinessLogic/Http/OrgToken/ProxyDataProvider.php
+++ b/IntegrationCore/BusinessLogic/Http/OrgToken/ProxyDataProvider.php
@@ -39,7 +39,7 @@ class ProxyDataProvider
public function transformPayment(Payment $payment)
{
- $method = $payment->getMethod();
+ $method = $payment->getMethods();
if (count($method) === 1) {
$method = implode('', $method);
}
@@ -85,7 +85,7 @@ public function transformOrder(Order $order)
$orderLines[] = $totalAdjustment;
}
- $method = $order->getMethod();
+ $method = $order->getMethods();
if (count($method) === 1) {
$method = implode('', $method);
}
diff --git a/IntegrationCore/BusinessLogic/Http/Proxy.php b/IntegrationCore/BusinessLogic/Http/Proxy.php
index bdf29e7..b65abee 100644
--- a/IntegrationCore/BusinessLogic/Http/Proxy.php
+++ b/IntegrationCore/BusinessLogic/Http/Proxy.php
@@ -12,88 +12,19 @@
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\TokenPermission;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\WebsiteProfile;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException;
-use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\OrgToken\ProxyDataProvider;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\PaymentMethod\Model\PaymentMethodConfig;
use Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpAuthenticationException;
use Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpCommunicationException;
use Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpRequestException;
-use Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\HttpClient;
-use Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\HttpResponse;
use Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Logger\LogData;
-use Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Logger\Logger;
-use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Configuration;
/**
* Class Proxy. In charge for communication with Mollie API.
*
* @package Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http
*/
-class Proxy
+class Proxy extends BaseProxy
{
- /**
- * Fully qualified name of this class.
- */
- const CLASS_NAME = __CLASS__;
- /**
- * Mollie base API URL.
- */
- const BASE_URL = 'https://api.mollie.com/';
- /**
- * Mollie API version
- */
- const API_VERSION = 'v2/';
- /**
- * Unauthorized HTTP status code.
- */
- const HTTP_STATUS_CODE_UNAUTHORIZED = 401;
- /**
- * Unprocessable entity status code
- */
- const HTTP_STATUS_CODE_UNPROCESSABLE = 422;
- /**
- * HTTP GET method
- */
- const HTTP_METHOD_GET = 'GET';
- /**
- * HTTP POST method
- */
- const HTTP_METHOD_POST = 'POST';
- /**
- * HTTP PUT method
- */
- const HTTP_METHOD_PATCH = 'PATCH';
- /**
- * HTTP DELETE method
- */
- const HTTP_METHOD_DELETE = 'DELETE';
- /**
- * HTTP Client.
- *
- * @var HttpClient
- */
- private $client;
- /**
- * @var Configuration
- */
- private $configService;
- /**
- * @var ProxyDataProvider
- */
- private $transformer;
-
- /**
- * Proxy constructor.
- *
- * @param Configuration $configService Configuration service.
- * @param HttpClient $client System HTTP client.
- * @param ProxyDataProvider $transformer
- */
- public function __construct(Configuration $configService, HttpClient $client, ProxyDataProvider $transformer)
- {
- $this->client = $client;
- $this->configService = $configService;
- $this->transformer = $transformer;
- }
/**
* @param LogData $data
@@ -292,7 +223,11 @@ public function getWebsiteProfiles()
*/
public function getAllPaymentMethods()
{
- $response = $this->call(self::HTTP_METHOD_GET, '/methods/all');
+ $params = array(
+ 'include' => 'issuers',
+ );
+ $queryString = http_build_query($params);
+ $response = $this->call(self::HTTP_METHOD_GET, "/methods/all?{$queryString}");
$result = $response->decodeBodyAsJson();
return PaymentMethod::fromArrayBatch(
@@ -531,136 +466,6 @@ public function getShipments($orderId)
return Shipment::fromArrayBatch($result['_embedded']['shipments']);
}
- /**
- * Makes a HTTP call and returns response.
- *
- * @param string $method HTTP method (GET, POST, PUT, etc.).
- * @param string $endpoint Endpoint resource on remote API.
- * @param array $body Request payload body.
- *
- * @return HttpResponse Response from request.
- *
- * @throws HttpAuthenticationException
- * @throws UnprocessableEntityRequestException
- * @throws HttpCommunicationException
- * @throws HttpRequestException
- */
- protected function call($method, $endpoint, array $body = array())
- {
- $endpoint = ltrim($endpoint, '/');
-
- $response = $this->client->request(
- $method,
- $this->getRequestUrl($method, $endpoint),
- $this->getRequestHeaders(),
- $this->getBodyAsString($method, $endpoint, $body)
- );
-
- $this->validateResponse($response);
-
- return $response;
- }
-
- /**
- * Creates full request URL for a given endpoint
- *
- * @param string $method HTTP method (GET, POST, PUT, etc.).
- * @param string $endpoint Endpoint resource on remote API.
- *
- * @return string
- */
- protected function getRequestUrl($method, $endpoint)
- {
- $url = static::BASE_URL . static::API_VERSION . $endpoint;
- $this->transformer->adjustUrl($url, $endpoint, $method);
-
- return $url;
- }
-
- /**
- * @param string $method HTTP method (GET, POST, PUT, etc.).
- * @param string $endpoint Endpoint resource on remote API.
- * @param array $body Request payload body.
- *
- * @return false|string
- */
- protected function getBodyAsString($method, $endpoint, array $body = array())
- {
- if (strtoupper($method) === self::HTTP_METHOD_GET) {
- return '';
- }
-
- $this->transformer->adjustBody($body, $endpoint);
-
- return empty($body) ? '{}' : json_encode($body);
- }
-
- /**
- * Validates HTTP response.
- *
- * @param HttpResponse $response HTTP response returned from API call.
- *
- * @throws HttpAuthenticationException
- * @throws UnprocessableEntityRequestException
- * @throws HttpRequestException
- */
- protected function validateResponse(HttpResponse $response)
- {
- if (!$response->isSuccessful()) {
- $httpCode = $response->getStatus();
- $error = $message = $response->decodeBodyAsJson();
- if (is_array($error)) {
- $message = "{$error['title']}: {$error['detail']}";
- }
-
- Logger::logInfo(
- 'Request to Mollie API was not successful.',
- 'Core',
- array(
- 'ApiErrorMessage' => $message
- )
- );
- if ($httpCode === self::HTTP_STATUS_CODE_UNAUTHORIZED) {
- throw new HttpAuthenticationException($message, $httpCode);
- }
-
- if ($httpCode === self::HTTP_STATUS_CODE_UNPROCESSABLE) {
- throw new UnprocessableEntityRequestException(array_key_exists('field', $error) ? $error['field'] : '', $message, $httpCode);
- }
-
- throw new HttpRequestException($message, $httpCode);
- }
- }
-
- /**
- * Returns headers together with authorization entry.
- *
- * @return array Formatted request headers.
- */
- protected function getRequestHeaders()
- {
- $userAgents = array(
- 'PHP/'.PHP_VERSION,
- str_replace(
- array(' ', "\t", "\n", "\r"),
- '-',
- $this->configService->getIntegrationName().'/'.$this->configService->getIntegrationVersion()
- ),
- str_replace(
- array(' ', "\t", "\n", "\r"),
- '-',
- $this->configService->getExtensionName().'/'.$this->configService->getExtensionVersion()
- ),
- );
-
- return array(
- 'accept' => 'Accept: application/json',
- 'content' => 'Content-Type: application/json',
- 'useragent' => 'User-Agent: '.implode(' ', $userAgents),
- 'token' => 'Authorization: Bearer ' . $this->configService->getAuthorizationToken(),
- );
- }
-
/**
* @param string $apiMethod
* @param string $billingCountry
diff --git a/IntegrationCore/BusinessLogic/PaymentMethod/Model/PaymentMethodConfig.php b/IntegrationCore/BusinessLogic/PaymentMethod/Model/PaymentMethodConfig.php
index f3f32bd..ce025eb 100644
--- a/IntegrationCore/BusinessLogic/PaymentMethod/Model/PaymentMethodConfig.php
+++ b/IntegrationCore/BusinessLogic/PaymentMethod/Model/PaymentMethodConfig.php
@@ -35,19 +35,25 @@ class PaymentMethodConfig extends Entity
const PRODUCT_ATTRIBUTE_DEFAULT = 'mollie_voucher_category';
const DEFAULT_TRANSACTION_DESCRIPTION = '{orderNumber}';
+
/**
* @var string[]
*/
- protected static $adiMethodRestrictions = array(
+ protected static $apiMethodRestrictions = array(
PaymentMethods::KlarnaPayLater => self::API_METHOD_ORDERS,
PaymentMethods::KlarnaSliceIt => self::API_METHOD_ORDERS,
+ PaymentMethods::KlarnaPayNow => self::API_METHOD_ORDERS,
PaymentMethods::Vouchers => self::API_METHOD_ORDERS
);
/**
* @var array
*/
- protected static $surchargeRestrictedPaymentMethods = array(PaymentMethods::KlarnaPayLater, PaymentMethods::KlarnaSliceIt);
+ protected static $surchargeRestrictedPaymentMethods = array(
+ PaymentMethods::KlarnaPayLater,
+ PaymentMethods::KlarnaSliceIt,
+ PaymentMethods::KlarnaPayNow,
+ );
/**
* @var array
@@ -82,6 +88,7 @@ class PaymentMethodConfig extends Entity
'transactionDescription',
'voucherCategory',
'productAttribute',
+ 'sortOrder',
);
/**
@@ -133,6 +140,10 @@ class PaymentMethodConfig extends Entity
* @var int
*/
protected $daysToPaymentExpire;
+ /**
+ * @var int
+ */
+ protected $sortOrder = 0;
/**
* @var string
*/
@@ -195,7 +206,7 @@ public function isSurchargeRestricted()
*/
public function isApiMethodRestricted()
{
- return array_key_exists($this->getMollieId(), static::$adiMethodRestrictions);
+ return array_key_exists($this->getMollieId(), static::$apiMethodRestrictions);
}
/**
@@ -296,14 +307,14 @@ public function setApiMethod($apiMethod)
if (
$this->isApiMethodRestricted() &&
- $apiMethod !== static::$adiMethodRestrictions[$this->getMollieId()]
+ $apiMethod !== static::$apiMethodRestrictions[$this->getMollieId()]
) {
throw new \InvalidArgumentException(
sprintf(
'Invalid payment method api value %s. Payment method %s supports only %s API method',
$apiMethod,
$this->getMollieId(),
- static::$adiMethodRestrictions[$this->getMollieId()]
+ static::$apiMethodRestrictions[$this->getMollieId()]
)
);
}
@@ -494,4 +505,20 @@ public function setProductAttribute($productAttribute)
{
$this->productAttribute = $productAttribute;
}
+
+ /**
+ * @return mixed
+ */
+ public function getSortOrder()
+ {
+ return $this->sortOrder;
+ }
+
+ /**
+ * @param mixed $sortOrder
+ */
+ public function setSortOrder($sortOrder)
+ {
+ $this->sortOrder = $sortOrder;
+ }
}
diff --git a/IntegrationCore/BusinessLogic/PaymentMethod/PaymentMethodService.php b/IntegrationCore/BusinessLogic/PaymentMethod/PaymentMethodService.php
index daf04c5..be02ee1 100644
--- a/IntegrationCore/BusinessLogic/PaymentMethod/PaymentMethodService.php
+++ b/IntegrationCore/BusinessLogic/PaymentMethod/PaymentMethodService.php
@@ -186,7 +186,7 @@ public function clearAllOther($profileId)
*
* @return PaymentMethodConfig[]
*/
- protected function getPaymentMethodConfigurationsMap($profileId)
+ public function getPaymentMethodConfigurationsMap($profileId)
{
$paymentMethodConfigsMap = array();
diff --git a/IntegrationCore/BusinessLogic/PaymentMethod/PaymentMethods.php b/IntegrationCore/BusinessLogic/PaymentMethod/PaymentMethods.php
index fad9a76..fab66e6 100644
--- a/IntegrationCore/BusinessLogic/PaymentMethod/PaymentMethods.php
+++ b/IntegrationCore/BusinessLogic/PaymentMethod/PaymentMethods.php
@@ -12,6 +12,7 @@ class PaymentMethods
const PayPal = 'paypal';
const KlarnaPayLater = 'klarnapaylater';
const KlarnaSliceIt = 'klarnasliceit';
+ const KlarnaPayNow = 'klarnapaynow';
const CreditCard = 'creditcard';
const iDEAL = 'ideal';
const KBC = 'kbc';
diff --git a/IntegrationCore/BusinessLogic/Resources/currencies/currencies.json b/IntegrationCore/BusinessLogic/Resources/currencies/currencies.json
new file mode 100644
index 0000000..a4fd8c4
--- /dev/null
+++ b/IntegrationCore/BusinessLogic/Resources/currencies/currencies.json
@@ -0,0 +1,1073 @@
+{
+ "USD": {
+ "symbol": "$",
+ "name": "US Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "USD",
+ "name_plural": "US dollars"
+ },
+ "CAD": {
+ "symbol": "CA$",
+ "name": "Canadian Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "CAD",
+ "name_plural": "Canadian dollars"
+ },
+ "EUR": {
+ "symbol": "€",
+ "name": "Euro",
+ "symbol_native": "€",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "EUR",
+ "name_plural": "euros"
+ },
+ "AED": {
+ "symbol": "AED",
+ "name": "United Arab Emirates Dirham",
+ "symbol_native": "د.إ.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "AED",
+ "name_plural": "UAE dirhams"
+ },
+ "AFN": {
+ "symbol": "Af",
+ "name": "Afghan Afghani",
+ "symbol_native": "؋",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "AFN",
+ "name_plural": "Afghan Afghanis"
+ },
+ "ALL": {
+ "symbol": "ALL",
+ "name": "Albanian Lek",
+ "symbol_native": "Lek",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "ALL",
+ "name_plural": "Albanian lekë"
+ },
+ "AMD": {
+ "symbol": "AMD",
+ "name": "Armenian Dram",
+ "symbol_native": "դր.",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "AMD",
+ "name_plural": "Armenian drams"
+ },
+ "ARS": {
+ "symbol": "AR$",
+ "name": "Argentine Peso",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "ARS",
+ "name_plural": "Argentine pesos"
+ },
+ "AUD": {
+ "symbol": "AU$",
+ "name": "Australian Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "AUD",
+ "name_plural": "Australian dollars"
+ },
+ "AZN": {
+ "symbol": "man.",
+ "name": "Azerbaijani Manat",
+ "symbol_native": "ман.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "AZN",
+ "name_plural": "Azerbaijani manats"
+ },
+ "BAM": {
+ "symbol": "KM",
+ "name": "Bosnia-Herzegovina Convertible Mark",
+ "symbol_native": "KM",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BAM",
+ "name_plural": "Bosnia-Herzegovina convertible marks"
+ },
+ "BDT": {
+ "symbol": "Tk",
+ "name": "Bangladeshi Taka",
+ "symbol_native": "৳",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BDT",
+ "name_plural": "Bangladeshi takas"
+ },
+ "BGN": {
+ "symbol": "BGN",
+ "name": "Bulgarian Lev",
+ "symbol_native": "лв.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BGN",
+ "name_plural": "Bulgarian leva"
+ },
+ "BHD": {
+ "symbol": "BD",
+ "name": "Bahraini Dinar",
+ "symbol_native": "د.ب.",
+ "decimal_digits": 3,
+ "rounding": 0,
+ "code": "BHD",
+ "name_plural": "Bahraini dinars"
+ },
+ "BIF": {
+ "symbol": "FBu",
+ "name": "Burundian Franc",
+ "symbol_native": "FBu",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "BIF",
+ "name_plural": "Burundian francs"
+ },
+ "BND": {
+ "symbol": "BN$",
+ "name": "Brunei Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BND",
+ "name_plural": "Brunei dollars"
+ },
+ "BOB": {
+ "symbol": "Bs",
+ "name": "Bolivian Boliviano",
+ "symbol_native": "Bs",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BOB",
+ "name_plural": "Bolivian bolivianos"
+ },
+ "BRL": {
+ "symbol": "R$",
+ "name": "Brazilian Real",
+ "symbol_native": "R$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BRL",
+ "name_plural": "Brazilian reals"
+ },
+ "BWP": {
+ "symbol": "BWP",
+ "name": "Botswanan Pula",
+ "symbol_native": "P",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BWP",
+ "name_plural": "Botswanan pulas"
+ },
+ "BYN": {
+ "symbol": "Br",
+ "name": "Belarusian Ruble",
+ "symbol_native": "руб.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BYN",
+ "name_plural": "Belarusian rubles"
+ },
+ "BZD": {
+ "symbol": "BZ$",
+ "name": "Belize Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "BZD",
+ "name_plural": "Belize dollars"
+ },
+ "CDF": {
+ "symbol": "CDF",
+ "name": "Congolese Franc",
+ "symbol_native": "FrCD",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "CDF",
+ "name_plural": "Congolese francs"
+ },
+ "CHF": {
+ "symbol": "CHF",
+ "name": "Swiss Franc",
+ "symbol_native": "CHF",
+ "decimal_digits": 2,
+ "rounding": 0.05,
+ "code": "CHF",
+ "name_plural": "Swiss francs"
+ },
+ "CLP": {
+ "symbol": "CL$",
+ "name": "Chilean Peso",
+ "symbol_native": "$",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "CLP",
+ "name_plural": "Chilean pesos"
+ },
+ "CNY": {
+ "symbol": "CN¥",
+ "name": "Chinese Yuan",
+ "symbol_native": "CN¥",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "CNY",
+ "name_plural": "Chinese yuan"
+ },
+ "COP": {
+ "symbol": "CO$",
+ "name": "Colombian Peso",
+ "symbol_native": "$",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "COP",
+ "name_plural": "Colombian pesos"
+ },
+ "CRC": {
+ "symbol": "₡",
+ "name": "Costa Rican Colón",
+ "symbol_native": "₡",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "CRC",
+ "name_plural": "Costa Rican colóns"
+ },
+ "CVE": {
+ "symbol": "CV$",
+ "name": "Cape Verdean Escudo",
+ "symbol_native": "CV$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "CVE",
+ "name_plural": "Cape Verdean escudos"
+ },
+ "CZK": {
+ "symbol": "Kč",
+ "name": "Czech Republic Koruna",
+ "symbol_native": "Kč",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "CZK",
+ "name_plural": "Czech Republic korunas"
+ },
+ "DJF": {
+ "symbol": "Fdj",
+ "name": "Djiboutian Franc",
+ "symbol_native": "Fdj",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "DJF",
+ "name_plural": "Djiboutian francs"
+ },
+ "DKK": {
+ "symbol": "Dkr",
+ "name": "Danish Krone",
+ "symbol_native": "kr",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "DKK",
+ "name_plural": "Danish kroner"
+ },
+ "DOP": {
+ "symbol": "RD$",
+ "name": "Dominican Peso",
+ "symbol_native": "RD$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "DOP",
+ "name_plural": "Dominican pesos"
+ },
+ "DZD": {
+ "symbol": "DA",
+ "name": "Algerian Dinar",
+ "symbol_native": "د.ج.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "DZD",
+ "name_plural": "Algerian dinars"
+ },
+ "EEK": {
+ "symbol": "Ekr",
+ "name": "Estonian Kroon",
+ "symbol_native": "kr",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "EEK",
+ "name_plural": "Estonian kroons"
+ },
+ "EGP": {
+ "symbol": "EGP",
+ "name": "Egyptian Pound",
+ "symbol_native": "ج.م.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "EGP",
+ "name_plural": "Egyptian pounds"
+ },
+ "ERN": {
+ "symbol": "Nfk",
+ "name": "Eritrean Nakfa",
+ "symbol_native": "Nfk",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "ERN",
+ "name_plural": "Eritrean nakfas"
+ },
+ "ETB": {
+ "symbol": "Br",
+ "name": "Ethiopian Birr",
+ "symbol_native": "Br",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "ETB",
+ "name_plural": "Ethiopian birrs"
+ },
+ "GBP": {
+ "symbol": "£",
+ "name": "British Pound Sterling",
+ "symbol_native": "£",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "GBP",
+ "name_plural": "British pounds sterling"
+ },
+ "GEL": {
+ "symbol": "GEL",
+ "name": "Georgian Lari",
+ "symbol_native": "GEL",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "GEL",
+ "name_plural": "Georgian laris"
+ },
+ "GHS": {
+ "symbol": "GH₵",
+ "name": "Ghanaian Cedi",
+ "symbol_native": "GH₵",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "GHS",
+ "name_plural": "Ghanaian cedis"
+ },
+ "GNF": {
+ "symbol": "FG",
+ "name": "Guinean Franc",
+ "symbol_native": "FG",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "GNF",
+ "name_plural": "Guinean francs"
+ },
+ "GTQ": {
+ "symbol": "GTQ",
+ "name": "Guatemalan Quetzal",
+ "symbol_native": "Q",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "GTQ",
+ "name_plural": "Guatemalan quetzals"
+ },
+ "HKD": {
+ "symbol": "HK$",
+ "name": "Hong Kong Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "HKD",
+ "name_plural": "Hong Kong dollars"
+ },
+ "HNL": {
+ "symbol": "HNL",
+ "name": "Honduran Lempira",
+ "symbol_native": "L",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "HNL",
+ "name_plural": "Honduran lempiras"
+ },
+ "HRK": {
+ "symbol": "kn",
+ "name": "Croatian Kuna",
+ "symbol_native": "kn",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "HRK",
+ "name_plural": "Croatian kunas"
+ },
+ "HUF": {
+ "symbol": "Ft",
+ "name": "Hungarian Forint",
+ "symbol_native": "Ft",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "HUF",
+ "name_plural": "Hungarian forints"
+ },
+ "IDR": {
+ "symbol": "Rp",
+ "name": "Indonesian Rupiah",
+ "symbol_native": "Rp",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "IDR",
+ "name_plural": "Indonesian rupiahs"
+ },
+ "ILS": {
+ "symbol": "₪",
+ "name": "Israeli New Sheqel",
+ "symbol_native": "₪",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "ILS",
+ "name_plural": "Israeli new sheqels"
+ },
+ "INR": {
+ "symbol": "Rs",
+ "name": "Indian Rupee",
+ "symbol_native": "টকা",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "INR",
+ "name_plural": "Indian rupees"
+ },
+ "IQD": {
+ "symbol": "IQD",
+ "name": "Iraqi Dinar",
+ "symbol_native": "د.ع.",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "IQD",
+ "name_plural": "Iraqi dinars"
+ },
+ "IRR": {
+ "symbol": "IRR",
+ "name": "Iranian Rial",
+ "symbol_native": "﷼",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "IRR",
+ "name_plural": "Iranian rials"
+ },
+ "ISK": {
+ "symbol": "Ikr",
+ "name": "Icelandic Króna",
+ "symbol_native": "kr",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "ISK",
+ "name_plural": "Icelandic krónur"
+ },
+ "JMD": {
+ "symbol": "J$",
+ "name": "Jamaican Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "JMD",
+ "name_plural": "Jamaican dollars"
+ },
+ "JOD": {
+ "symbol": "JD",
+ "name": "Jordanian Dinar",
+ "symbol_native": "د.أ.",
+ "decimal_digits": 3,
+ "rounding": 0,
+ "code": "JOD",
+ "name_plural": "Jordanian dinars"
+ },
+ "JPY": {
+ "symbol": "¥",
+ "name": "Japanese Yen",
+ "symbol_native": "¥",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "JPY",
+ "name_plural": "Japanese yen"
+ },
+ "KES": {
+ "symbol": "Ksh",
+ "name": "Kenyan Shilling",
+ "symbol_native": "Ksh",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "KES",
+ "name_plural": "Kenyan shillings"
+ },
+ "KHR": {
+ "symbol": "KHR",
+ "name": "Cambodian Riel",
+ "symbol_native": "៛",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "KHR",
+ "name_plural": "Cambodian riels"
+ },
+ "KMF": {
+ "symbol": "CF",
+ "name": "Comorian Franc",
+ "symbol_native": "FC",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "KMF",
+ "name_plural": "Comorian francs"
+ },
+ "KRW": {
+ "symbol": "₩",
+ "name": "South Korean Won",
+ "symbol_native": "₩",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "KRW",
+ "name_plural": "South Korean won"
+ },
+ "KWD": {
+ "symbol": "KD",
+ "name": "Kuwaiti Dinar",
+ "symbol_native": "د.ك.",
+ "decimal_digits": 3,
+ "rounding": 0,
+ "code": "KWD",
+ "name_plural": "Kuwaiti dinars"
+ },
+ "KZT": {
+ "symbol": "KZT",
+ "name": "Kazakhstani Tenge",
+ "symbol_native": "тңг.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "KZT",
+ "name_plural": "Kazakhstani tenges"
+ },
+ "LBP": {
+ "symbol": "L.L.",
+ "name": "Lebanese Pound",
+ "symbol_native": "ل.ل.",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "LBP",
+ "name_plural": "Lebanese pounds"
+ },
+ "LKR": {
+ "symbol": "SLRs",
+ "name": "Sri Lankan Rupee",
+ "symbol_native": "SL Re",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "LKR",
+ "name_plural": "Sri Lankan rupees"
+ },
+ "LTL": {
+ "symbol": "Lt",
+ "name": "Lithuanian Litas",
+ "symbol_native": "Lt",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "LTL",
+ "name_plural": "Lithuanian litai"
+ },
+ "LVL": {
+ "symbol": "Ls",
+ "name": "Latvian Lats",
+ "symbol_native": "Ls",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "LVL",
+ "name_plural": "Latvian lati"
+ },
+ "LYD": {
+ "symbol": "LD",
+ "name": "Libyan Dinar",
+ "symbol_native": "د.ل.",
+ "decimal_digits": 3,
+ "rounding": 0,
+ "code": "LYD",
+ "name_plural": "Libyan dinars"
+ },
+ "MAD": {
+ "symbol": "MAD",
+ "name": "Moroccan Dirham",
+ "symbol_native": "د.م.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "MAD",
+ "name_plural": "Moroccan dirhams"
+ },
+ "MDL": {
+ "symbol": "MDL",
+ "name": "Moldovan Leu",
+ "symbol_native": "MDL",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "MDL",
+ "name_plural": "Moldovan lei"
+ },
+ "MGA": {
+ "symbol": "MGA",
+ "name": "Malagasy Ariary",
+ "symbol_native": "MGA",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "MGA",
+ "name_plural": "Malagasy Ariaries"
+ },
+ "MKD": {
+ "symbol": "MKD",
+ "name": "Macedonian Denar",
+ "symbol_native": "MKD",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "MKD",
+ "name_plural": "Macedonian denari"
+ },
+ "MMK": {
+ "symbol": "MMK",
+ "name": "Myanma Kyat",
+ "symbol_native": "K",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "MMK",
+ "name_plural": "Myanma kyats"
+ },
+ "MOP": {
+ "symbol": "MOP$",
+ "name": "Macanese Pataca",
+ "symbol_native": "MOP$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "MOP",
+ "name_plural": "Macanese patacas"
+ },
+ "MUR": {
+ "symbol": "MURs",
+ "name": "Mauritian Rupee",
+ "symbol_native": "MURs",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "MUR",
+ "name_plural": "Mauritian rupees"
+ },
+ "MXN": {
+ "symbol": "MX$",
+ "name": "Mexican Peso",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "MXN",
+ "name_plural": "Mexican pesos"
+ },
+ "MYR": {
+ "symbol": "RM",
+ "name": "Malaysian Ringgit",
+ "symbol_native": "RM",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "MYR",
+ "name_plural": "Malaysian ringgits"
+ },
+ "MZN": {
+ "symbol": "MTn",
+ "name": "Mozambican Metical",
+ "symbol_native": "MTn",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "MZN",
+ "name_plural": "Mozambican meticals"
+ },
+ "NAD": {
+ "symbol": "N$",
+ "name": "Namibian Dollar",
+ "symbol_native": "N$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "NAD",
+ "name_plural": "Namibian dollars"
+ },
+ "NGN": {
+ "symbol": "₦",
+ "name": "Nigerian Naira",
+ "symbol_native": "₦",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "NGN",
+ "name_plural": "Nigerian nairas"
+ },
+ "NIO": {
+ "symbol": "C$",
+ "name": "Nicaraguan Córdoba",
+ "symbol_native": "C$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "NIO",
+ "name_plural": "Nicaraguan córdobas"
+ },
+ "NOK": {
+ "symbol": "Nkr",
+ "name": "Norwegian Krone",
+ "symbol_native": "kr",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "NOK",
+ "name_plural": "Norwegian kroner"
+ },
+ "NPR": {
+ "symbol": "NPRs",
+ "name": "Nepalese Rupee",
+ "symbol_native": "नेरू",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "NPR",
+ "name_plural": "Nepalese rupees"
+ },
+ "NZD": {
+ "symbol": "NZ$",
+ "name": "New Zealand Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "NZD",
+ "name_plural": "New Zealand dollars"
+ },
+ "OMR": {
+ "symbol": "OMR",
+ "name": "Omani Rial",
+ "symbol_native": "ر.ع.",
+ "decimal_digits": 3,
+ "rounding": 0,
+ "code": "OMR",
+ "name_plural": "Omani rials"
+ },
+ "PAB": {
+ "symbol": "B/.",
+ "name": "Panamanian Balboa",
+ "symbol_native": "B/.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "PAB",
+ "name_plural": "Panamanian balboas"
+ },
+ "PEN": {
+ "symbol": "S/.",
+ "name": "Peruvian Nuevo Sol",
+ "symbol_native": "S/.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "PEN",
+ "name_plural": "Peruvian nuevos soles"
+ },
+ "PHP": {
+ "symbol": "₱",
+ "name": "Philippine Peso",
+ "symbol_native": "₱",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "PHP",
+ "name_plural": "Philippine pesos"
+ },
+ "PKR": {
+ "symbol": "PKRs",
+ "name": "Pakistani Rupee",
+ "symbol_native": "₨",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "PKR",
+ "name_plural": "Pakistani rupees"
+ },
+ "PLN": {
+ "symbol": "zł",
+ "name": "Polish Zloty",
+ "symbol_native": "zł",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "PLN",
+ "name_plural": "Polish zlotys"
+ },
+ "PYG": {
+ "symbol": "₲",
+ "name": "Paraguayan Guarani",
+ "symbol_native": "₲",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "PYG",
+ "name_plural": "Paraguayan guaranis"
+ },
+ "QAR": {
+ "symbol": "QR",
+ "name": "Qatari Rial",
+ "symbol_native": "ر.ق.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "QAR",
+ "name_plural": "Qatari rials"
+ },
+ "RON": {
+ "symbol": "RON",
+ "name": "Romanian Leu",
+ "symbol_native": "RON",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "RON",
+ "name_plural": "Romanian lei"
+ },
+ "RSD": {
+ "symbol": "din.",
+ "name": "Serbian Dinar",
+ "symbol_native": "дин.",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "RSD",
+ "name_plural": "Serbian dinars"
+ },
+ "RUB": {
+ "symbol": "RUB",
+ "name": "Russian Ruble",
+ "symbol_native": "₽.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "RUB",
+ "name_plural": "Russian rubles"
+ },
+ "RWF": {
+ "symbol": "RWF",
+ "name": "Rwandan Franc",
+ "symbol_native": "FR",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "RWF",
+ "name_plural": "Rwandan francs"
+ },
+ "SAR": {
+ "symbol": "SR",
+ "name": "Saudi Riyal",
+ "symbol_native": "ر.س.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "SAR",
+ "name_plural": "Saudi riyals"
+ },
+ "SDG": {
+ "symbol": "SDG",
+ "name": "Sudanese Pound",
+ "symbol_native": "SDG",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "SDG",
+ "name_plural": "Sudanese pounds"
+ },
+ "SEK": {
+ "symbol": "Skr",
+ "name": "Swedish Krona",
+ "symbol_native": "kr",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "SEK",
+ "name_plural": "Swedish kronor"
+ },
+ "SGD": {
+ "symbol": "S$",
+ "name": "Singapore Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "SGD",
+ "name_plural": "Singapore dollars"
+ },
+ "SOS": {
+ "symbol": "Ssh",
+ "name": "Somali Shilling",
+ "symbol_native": "Ssh",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "SOS",
+ "name_plural": "Somali shillings"
+ },
+ "SYP": {
+ "symbol": "SY£",
+ "name": "Syrian Pound",
+ "symbol_native": "ل.س.",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "SYP",
+ "name_plural": "Syrian pounds"
+ },
+ "THB": {
+ "symbol": "฿",
+ "name": "Thai Baht",
+ "symbol_native": "฿",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "THB",
+ "name_plural": "Thai baht"
+ },
+ "TND": {
+ "symbol": "DT",
+ "name": "Tunisian Dinar",
+ "symbol_native": "د.ت.",
+ "decimal_digits": 3,
+ "rounding": 0,
+ "code": "TND",
+ "name_plural": "Tunisian dinars"
+ },
+ "TOP": {
+ "symbol": "T$",
+ "name": "Tongan Paʻanga",
+ "symbol_native": "T$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "TOP",
+ "name_plural": "Tongan paʻanga"
+ },
+ "TRY": {
+ "symbol": "TL",
+ "name": "Turkish Lira",
+ "symbol_native": "TL",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "TRY",
+ "name_plural": "Turkish Lira"
+ },
+ "TTD": {
+ "symbol": "TT$",
+ "name": "Trinidad and Tobago Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "TTD",
+ "name_plural": "Trinidad and Tobago dollars"
+ },
+ "TWD": {
+ "symbol": "NT$",
+ "name": "New Taiwan Dollar",
+ "symbol_native": "NT$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "TWD",
+ "name_plural": "New Taiwan dollars"
+ },
+ "TZS": {
+ "symbol": "TSh",
+ "name": "Tanzanian Shilling",
+ "symbol_native": "TSh",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "TZS",
+ "name_plural": "Tanzanian shillings"
+ },
+ "UAH": {
+ "symbol": "₴",
+ "name": "Ukrainian Hryvnia",
+ "symbol_native": "₴",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "UAH",
+ "name_plural": "Ukrainian hryvnias"
+ },
+ "UGX": {
+ "symbol": "USh",
+ "name": "Ugandan Shilling",
+ "symbol_native": "USh",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "UGX",
+ "name_plural": "Ugandan shillings"
+ },
+ "UYU": {
+ "symbol": "$U",
+ "name": "Uruguayan Peso",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "UYU",
+ "name_plural": "Uruguayan pesos"
+ },
+ "UZS": {
+ "symbol": "UZS",
+ "name": "Uzbekistan Som",
+ "symbol_native": "UZS",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "UZS",
+ "name_plural": "Uzbekistan som"
+ },
+ "VEF": {
+ "symbol": "Bs.F.",
+ "name": "Venezuelan Bolívar",
+ "symbol_native": "Bs.F.",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "VEF",
+ "name_plural": "Venezuelan bolívars"
+ },
+ "VND": {
+ "symbol": "₫",
+ "name": "Vietnamese Dong",
+ "symbol_native": "₫",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "VND",
+ "name_plural": "Vietnamese dong"
+ },
+ "XAF": {
+ "symbol": "FCFA",
+ "name": "CFA Franc BEAC",
+ "symbol_native": "FCFA",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "XAF",
+ "name_plural": "CFA francs BEAC"
+ },
+ "XOF": {
+ "symbol": "CFA",
+ "name": "CFA Franc BCEAO",
+ "symbol_native": "CFA",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "XOF",
+ "name_plural": "CFA francs BCEAO"
+ },
+ "YER": {
+ "symbol": "YR",
+ "name": "Yemeni Rial",
+ "symbol_native": "ر.ي.",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "YER",
+ "name_plural": "Yemeni rials"
+ },
+ "ZAR": {
+ "symbol": "R",
+ "name": "South African Rand",
+ "symbol_native": "R",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "ZAR",
+ "name_plural": "South African rand"
+ },
+ "ZMK": {
+ "symbol": "ZK",
+ "name": "Zambian Kwacha",
+ "symbol_native": "ZK",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "ZMK",
+ "name_plural": "Zambian kwachas"
+ },
+ "ZWL": {
+ "symbol": "ZWL$",
+ "name": "Zimbabwean Dollar",
+ "symbol_native": "ZWL$",
+ "decimal_digits": 0,
+ "rounding": 0,
+ "code": "ZWL",
+ "name_plural": "Zimbabwean Dollar"
+ }
+}
\ No newline at end of file
diff --git a/IntegrationCore/BusinessLogic/Utility/CurrencySymbolService.php b/IntegrationCore/BusinessLogic/Utility/CurrencySymbolService.php
new file mode 100644
index 0000000..1c27bb0
--- /dev/null
+++ b/IntegrationCore/BusinessLogic/Utility/CurrencySymbolService.php
@@ -0,0 +1,31 @@
+orderReference->getPayload());
$reminder = $this->getReminderDetail();
if ($reminder && $reminder->getRemainderMethod()) {
-
$voucherLabel = $this->getLabel($configs, $channelId, 'voucher');
$reminderLabel = $this->getLabel($configs, $channelId, $reminder->getRemainderMethod());
$voucherCurrencySymbol = $this->localeExtension->getCurrencySymbolByCurrency($order->getAmount()->getCurrency());
@@ -87,7 +86,7 @@ public function isVoucher()
{
$order = $this->getOrder();
- return $order && implode('', $order->getMethod()) === 'voucher';
+ return $order && implode('', $order->getMethods()) === 'voucher';
}
/**
diff --git a/PaymentMethod/Factory/MollieConfigMapperDecorator.php b/PaymentMethod/Factory/MollieConfigMapperDecorator.php
index 3a26b27..4d46b6b 100644
--- a/PaymentMethod/Factory/MollieConfigMapperDecorator.php
+++ b/PaymentMethod/Factory/MollieConfigMapperDecorator.php
@@ -58,7 +58,7 @@ public function getOrderData(PaymentTransaction $paymentTransaction)
$orderData = $this->dtoMapper->getOrderData($paymentTransaction);
if ($orderData) {
$orderData->setProfileId($this->config->getProfileId());
- $orderData->setMethod([$this->config->getMollieId()]);
+ $orderData->setMethods([$this->config->getMollieId()]);
$expiryDays = $this->config->getOrderExpiryDays();
if ($expiryDays > 0) {
$orderData->calculateExpiresAt($expiryDays);
@@ -102,7 +102,7 @@ public function getPaymentData(PaymentTransaction $paymentTransaction)
{
$paymentData = $this->dtoMapper->getPaymentData($paymentTransaction);
$paymentData->setProfileId($this->config->getProfileId());
- $paymentData->setMethod([$this->config->getMollieId()]);
+ $paymentData->setMethods([$this->config->getMollieId()]);
$this->setDueDate($paymentData);
return $paymentData;
diff --git a/PaymentMethod/Factory/MolliePaymentLinkMapperDecorator.php b/PaymentMethod/Factory/MolliePaymentLinkMapperDecorator.php
index 1fa6d2e..ce31a92 100644
--- a/PaymentMethod/Factory/MolliePaymentLinkMapperDecorator.php
+++ b/PaymentMethod/Factory/MolliePaymentLinkMapperDecorator.php
@@ -44,7 +44,7 @@ public function getOrderData(PaymentTransaction $paymentTransaction)
{
$orderData = parent::getOrderData($paymentTransaction);
if ($orderData) {
- $orderData->setMethod($this->getPaymentLinkMethod($paymentTransaction));
+ $orderData->setMethods($this->getPaymentLinkMethod($paymentTransaction));
}
return $orderData;
@@ -57,7 +57,7 @@ public function getPaymentData(PaymentTransaction $paymentTransaction)
{
$paymentData = parent::getPaymentData($paymentTransaction);
- $paymentData->setMethod($this->getPaymentLinkMethod($paymentTransaction));
+ $paymentData->setMethods($this->getPaymentLinkMethod($paymentTransaction));
return $paymentData;
}
diff --git a/README.md b/README.md
index 9127024..443ec41 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,9 @@ To finalize the installation you need to enter your organization API token in th
Read more about the integration configuration on [our Wiki](https://github.com/mollie/orocommerce/wiki).
## Release notes
- - **4.0.8:**
+ - **4.0.9:**
+ - Added support for the Klarna Pay Now payment method.
+ - **4.0.8:**
- Optimization: Updated translations
- Optimization: Fixed Composer version number
- **4.0.7:**
diff --git a/Resources/config/services.yml b/Resources/config/services.yml
index 485a621..7c33ec0 100755
--- a/Resources/config/services.yml
+++ b/Resources/config/services.yml
@@ -1,5 +1,5 @@
parameters:
- mollie_payment.version: 4.0.8
+ mollie_payment.version: 4.0.9
mollie_payment.method.identifier_prefix.mollie_payment: 'mollie_payment'
mollie_payment.uploader.public_image_path: 'bundles/molliepayment/img/uploads'
mollie_payment.uploader.image_dir: '%kernel.project_dir%/public/%mollie_payment.uploader.public_image_path%'
diff --git a/Resources/translations/messages.de.yml b/Resources/translations/messages.de.yml
index 220940c..093b0da 100644
--- a/Resources/translations/messages.de.yml
+++ b/Resources/translations/messages.de.yml
@@ -35,10 +35,10 @@ mollie:
redirect_to_mollie_payment: 'Nach dem Kontrollieren Ihrer Bestellung werden Sie zur Website des Zahlungsanbieters weitergeleitet, um den Einkauf abzuschließen.'
admin_link_error: 'Wir konnten Ihre Zahlung nicht verarbeiten. Überprüfen Sie bitte die Zahlungsinformationen und versuchen Sie es erneut.'
selectBank: 'Bank auswählen'
- cardHolder: 'Kartenhalter'
- cardNumber: 'Kartennummer'
- expiryDate: 'Verfallsdatum'
- verificationCode: 'Verifizierungs-Schlüssel'
+ cardHolder: 'Name des Karteninhabers'
+ cardNumber: 'Kreditkartennummer'
+ expiryDate: 'MM/JJ'
+ verificationCode: 'CVV'
config:
authorization:
label: 'Befugnis'
diff --git a/Resources/translations/messages.de_DE.yml b/Resources/translations/messages.de_DE.yml
index 220940c..093b0da 100644
--- a/Resources/translations/messages.de_DE.yml
+++ b/Resources/translations/messages.de_DE.yml
@@ -35,10 +35,10 @@ mollie:
redirect_to_mollie_payment: 'Nach dem Kontrollieren Ihrer Bestellung werden Sie zur Website des Zahlungsanbieters weitergeleitet, um den Einkauf abzuschließen.'
admin_link_error: 'Wir konnten Ihre Zahlung nicht verarbeiten. Überprüfen Sie bitte die Zahlungsinformationen und versuchen Sie es erneut.'
selectBank: 'Bank auswählen'
- cardHolder: 'Kartenhalter'
- cardNumber: 'Kartennummer'
- expiryDate: 'Verfallsdatum'
- verificationCode: 'Verifizierungs-Schlüssel'
+ cardHolder: 'Name des Karteninhabers'
+ cardNumber: 'Kreditkartennummer'
+ expiryDate: 'MM/JJ'
+ verificationCode: 'CVV'
config:
authorization:
label: 'Befugnis'
diff --git a/Resources/translations/messages.en.yml b/Resources/translations/messages.en.yml
index 4e72d10..0f8b69c 100644
--- a/Resources/translations/messages.en.yml
+++ b/Resources/translations/messages.en.yml
@@ -35,10 +35,10 @@ mollie:
redirect_to_mollie_payment: 'You will be redirected to payment gateway website to complete your purchase after the order review step.'
admin_link_error: 'We were unable to process your payment. Please verify your payment information and try again.'
selectBank: 'Select bank'
- cardHolder: 'Card Holder'
- cardNumber: 'Card Number'
- expiryDate: 'Expiry Date'
- verificationCode: 'Verification Code'
+ cardHolder: 'Name on card'
+ cardNumber: 'Card number'
+ expiryDate: 'Expiry date'
+ verificationCode: 'CVC/CVV'
config:
authorization:
label: 'Authorization'
diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml
index afe5590..9a4b4cd 100644
--- a/Resources/translations/messages.fr.yml
+++ b/Resources/translations/messages.fr.yml
@@ -35,10 +35,10 @@ mollie:
redirect_to_mollie_payment: "Vous allez être redirigé vers le site web de la passerelle de paiement pour effectuer votre achat après l'étape de révision de la commande."
admin_link_error: "Nous n'avons pas pu traiter votre paiement. Veuillez vérifier vos informations de paiement et try again."
selectBank: 'Choisir une banque'
- cardHolder: 'Titulaire de la carte'
- cardNumber: 'Numéro de carte'
- expiryDate: "Date d'expiration"
- verificationCode: 'Code de vérification'
+ cardHolder: 'Nom du titulaire de la carte'
+ cardNumber: 'Numero de carte de credit'
+ expiryDate: 'MM/AA'
+ verificationCode: 'CVV'
config:
authorization:
label: 'Autorisation'
diff --git a/Resources/translations/messages.fr_FR.yml b/Resources/translations/messages.fr_FR.yml
index afe5590..9a4b4cd 100644
--- a/Resources/translations/messages.fr_FR.yml
+++ b/Resources/translations/messages.fr_FR.yml
@@ -35,10 +35,10 @@ mollie:
redirect_to_mollie_payment: "Vous allez être redirigé vers le site web de la passerelle de paiement pour effectuer votre achat après l'étape de révision de la commande."
admin_link_error: "Nous n'avons pas pu traiter votre paiement. Veuillez vérifier vos informations de paiement et try again."
selectBank: 'Choisir une banque'
- cardHolder: 'Titulaire de la carte'
- cardNumber: 'Numéro de carte'
- expiryDate: "Date d'expiration"
- verificationCode: 'Code de vérification'
+ cardHolder: 'Nom du titulaire de la carte'
+ cardNumber: 'Numero de carte de credit'
+ expiryDate: 'MM/AA'
+ verificationCode: 'CVV'
config:
authorization:
label: 'Autorisation'
diff --git a/Resources/translations/messages.nl.yml b/Resources/translations/messages.nl.yml
index 0e708f7..6f81819 100644
--- a/Resources/translations/messages.nl.yml
+++ b/Resources/translations/messages.nl.yml
@@ -35,10 +35,10 @@ mollie:
redirect_to_mollie_payment: 'Nadat je de bestelling hebt gecontroleerd, word je doorgestuurd naar de website van de betalingsprovider om je aankoop af te ronden.'
admin_link_error: 'We hebben je betaling niet kunnen verwerken. Controleer je betalingsgegevens en probeer het opnieuw.'
selectBank: 'Selecteer Bank'
- cardHolder: 'Kaarthouder'
- cardNumber: 'Kaartnummer'
- expiryDate: 'Vervaldatum'
- verificationCode: 'Verificatie code'
+ cardHolder: 'Naam kaarthouder'
+ cardNumber: 'Creditcardnummer'
+ expiryDate: 'MM/JJ'
+ verificationCode: 'CVV'
config:
authorization:
label: 'Autorisatie'
diff --git a/Resources/translations/messages.nl_NL.yml b/Resources/translations/messages.nl_NL.yml
index 0e708f7..6f81819 100644
--- a/Resources/translations/messages.nl_NL.yml
+++ b/Resources/translations/messages.nl_NL.yml
@@ -35,10 +35,10 @@ mollie:
redirect_to_mollie_payment: 'Nadat je de bestelling hebt gecontroleerd, word je doorgestuurd naar de website van de betalingsprovider om je aankoop af te ronden.'
admin_link_error: 'We hebben je betaling niet kunnen verwerken. Controleer je betalingsgegevens en probeer het opnieuw.'
selectBank: 'Selecteer Bank'
- cardHolder: 'Kaarthouder'
- cardNumber: 'Kaartnummer'
- expiryDate: 'Vervaldatum'
- verificationCode: 'Verificatie code'
+ cardHolder: 'Naam kaarthouder'
+ cardNumber: 'Creditcardnummer'
+ expiryDate: 'MM/JJ'
+ verificationCode: 'CVV'
config:
authorization:
label: 'Autorisatie'
diff --git a/Tests/Unit/BusinessLogic/Authorization/ApiKeyAuthServiceTest.php b/Tests/Unit/BusinessLogic/Authorization/ApiKeyAuthServiceTest.php
new file mode 100644
index 0000000..a6f73f7
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Authorization/ApiKeyAuthServiceTest.php
@@ -0,0 +1,124 @@
+httpClient = new TestHttpClient();
+ TestServiceRegister::registerService(
+ HttpClient::CLASS_NAME,
+ function () use ($me) {
+ return $me->httpClient;
+ }
+ );
+ TestServiceRegister::registerService(
+ Proxy::CLASS_NAME,
+ function () use ($me) {
+ return new Proxy($me->shopConfig, $me->httpClient, new ProxyDataProvider());
+ }
+ );
+
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () use ($me) {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
+
+ $this->authService = ApiKeyAuthService::getInstance();
+ }
+
+ protected function tearDown()
+ {
+ parent::tearDown();
+ ApiKeyAuthService::resetInstance();
+ }
+
+ public function testConnection()
+ {
+ $this->httpClient->setMockResponses(array($this->getMockProfile(), $this->getMockProfile()));
+ $apiKey = new ApiKey('test_vzCV9AHCDpwuAMfEPtWz3Pah7RqA7j');
+ $this->authService->connect($apiKey);
+
+ $lastRequest = $this->httpClient->getLastRequest();
+ $this->assertNotEmpty($lastRequest);
+ $this->assertEquals('Authorization: Bearer test_vzCV9AHCDpwuAMfEPtWz3Pah7RqA7j', $lastRequest['headers']['token']);
+ $this->assertNotContains('testmode=true', $lastRequest['url']);
+ }
+
+ public function testConnectionWithInvalidKey()
+ {
+ $this->httpClient->setMockResponses(array(new HttpResponse(401, array(), '')));
+ $apiKey = new ApiKey('test_vzCV9AHCDpwuAMfEPtWz3Pah7RqA7j');
+ $this->expectException('Exception');
+ $this->authService->connect($apiKey);
+ }
+
+ public function testAuthorizationTokenValidation()
+ {
+ $this->httpClient->setMockResponses(array($this->getMockProfile()));
+
+ $apiKey = new ApiKey('test_vzCV9AHCDpwuAMfEPtWz3Pah7RqA7j');
+ $result = $this->authService->validateToken($apiKey);
+
+ $lastRequest = $this->httpClient->getLastRequest();
+ $this->assertTrue($result);
+ $this->assertNotEmpty($lastRequest);
+ $this->assertEquals('Authorization: Bearer test_vzCV9AHCDpwuAMfEPtWz3Pah7RqA7j', $lastRequest['headers']['token']);
+ $this->assertNotContains('testmode=true', $lastRequest['url']);
+ }
+
+ public function testApiKeyTestMode()
+ {
+ $apiKey = new ApiKey('test_vzCV9AHCDpwuAMfEPtWz3Pah7RqA7j');
+ $this->assertTrue($apiKey->isTest());
+ }
+
+
+ public function testApiKeyNotInValidFormat()
+ {
+ $this->expectException('InvalidArgumentException');
+ $this->authService->validateToken(new ApiKey('fajsdkljf'));
+ }
+
+ private function getMockProfile()
+ {
+ $response = file_get_contents(__DIR__ . '/../Common/ApiResponses/currentProfile.json');
+
+ return new HttpResponse(200, array(), $response);
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Authorization/OrgTokenAuthServiceTest.php b/Tests/Unit/BusinessLogic/Authorization/OrgTokenAuthServiceTest.php
new file mode 100644
index 0000000..ba126e3
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Authorization/OrgTokenAuthServiceTest.php
@@ -0,0 +1,172 @@
+httpClient = new TestHttpClient();
+ TestServiceRegister::registerService(
+ HttpClient::CLASS_NAME,
+ function () use ($me) {
+ return $me->httpClient;
+ }
+ );
+ TestServiceRegister::registerService(
+ Proxy::CLASS_NAME,
+ function () use ($me) {
+ return new Proxy($me->shopConfig, $me->httpClient, new ProxyDataProvider());
+ }
+ );
+ TestServiceRegister::registerService(
+ PaymentMethodService::CLASS_NAME,
+ function () {
+ return MockPaymentMethodService::getInstance();
+ }
+ );
+
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () use ($me) {
+ return OrgTokenAuthService::getInstance();
+ }
+ );
+
+
+ $this->authService = OrgTokenAuthService::getInstance();
+ $this->paymentMethodService = MockPaymentMethodService::getInstance();
+ }
+
+ protected function tearDown()
+ {
+ parent::tearDown();
+ OrgTokenAuthService::resetInstance();
+ MockPaymentMethodService::resetInstance();
+ }
+
+ public function testAuthorizationTokenValidation()
+ {
+ $this->httpClient->setMockResponses(array($this->getMockPermissions()));
+ $token = new OrgToken('test_token', true);
+
+ $result = $this->authService->validateToken($token);
+
+ $lastRequest = $this->httpClient->getLastRequest();
+ $this->assertTrue($result);
+ $this->assertNotEmpty($lastRequest);
+ $this->assertEquals('Authorization: Bearer test_token', $lastRequest['headers']['token']);
+ $this->assertNotContains('testmode=true', $lastRequest['url']);
+ }
+
+ public function testAuthorizationTokenValidationWithInsufficientPermissions()
+ {
+ $this->httpClient->setMockResponses(array($this->getMockInsufficientPermissions()));
+
+ $result = $this->authService->validateToken(new OrgToken('test_token', true));
+
+ $lastRequest = $this->httpClient->getLastRequest();
+ $this->assertFalse($result);
+ $this->assertNotEmpty($lastRequest);
+ $this->assertEquals('Authorization: Bearer test_token', $lastRequest['headers']['token']);
+ $this->assertNotContains('testmode=true', $lastRequest['url']);
+ }
+
+ public function testAuthorizationTokenValidationFailure()
+ {
+ $response = new HttpResponse(401, array(), '{ "status": 401, "title": "Unauthorized Request" }');
+ $this->httpClient->setMockResponses(array($response));
+
+ $result = $this->authService->validateToken(new OrgToken('test_token', false));
+
+ $lastRequest = $this->httpClient->getLastRequest();
+ $this->assertFalse($result);
+ $this->assertNotEmpty($lastRequest);
+ $this->assertEquals('Authorization: Bearer test_token', $lastRequest['headers']['token']);
+ $this->assertNotContains('testmode=true', $lastRequest['url']);
+ }
+
+ public function testAuthorizationTokenValidationConfigServiceCleanup()
+ {
+ $this->httpClient->setMockResponses(array($this->getMockPermissions()));
+
+ $this->authService->validateToken(new OrgToken('test_token', true));
+
+ $this->assertNull($this->shopConfig->getAuthorizationToken());
+ $this->assertFalse($this->shopConfig->isTestMode());
+ }
+
+ public function testResetting()
+ {
+ $profileId = 'test_dfklasjio11231';
+ $this->shopConfig->setContext('test_context');
+ $this->shopConfig->setAuthorizationToken('test_token');
+ $this->shopConfig->setTestMode(true);
+ $this->shopConfig->setWebsiteProfile(
+ WebsiteProfile::fromArray(array(
+ 'resource' => 'profile',
+ 'id' => $profileId,
+ 'name' => 'Test profile',
+ ))
+ );
+
+ $this->authService->reset();
+
+ $clearPaymentsCallHistory = $this->paymentMethodService->getCallHistory('clear');
+ $this->assertCount(1, $clearPaymentsCallHistory);
+ $this->assertEquals($profileId, $clearPaymentsCallHistory[0]['profileId']);
+ $this->assertNull($this->shopConfig->getAuthorizationToken());
+ $this->assertFalse($this->shopConfig->isTestMode());
+ $this->assertNull($this->shopConfig->getWebsiteProfile());
+ }
+
+ protected function getMockPermissions()
+ {
+ $response = file_get_contents(__DIR__ . '/../Common/ApiResponses/permissions.json');
+
+ return new HttpResponse(200, array(), $response);
+ }
+
+ protected function getMockInsufficientPermissions()
+ {
+ $response = file_get_contents(__DIR__ . '/../Common/ApiResponses/insufficientPermissions.json');
+
+ return new HttpResponse(200, array(), $response);
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/CheckoutLink/CheckoutLinkTest.php b/Tests/Unit/BusinessLogic/CheckoutLink/CheckoutLinkTest.php
index 99a5bd1..5fca4bd 100644
--- a/Tests/Unit/BusinessLogic/CheckoutLink/CheckoutLinkTest.php
+++ b/Tests/Unit/BusinessLogic/CheckoutLink/CheckoutLinkTest.php
@@ -2,6 +2,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\CheckoutLink;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\CheckoutLink\CheckoutLinkService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\CheckoutLink\Exceptions\CheckoutLinkNotAvailableException;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException;
@@ -92,6 +94,13 @@ function () {
}
);
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () use ($me) {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
$this->shopConfig->setAuthorizationToken('test_token');
$this->shopConfig->setTestMode(true);
$this->checkoutLinkService = CheckoutLinkService::getInstance();
diff --git a/Tests/Unit/BusinessLogic/Common/ApiResponses/allShipments.json b/Tests/Unit/BusinessLogic/Common/ApiResponses/allShipments.json
new file mode 100644
index 0000000..ec05af4
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Common/ApiResponses/allShipments.json
@@ -0,0 +1,86 @@
+{
+ "count": 2,
+ "_embedded": {
+ "shipments": [
+ {
+ "resource": "shipment",
+ "id": "shp_3wmsgCJN4U",
+ "orderId": "ord_kEn1PlbGa",
+ "createdAt": "2018-08-09T14:33:54+00:00",
+ "tracking": {
+ "carrier": "PostNL",
+ "code": "3SKABA000000000",
+ "url": "http://postnl.nl/tracktrace/?B=3SKABA000000000&P=1016EE&D=NL&T=C"
+ },
+ "lines": [
+ {
+ "resource": "orderline",
+ "id": "odl_dgtxyl",
+ "orderId": "ord_pbjz8x",
+ "name": "LEGO 42083 Bugatti Chiron",
+ "sku": "5702016116977",
+ "type": "physical",
+ "status": "shipping",
+ "metadata": null,
+ "isCancelable": true,
+ "quantity": 1,
+ "unitPrice": {
+ "value": "399.00",
+ "currency": "EUR"
+ },
+ "vatRate": "21.00",
+ "vatAmount": {
+ "value": "51.89",
+ "currency": "EUR"
+ },
+ "discountAmount": {
+ "value": "100.00",
+ "currency": "EUR"
+ },
+ "totalAmount": {
+ "value": "299.00",
+ "currency": "EUR"
+ },
+ "createdAt": "2018-08-02T09:29:56+00:00",
+ "_links": {
+ "productUrl": {
+ "href": "https://shop.lego.com/nl-NL/Bugatti-Chiron-42083",
+ "type": "text/html"
+ },
+ "imageUrl": {
+ "href": "https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$",
+ "type": "text/html"
+ }
+ }
+ },
+ { }
+ ],
+ "_links": {
+ "self": {
+ "href": "https://api.mollie.com/v2/order/ord_kEn1PlbGa/shipments/shp_3wmsgCJN4U",
+ "type": "application/hal+json"
+ },
+ "order": {
+ "href": "https://api.mollie.com/v2/orders/ord_kEn1PlbGa",
+ "type": "application/hal+json"
+ },
+ "documentation": {
+ "href": "https://docs.mollie.com/reference/v2/shipments-api/get-shipment",
+ "type": "text/html"
+ }
+ }
+ },
+ { }
+ ]
+ },
+ "_links": {
+ "self": {
+ "href": "https://api.mollie.com/v2/order/ord_kEn1PlbGa/shipments",
+ "type": "application/hal+json"
+ },
+ "documentation": {
+ "href": "https://docs.mollie.com/reference/v2/shipments-api/list-shipments",
+ "type": "text/html"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/Unit/BusinessLogic/Common/ApiResponses/currentProfile.json b/Tests/Unit/BusinessLogic/Common/ApiResponses/currentProfile.json
new file mode 100644
index 0000000..d954054
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Common/ApiResponses/currentProfile.json
@@ -0,0 +1,49 @@
+{
+ "resource": "profile",
+ "id": "pfl_v9hTwCvYqw",
+ "mode": "live",
+ "name": "My website name",
+ "website": "https://www.mywebsite.com",
+ "email": "info@mywebsite.com",
+ "phone": "+31208202070",
+ "categoryCode": 5399,
+ "status": "verified",
+ "review": {
+ "status": "pending"
+ },
+ "createdAt": "2018-03-20T09:28:37+00:00",
+ "_links": {
+ "self": {
+ "href": "https://api.mollie.com/v2/profiles/pfl_v9hTwCvYqw",
+ "type": "application/hal+json"
+ },
+ "dashboard": {
+ "href": "https://www.mollie.com/dashboard/org_123456789/settings/profiles/pfl_v9hTwCvYqw",
+ "type": "text/html"
+ },
+ "chargebacks": {
+ "href": "https://api.mollie.com/v2/chargebacks",
+ "type": "application/hal+json"
+ },
+ "methods": {
+ "href": "https://api.mollie.com/v2/methods",
+ "type": "application/hal+json"
+ },
+ "payments": {
+ "href": "https://api.mollie.com/v2/payments",
+ "type": "application/hal+json"
+ },
+ "refunds": {
+ "href": "https://api.mollie.com/v2/refunds",
+ "type": "application/hal+json"
+ },
+ "checkoutPreviewUrl": {
+ "href": "https://www.mollie.com/payscreen/preview/pfl_v9hTwCvYqw",
+ "type": "text/html"
+ },
+ "documentation": {
+ "href": "https://docs.mollie.com/reference/v2/profiles-api/get-profile-me",
+ "type": "text/html"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/Unit/BusinessLogic/Common/ApiResponses/mollieTokens.json b/Tests/Unit/BusinessLogic/Common/ApiResponses/mollieTokens.json
new file mode 100644
index 0000000..4d15c9b
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Common/ApiResponses/mollieTokens.json
@@ -0,0 +1,7 @@
+{
+ "access_token": "access_TRbHbeB3my8XywBAdT6HRkGAJMuh4",
+ "refresh_token": "refresh_FS4xc3Mgci2xQ5s5DzaLXh3HhaTZOP",
+ "expires_in": 3600,
+ "token_type": "bearer",
+ "scope": "payments.read organizations.read"
+}
\ No newline at end of file
diff --git a/Tests/Unit/BusinessLogic/Common/ApiResponses/paymentMethodsAll.json b/Tests/Unit/BusinessLogic/Common/ApiResponses/paymentMethodsAll.json
index c1f04ce..548bc68 100644
--- a/Tests/Unit/BusinessLogic/Common/ApiResponses/paymentMethodsAll.json
+++ b/Tests/Unit/BusinessLogic/Common/ApiResponses/paymentMethodsAll.json
@@ -360,6 +360,31 @@
}
}
},
+ {
+ "resource": "method",
+ "id": "klarnapaynow",
+ "description": "Pay now.",
+ "minimumAmount": {
+ "value": "0.10",
+ "currency": "EUR"
+ },
+ "maximumAmount": {
+ "value": "10000.00",
+ "currency": "EUR"
+ },
+ "image": {
+ "size1x": "https://www.mollie.com/external/icons/payment-methods/klarnapaynow.png",
+ "size2x": "https://www.mollie.com/external/icons/payment-methods/klarnapaynow%402x.png",
+ "svg": "https://www.mollie.com/external/icons/payment-methods/klarnapaynow.svg"
+ },
+ "status": "activated",
+ "_links": {
+ "self": {
+ "href": "https://api.mollie.com/v2/methods/klarnapaynow",
+ "type": "application/hal+json"
+ }
+ }
+ },
{
"resource": "method",
"id": "klarnasliceit",
diff --git a/Tests/Unit/BusinessLogic/Common/ApiResponses/version-check.json b/Tests/Unit/BusinessLogic/Common/ApiResponses/version-check.json
new file mode 100644
index 0000000..25cc4a1
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Common/ApiResponses/version-check.json
@@ -0,0 +1,27 @@
+{
+ "name": "mollie/integration-core",
+ "version": "2.0.0",
+ "description": "Mollie integrations core library",
+ "type": "library",
+ "license": "proprietary",
+ "require": {
+ "php": ">=5.3"
+ },
+ "autoload": {
+ "psr-4": {
+ "Mollie\Bundle\PaymentBundle\IntegrationCore\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Mollie\Bundle\PaymentBundle\IntegrationCore\\Tests\\": "tests",
+ "Mollie\Bundle\PaymentBundle\IntegrationCore\\Console\\": "console"
+ }
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35",
+ "codacy/coverage": "dev-master",
+ "ext-json": "*",
+ "symfony/console": "^5.1"
+ }
+}
\ No newline at end of file
diff --git a/Tests/Unit/BusinessLogic/Common/TestComponents/MockAuthorizationService.php b/Tests/Unit/BusinessLogic/Common/TestComponents/MockAuthorizationService.php
new file mode 100644
index 0000000..aa7fd19
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Common/TestComponents/MockAuthorizationService.php
@@ -0,0 +1,116 @@
+getTimestamp());
+ }
+
+ /**
+ * Returns invalid AuthInfo object (accessToken is not valid)
+ *
+ * @return AuthInfo
+ */
+ public function getInvalidMockupAuthInfo()
+ {
+ $time = new \DateTime('2021-6-1');
+
+ return new AuthInfo('accessToken', 'refreshToken', $time->getTimestamp());
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return string
+ */
+ public function getClientSecret()
+ {
+ return 'clientSecret';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return string
+ */
+ public function getAuthToken()
+ {
+ return 'authToken';
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Common/TestComponents/TestPaymentTransactionDescriptionService.php b/Tests/Unit/BusinessLogic/Common/TestComponents/TestPaymentTransactionDescriptionService.php
new file mode 100644
index 0000000..633def8
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Common/TestComponents/TestPaymentTransactionDescriptionService.php
@@ -0,0 +1,19 @@
+mockDescription;
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Common/TestComponents/TestVersionCheckService.php b/Tests/Unit/BusinessLogic/Common/TestComponents/TestVersionCheckService.php
new file mode 100644
index 0000000..511306d
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Common/TestComponents/TestVersionCheckService.php
@@ -0,0 +1,26 @@
+callHistory[] = array('latestverion' => $latestVersion);
+
+ return $latestVersion;
+ }
+
+ public function getCallHistory()
+ {
+ return $this->callHistory;
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Common/TestMaintenanceModeService.php b/Tests/Unit/BusinessLogic/Common/TestMaintenanceModeService.php
new file mode 100644
index 0000000..50b023a
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Common/TestMaintenanceModeService.php
@@ -0,0 +1,42 @@
+callHistory[] = array('called' => true);
+ }
+
+ /**
+ * @return array
+ */
+ public function getCallHistory()
+ {
+ return $this->callHistory;
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Connect/AuthorizationServiceTest.php b/Tests/Unit/BusinessLogic/Connect/AuthorizationServiceTest.php
new file mode 100644
index 0000000..fb81cc4
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Connect/AuthorizationServiceTest.php
@@ -0,0 +1,131 @@
+httpClient = new TestHttpClient();
+ $self = $this;
+
+ TestServiceRegister::registerService(
+ HttpClient::CLASS_NAME,
+ function () use ($self) {
+ return $self->httpClient;
+ }
+ );
+
+ TestServiceRegister::registerService(
+ Proxy::CLASS_NAME,
+ function () use ($self) {
+ return new Proxy($self->shopConfig, $self->httpClient, new ProxyDataProvider());
+ }
+ );
+
+ TestServiceRegister::registerService(
+ TokenService::CLASS_NAME,
+ function () use ($self) {
+ return new TokenService(new TokenProxy($self->shopConfig, $self->httpClient, new ProxyDataProvider()));
+ }
+ );
+
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return new MockAuthorizationService();
+ }
+ );
+ }
+
+ /**
+ * Tests getAuthInfo when accessToken is valid
+ *
+ * @throws UnprocessableEntityRequestException
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ */
+ public function testValidGetAuthInfo()
+ {
+ $time = new \DateTime('now');
+ $time->modify('+ 59 minutes');
+
+ $originalAuthInfo = new AuthInfo('accessToken', 'refreshToken', $time->getTimestamp());
+ $this->shopConfig->setAuthorizationInfo(new AuthInfo('accessToken', 'refreshToken', $time->getTimestamp()));
+
+ $authInfo = $this->getAuthorizationService()->getAuthInfo();
+
+ self::assertEquals($originalAuthInfo, $authInfo);
+ }
+
+ /**
+ * Testing function for getting authorize url
+ */
+ public function testGettingAuthorizeUrl()
+ {
+ $authorizeUrl = $this->getAuthorizationService()->getAuthorizeUrl('en_US');
+
+ $params = array(
+ 'client_id' => 'clientId',
+ 'redirect_uri' => 'https://test/tets',
+ 'state' => $this->shopConfig->getStateString(),
+ 'scope' => 'payments.read organizations.read',
+ 'response_type' => 'code',
+ 'approval_prompt' => 'force',
+ 'locale' => 'en_US',
+ );
+
+ $testAuthorizeUrl = 'https://www.mollie.com/oauth2/authorize' . '?' . http_build_query($params);
+
+ self::assertEquals($authorizeUrl, $testAuthorizeUrl);
+ }
+
+ /**
+ * Returns authorization service
+ *
+ * @return MockAuthorizationService
+ */
+ public function getAuthorizationService()
+ {
+ return ServiceRegister::getService(AuthorizationService::CLASS_NAME);
+ }
+
+ /**
+ * Returns Token service
+ *
+ * @return TokenService
+ */
+ public function getTokenService()
+ {
+ return ServiceRegister::getService(TokenService::CLASS_NAME);
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Connect/DTO/AuthInfoTest.php b/Tests/Unit/BusinessLogic/Connect/DTO/AuthInfoTest.php
new file mode 100644
index 0000000..755b93c
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Connect/DTO/AuthInfoTest.php
@@ -0,0 +1,46 @@
+ 'testAccess',
+ 'refresh_token' => 'testRefresh',
+ 'expires_in' => 542689,
+ );
+
+ $authInfo = new AuthInfo('testAccess', 'testRefresh', 542689);
+
+ self::assertSame($array, $authInfo->toArray());
+ }
+
+ /**
+ * Tests Auth info fromArray function
+ */
+ public function testAuthInfoFromArray()
+ {
+ $array = array(
+ 'access_token' => 'testAccess',
+ 'refresh_token' => 'testRefresh',
+ 'expires_in' => 542689,
+ );
+
+ $authInfo = new AuthInfo('testAccess', 'testRefresh', 542689);
+ $authInfoTest = AuthInfo::fromArray($array);
+
+ self::assertEquals($authInfoTest, $authInfo);
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Connect/DTO/TokenRequestTest.php b/Tests/Unit/BusinessLogic/Connect/DTO/TokenRequestTest.php
new file mode 100644
index 0000000..19260fc
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Connect/DTO/TokenRequestTest.php
@@ -0,0 +1,33 @@
+ 'authorization_code',
+ 'code' => 'testCode',
+ 'redirect_uri' => 'someUrl',
+ );
+
+ $arrayRefresh = array(
+ 'grant_type' => 'refresh_token',
+ 'refresh_token' => 'testRefresh',
+ );
+
+ $tokenCode = new TokenRequest('authorization_code', 'testCode', '', 'someUrl');
+ $tokenRefresh = new TokenRequest('refresh_token', '', 'testRefresh', '');
+
+ self::assertSame($arrayCode, $tokenCode->toArray());
+ self::assertSame($arrayRefresh, $tokenRefresh->toArray());
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Connect/TokenServiceTest.php b/Tests/Unit/BusinessLogic/Connect/TokenServiceTest.php
new file mode 100644
index 0000000..86dffe1
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Connect/TokenServiceTest.php
@@ -0,0 +1,132 @@
+httpClient = new TestHttpClient();
+ $self = $this;
+
+ TestServiceRegister::registerService(
+ HttpClient::CLASS_NAME,
+ function () use ($self) {
+ return $self->httpClient;
+ }
+ );
+
+ TestServiceRegister::registerService(
+ Proxy::CLASS_NAME,
+ function () use ($self) {
+ return new Proxy($self->shopConfig, $self->httpClient, new ProxyDataProvider());
+ }
+ );
+
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return new MockAuthorizationService();
+ }
+ );
+
+ TestServiceRegister::registerService(
+ TokenProxy::CLASS_NAME,
+ function () use ($self) {
+ return new TokenProxy($self->shopConfig, $self->httpClient, new ProxyDataProvider());
+ }
+ );
+
+ TestServiceRegister::registerService(
+ TokenService::CLASS_NAME,
+ function () use ($self) {
+ return new TokenService(new TokenProxy($self->shopConfig, $self->httpClient, new ProxyDataProvider()));
+ }
+ );
+ }
+
+ /**
+ * Test generate tokens from given code
+ *
+ * @throws UnprocessableEntityRequestException
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ */
+ public function testGenerateTokens()
+ {
+ $response = file_get_contents(__DIR__ . '/../Common/ApiResponses/mollieTokens.json');
+ $this->httpClient->setMockResponses(array(new HttpResponse(200, array(), $response)));
+
+ $authInfo = $this->getTokenService()->generate('code_54865asdbviyt6845asdasd');
+ $authInfoTest = new AuthInfo(
+ 'access_TRbHbeB3my8XywBAdT6HRkGAJMuh4',
+ 'refresh_FS4xc3Mgci2xQ5s5DzaLXh3HhaTZOP',
+ $authInfo->getAccessTokenDuration()
+ );
+
+ self::assertEquals($authInfo, $authInfoTest);
+ }
+
+ /**
+ * Test generate tokens from given refreshToken
+ *
+ * @throws HttpAuthenticationException
+ * @throws HttpCommunicationException
+ * @throws HttpRequestException
+ * @throws UnprocessableEntityRequestException
+ */
+ public function testRefreshTokens()
+ {
+ $response = file_get_contents(__DIR__ . '/../Common/ApiResponses/mollieTokens.json');
+ $this->httpClient->setMockResponses(array(new HttpResponse(200, array(), $response)));
+
+ $authInfo = $this->getTokenService()->refreshToken('refresh_54865asdbviyt6845asdasd');
+ $authInfoTest = new AuthInfo(
+ 'access_TRbHbeB3my8XywBAdT6HRkGAJMuh4',
+ 'refresh_FS4xc3Mgci2xQ5s5DzaLXh3HhaTZOP',
+ $authInfo->getAccessTokenDuration()
+ );
+
+ self::assertEquals($authInfo, $authInfoTest);
+ }
+
+ /**
+ * Get TokenService
+ *
+ * @return TokenService
+ */
+ private function getTokenService()
+ {
+ return ServiceRegister::getService(TokenService::CLASS_NAME);
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Http/DTO/AmountTest.php b/Tests/Unit/BusinessLogic/Http/DTO/AmountTest.php
index f4d2164..c2fab43 100644
--- a/Tests/Unit/BusinessLogic/Http/DTO/AmountTest.php
+++ b/Tests/Unit/BusinessLogic/Http/DTO/AmountTest.php
@@ -36,6 +36,17 @@ public function testAmountValueFromCurrencySmallestUnit()
$this->assertSame('10.147', $amountArray['value']);
}
+ public function testAmountValueWithoutMinorUnits()
+ {
+ $amount = Amount::fromSmallestUnit(10147, 'JPY');
+
+ $this->assertEquals(10147, $amount->getAmountValue());
+
+ $amountArray = $amount->toArray();
+
+ $this->assertSame('10147', $amountArray['value']);
+ }
+
public function testAmountValueToCurrencySmallestUnit()
{
$amount = Amount::fromArray(array(
@@ -46,6 +57,16 @@ public function testAmountValueToCurrencySmallestUnit()
$this->assertEquals(10147, $amount->getAmountValueInSmallestUnit());
}
+ public function testAmountConversionToCurrencySmallestUnit()
+ {
+ $amount = Amount::fromArray(array(
+ 'value' => 2.05,
+ 'currency' => 'EUR'
+ ));
+
+ $this->assertEquals(205, $amount->getAmountValueInSmallestUnit());
+ }
+
public function testExistingCurrency()
{
$amount = Amount::fromSmallestUnit(101475, 'UYW');
diff --git a/Tests/Unit/BusinessLogic/Http/ProxyTest.php b/Tests/Unit/BusinessLogic/Http/ProxyTest.php
index 5a3bdba..f3c37ff 100644
--- a/Tests/Unit/BusinessLogic/Http/ProxyTest.php
+++ b/Tests/Unit/BusinessLogic/Http/ProxyTest.php
@@ -2,6 +2,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\Http;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\OrgToken\ProxyDataProvider;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Proxy;
@@ -46,6 +48,13 @@ function () use ($self) {
return new Proxy($self->shopConfig, $self->httpClient, new ProxyDataProvider());
}
);
+
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
}
/**
diff --git a/Tests/Unit/BusinessLogic/Integration/EventHandler/IntegrationOrderAddressChangedEvenHandlerTest.php b/Tests/Unit/BusinessLogic/Integration/EventHandler/IntegrationOrderAddressChangedEvenHandlerTest.php
index ab70616..b5048b7 100644
--- a/Tests/Unit/BusinessLogic/Integration/EventHandler/IntegrationOrderAddressChangedEvenHandlerTest.php
+++ b/Tests/Unit/BusinessLogic/Integration/EventHandler/IntegrationOrderAddressChangedEvenHandlerTest.php
@@ -16,6 +16,7 @@
*/
class IntegrationOrderAddressChangedEvenHandlerTest extends IntegrationOrderEventHandlerTest
{
+
/**
* @throws \Exception
*/
diff --git a/Tests/Unit/BusinessLogic/Integration/EventHandler/IntegrationOrderEventHandlerTest.php b/Tests/Unit/BusinessLogic/Integration/EventHandler/IntegrationOrderEventHandlerTest.php
index eaba0f1..9888751 100644
--- a/Tests/Unit/BusinessLogic/Integration/EventHandler/IntegrationOrderEventHandlerTest.php
+++ b/Tests/Unit/BusinessLogic/Integration/EventHandler/IntegrationOrderEventHandlerTest.php
@@ -2,6 +2,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\Integration\EventHandler;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Orders\Order;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Payment;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\OrgToken\ProxyDataProvider;
@@ -90,6 +92,12 @@ function () {
return $me->defaultChannel;
});
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () use ($me) {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
$this->setUpTestOrderReferences();
}
diff --git a/Tests/Unit/BusinessLogic/MaintenanceMode/MaintenanceModeServiceTest.php b/Tests/Unit/BusinessLogic/MaintenanceMode/MaintenanceModeServiceTest.php
new file mode 100644
index 0000000..9de0387
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/MaintenanceMode/MaintenanceModeServiceTest.php
@@ -0,0 +1,42 @@
+maintenanceModeService = TestMaintenanceModeService::getInstance();
+ }
+
+ public function tearDown()
+ {
+ TestMaintenanceModeService::resetInstance();
+
+ parent::tearDown();
+ }
+
+ public function testWhenInMaintenanceMode()
+ {
+ $this->maintenanceModeService->checkMaintenanceMode();
+
+ $this->assertNotEmpty($this->maintenanceModeService->getCallHistory());
+ }
+
+
+ public function testWhenNotInMaintenanceMode()
+ {
+ TestMaintenanceModeService::$IN_MAINTENANCE = false;
+ $this->maintenanceModeService->checkMaintenanceMode();
+
+ $this->assertEmpty($this->maintenanceModeService->getCallHistory());
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Orders/OrderServiceTest.php b/Tests/Unit/BusinessLogic/Orders/OrderServiceTest.php
index 572889b..f97b411 100644
--- a/Tests/Unit/BusinessLogic/Orders/OrderServiceTest.php
+++ b/Tests/Unit/BusinessLogic/Orders/OrderServiceTest.php
@@ -2,6 +2,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\Orders;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Orders\Order;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\OrgToken\ProxyDataProvider;
@@ -76,6 +78,13 @@ function () {
}
);
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
$this->shopConfig->setAuthorizationToken('test_token');
$this->shopConfig->setTestMode(true);
$this->orderService = OrderService::getInstance();
@@ -125,9 +134,14 @@ public function testOrderCreationWithMultiplePaymentMethods()
{
$shopReference = 'test_reference_id';
$profileId = 'pfl_URR55HPMGx';
- $paymentMethods = array(PaymentMethods::PayPal, PaymentMethods::KlarnaSliceIt, PaymentMethods::KlarnaPayLater);
+ $paymentMethods = array(
+ PaymentMethods::PayPal,
+ PaymentMethods::KlarnaSliceIt,
+ PaymentMethods::KlarnaPayLater,
+ PaymentMethods::KlarnaPayNow,
+ );
$order = $this->getOrderData($shopReference, $profileId);
- $order->setMethod($paymentMethods);
+ $order->setMethods($paymentMethods);
$this->httpClient->setMockResponses(array($this->getMockOrderResponse(), $this->getMockOrderResponse()));
$createdOrder = $this->orderService->createOrder($shopReference, $order);
diff --git a/Tests/Unit/BusinessLogic/PaymentMethod/PaymentMethodServiceTest.php b/Tests/Unit/BusinessLogic/PaymentMethod/PaymentMethodServiceTest.php
index 4f7f36a..141132e 100644
--- a/Tests/Unit/BusinessLogic/PaymentMethod/PaymentMethodServiceTest.php
+++ b/Tests/Unit/BusinessLogic/PaymentMethod/PaymentMethodServiceTest.php
@@ -5,6 +5,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\PaymentMethod;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Amount;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\PaymentMethod;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\WebsiteProfile;
@@ -59,6 +61,13 @@ function () use ($me) {
}
);
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
$this->shopConfig->setAuthorizationToken('test_token');
$this->shopConfig->setTestMode(true);
$testProfile = new WebsiteProfile();
@@ -103,7 +112,7 @@ public function testGettingAllPaymentMethodConfigurationsResultWhenDBIsEmpty()
$result = $this->paymentMethodService->getAllPaymentMethodConfigurations($profileId);
- $this->assertCount(16, $result);
+ $this->assertCount(17, $result);
$this->assertNull($result[0]->getId());
$this->assertEquals($profileId, $result[0]->getProfileId());
$this->assertEquals('applepay', $result[0]->getMollieId());
@@ -131,7 +140,7 @@ public function testGettingAllPaymentMethodConfigurationsResultWithExistingDBCon
$result = $this->paymentMethodService->getAllPaymentMethodConfigurations($profileId);
- $this->assertCount(16, $result);
+ $this->assertCount(17, $result);
$this->assertNotNull($result[0]->getId());
$this->assertEquals($profileId, $result[0]->getProfileId());
$this->assertEquals('applepay', $result[0]->getMollieId());
diff --git a/Tests/Unit/BusinessLogic/PaymentMethod/PaymentTransactionDescriptionServiceTest.php b/Tests/Unit/BusinessLogic/PaymentMethod/PaymentTransactionDescriptionServiceTest.php
new file mode 100644
index 0000000..0c65c03
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/PaymentMethod/PaymentTransactionDescriptionServiceTest.php
@@ -0,0 +1,59 @@
+transactionDescriptionService = TestPaymentTransactionDescriptionService::getInstance();
+ }
+
+ public function tearDown()
+ {
+ TestPaymentTransactionDescriptionService::resetInstance();
+
+ parent::tearDown();
+ }
+
+ public function testTransactionDescriptionWithAllParameters()
+ {
+ $orderNumber = 100;
+ $storeName = 'Test Store';
+ $firstName = 'John';
+ $lastName = 'Doe';
+ $company = 'Test Company';
+ $cartNumber = 'CART001';
+
+ $this->transactionDescriptionService->mockDescription = 'Store: {storeName}, Order number: {orderNumber}, Customer: {customerFirstname} {customerLastname}, Company: {customerCompany}, cart: {cartNumber}';
+
+ $parameters = new DescriptionParameters($orderNumber, $storeName, $firstName, $lastName, $company, $cartNumber);
+ $description = $this->transactionDescriptionService->formatPaymentDescription($parameters, 'test');
+ $expected = "Store: $storeName, Order number: $orderNumber, Customer: $firstName $lastName, Company: $company, cart: $cartNumber";
+
+ $this->assertEquals($expected, $description);
+ }
+
+ public function testTransactionDescriptionWithSomeParameters()
+ {
+ $this->transactionDescriptionService->mockDescription = 'Order number: {orderNumber}, Customer: {customerLastname}';
+ $orderNumber = 100;
+ $lastName = 'Doe';
+
+ $parameters = DescriptionParameters::fromArray(array('orderNumber' => $orderNumber, 'customerLastname' => $lastName));
+
+ $description = $this->transactionDescriptionService->formatPaymentDescription($parameters, 'test');
+ $expected = "Order number: $orderNumber, Customer: $lastName";
+
+ $this->assertEquals($expected, $description);
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/Payments/PaymentServiceTest.php b/Tests/Unit/BusinessLogic/Payments/PaymentServiceTest.php
index cd19744..31edbba 100644
--- a/Tests/Unit/BusinessLogic/Payments/PaymentServiceTest.php
+++ b/Tests/Unit/BusinessLogic/Payments/PaymentServiceTest.php
@@ -2,6 +2,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\Payments;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Orders\Order;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Payment;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException;
@@ -76,6 +78,13 @@ function () {
}
);
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
$this->shopConfig->setAuthorizationToken('test_token');
$this->shopConfig->setTestMode(true);
$this->paymentService = PaymentService::getInstance();
@@ -274,7 +283,12 @@ public function testPaymentCreationWithMultiplePaymentMethods()
{
$profileId = 'pfl_QkEhN94Ba';
$shopReference = 'test_reference_id';
- $paymentMethods = array(PaymentMethods::PayPal, PaymentMethods::KlarnaSliceIt, PaymentMethods::KlarnaPayLater);
+ $paymentMethods = array(
+ PaymentMethods::PayPal,
+ PaymentMethods::KlarnaSliceIt,
+ PaymentMethods::KlarnaPayLater,
+ PaymentMethods::KlarnaPayNow,
+ );
$payment = Payment::fromArray(array(
'profileId' => $profileId,
'amount' => array(
diff --git a/Tests/Unit/BusinessLogic/Refunds/RefundServiceTest.php b/Tests/Unit/BusinessLogic/Refunds/RefundServiceTest.php
index ff4df48..b064dbb 100644
--- a/Tests/Unit/BusinessLogic/Refunds/RefundServiceTest.php
+++ b/Tests/Unit/BusinessLogic/Refunds/RefundServiceTest.php
@@ -2,6 +2,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\Refunds;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Amount;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Orders\Order;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Orders\OrderLine;
@@ -94,6 +96,13 @@ function () {
}
);
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
$this->shopConfig->setAuthorizationToken('test_token');
$this->shopConfig->setTestMode(true);
$this->refundService = RefundService::getInstance();
diff --git a/Tests/Unit/BusinessLogic/Shipments/ShipmentServiceTest.php b/Tests/Unit/BusinessLogic/Shipments/ShipmentServiceTest.php
new file mode 100644
index 0000000..58abb50
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Shipments/ShipmentServiceTest.php
@@ -0,0 +1,163 @@
+httpClient = new TestHttpClient();
+ TestServiceRegister::registerService(
+ HttpClient::CLASS_NAME,
+ function () use ($me) {
+ return $me->httpClient;
+ }
+ );
+ TestServiceRegister::registerService(
+ Proxy::CLASS_NAME,
+ function () use ($me) {
+ return new Proxy($me->shopConfig, $me->httpClient, new ProxyDataProvider());
+ }
+ );
+
+ TestServiceRegister::registerService(
+ OrderReferenceService::CLASS_NAME,
+ function () {
+ return OrderReferenceService::getInstance();
+ }
+ );
+
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
+ $this->shipmentService = ShipmentService::getInstance();
+ $this->shopConfig->setAuthorizationToken('test_token');
+ $this->shopConfig->setTestMode(true);
+ $testProfile = new WebsiteProfile();
+ $testProfile->setId('pfl_htsmhPNGw3');
+ $this->shopConfig->setWebsiteProfile($testProfile);
+ $this->orderReferenceRepository = RepositoryRegistry::getRepository(OrderReference::CLASS_NAME);
+ }
+
+ public function tearDown()
+ {
+ ShipmentService::resetInstance();
+
+ parent::tearDown();
+ }
+
+ /**
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\OrderReference\Exceptions\ReferenceNotFoundException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpAuthenticationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpCommunicationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpRequestException
+ */
+ public function testGetShipments()
+ {
+ $this->httpClient->setMockResponses(array($this->getMockAllShipments()));
+ $this->setUpTestOrderReferences('test_reference_id');
+ $shopReference = 'test_reference_id';
+ $shipments = $this->shipmentService->getShipments($shopReference);
+ $this->assertCount(2, $shipments);
+
+ $this->assertInstanceOf('Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Orders\Shipment', $shipments[0]);
+ }
+
+ /**
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\OrderReference\Exceptions\ReferenceNotFoundException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpAuthenticationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpCommunicationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpRequestException
+ */
+ public function testCreateShipmentWhenPaymentsApiIsUsed()
+ {
+ $shopReference = 'test_reference_id';
+ $this->setUpTestOrderReferences($shopReference, PaymentMethodConfig::API_METHOD_PAYMENT);
+ $this->expectException('\Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\OrderReference\Exceptions\ReferenceNotFoundException');
+ $this->shipmentService->shipOrder($shopReference);
+ }
+
+ /**
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\OrderReference\Exceptions\ReferenceNotFoundException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpAuthenticationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpCommunicationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpRequestException
+ */
+ public function testGetShipmentsWhenReferenceNotFound()
+ {
+ $shopReference = 'unknown_reference';
+
+ $this->expectException('\Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\OrderReference\Exceptions\ReferenceNotFoundException');
+ $this->shipmentService->getShipments($shopReference);
+ }
+
+ /**
+ * @return HttpResponse
+ */
+ protected function getMockAllShipments()
+ {
+ $response = file_get_contents(__DIR__ . '/../Common/ApiResponses/allShipments.json');
+
+ return new HttpResponse(200, array(), $response);
+ }
+
+ /**
+ * @param string $shopReference
+ * @param string $method
+ */
+ protected function setUpTestOrderReferences($shopReference, $method = PaymentMethodConfig::API_METHOD_ORDERS)
+ {
+ $payload = file_get_contents(__DIR__ . '/../Common/ApiResponses/orderResponse.json');
+ $order = Order::fromArray(json_decode($payload, true));
+ OrderReferenceService::getInstance()->updateOrderReference(
+ $order,
+ $shopReference,
+ $method
+ );
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/UI/Controllers/WebsiteProfileControllerTest.php b/Tests/Unit/BusinessLogic/UI/Controllers/WebsiteProfileControllerTest.php
index 884b715..e2da07d 100644
--- a/Tests/Unit/BusinessLogic/UI/Controllers/WebsiteProfileControllerTest.php
+++ b/Tests/Unit/BusinessLogic/UI/Controllers/WebsiteProfileControllerTest.php
@@ -5,6 +5,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\UI\Controllers;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\WebsiteProfile;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\OrgToken\ProxyDataProvider;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Proxy;
@@ -59,6 +61,13 @@ function () {
}
);
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
$this->websiteProfileController = new WebsiteProfileController();
$this->paymentMethodService = MockPaymentMethodService::getInstance();
}
diff --git a/Tests/Unit/BusinessLogic/Utility/CurrencySymbolServiceTest.php b/Tests/Unit/BusinessLogic/Utility/CurrencySymbolServiceTest.php
new file mode 100644
index 0000000..0e7121d
--- /dev/null
+++ b/Tests/Unit/BusinessLogic/Utility/CurrencySymbolServiceTest.php
@@ -0,0 +1,23 @@
+versionCheckService = TestVersionCheckService::getInstance();
+
+ $this->httpClient = new TestHttpClient();
+ $me = $this;
+ TestServiceRegister::registerService(
+ HttpClient::CLASS_NAME,
+ function () use ($me) {
+ return $me->httpClient;
+ }
+ );
+ TestServiceRegister::registerService(
+ VersionCheckProxy::CLASS_NAME,
+ function () use ($me) {
+ return new VersionCheckProxy($me->shopConfig, $me->httpClient, new ProxyDataProvider());
+ }
+ );
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+ }
+
+ public function tearDown()
+ {
+ TestVersionCheckService::resetInstance();
+
+ parent::tearDown();
+ }
+
+ /**
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpAuthenticationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpCommunicationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpRequestException
+ */
+ public function testVersionCheckWhenNewVersionAvailable()
+ {
+ $this->httpClient->setMockResponses(array($this->getMockNewVersionResults()));
+ $this->versionCheckService->checkForNewVersion();
+
+ $this->assertNotEmpty($this->versionCheckService->getCallHistory());
+ }
+
+ /**
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\Exceptions\UnprocessableEntityRequestException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpAuthenticationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpCommunicationException
+ * @throws \Mollie\Bundle\PaymentBundle\IntegrationCore\Infrastructure\Http\Exceptions\HttpRequestException
+ */
+ public function testVersionCheckWhenNewVersionNotAvailable()
+ {
+ TestShopConfiguration::$CURRENT_INTEGRATION_VERSION = '3.0.0';
+ $this->httpClient->setMockResponses(array($this->getMockNewVersionResults()));
+ $this->versionCheckService->checkForNewVersion();
+
+ $this->assertEmpty($this->versionCheckService->getCallHistory());
+ }
+
+ /**
+ * @return HttpResponse
+ */
+ protected function getMockNewVersionResults()
+ {
+ $response = file_get_contents(__DIR__ . '/../Common/ApiResponses/version-check.json');
+
+ return new HttpResponse(200, array(), $response);
+ }
+}
diff --git a/Tests/Unit/BusinessLogic/WebHook/WebHookTransformerTest.php b/Tests/Unit/BusinessLogic/WebHook/WebHookTransformerTest.php
index be30c73..ccd190d 100644
--- a/Tests/Unit/BusinessLogic/WebHook/WebHookTransformerTest.php
+++ b/Tests/Unit/BusinessLogic/WebHook/WebHookTransformerTest.php
@@ -2,6 +2,8 @@
namespace Mollie\Bundle\PaymentBundle\Tests\Unit\BusinessLogic\WebHook;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\ApiKey\ApiKeyAuthService;
+use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Authorization\Interfaces\AuthorizationService;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Orders\Order;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\DTO\Payment;
use Mollie\Bundle\PaymentBundle\IntegrationCore\BusinessLogic\Http\OrgToken\ProxyDataProvider;
@@ -96,6 +98,13 @@ function () {
}
);
+ TestServiceRegister::registerService(
+ AuthorizationService::CLASS_NAME,
+ function () use ($me) {
+ return ApiKeyAuthService::getInstance();
+ }
+ );
+
$this->setUpEventHandlers();
$this->setUpTestOrderReferences();
}
diff --git a/composer.json b/composer.json
index b1df1b3..052632f 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "mollie/orocommerce",
- "version": "4.0.8",
+ "version": "4.0.9",
"type": "symfony-bundle",
"description": "Mollie Payment Module for OroCommerce",
"keywords": [
@@ -38,6 +38,7 @@
"inghomepay",
"klarna",
"paylater",
+ "paynow",
"sliceit",
"mybank",
"oro",