Skip to content
This repository has been archived by the owner on Mar 15, 2024. It is now read-only.

Fix validation of IPN response #28

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ public function getConfigTreeBuilder()
->scalarNode('key')->isRequired()->end()
->end()
->end()
->enumNode('validation_by')
->values(array('url_ipn', 'pbx_retour'))
->cannotBeEmpty()
->defaultValue('url_ipn')
->info("Define the method for IPN validation. Select 'pbx_retour' only if you use 'PBX_REPONSE_A' option.")
->end()
->scalarNode('pbx_retour')
->info("PBX_RETOUR option for validation by 'pbx_retour'")
->end()
->end()
->end()

Expand Down
23 changes: 23 additions & 0 deletions DependencyInjection/LexikPayboxExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Lexik\Bundle\PayboxBundle\Paybox\Paybox;

/**
* This is the class that loads and manages your bundle configuration
Expand Down Expand Up @@ -33,6 +34,28 @@ public function load(array $configs, ContainerBuilder $container)
$config['parameters']['public_key'] = __DIR__ . '/../Resources/config/paybox_public_key.pem';
}

if('pbx_retour' == $config['parameters']['validation_by']){
if(!isset($config['parameters']['pbx_retour']) || !$config['parameters']['pbx_retour']){
throw new \InvalidArgumentException(
'The "pbx_retour" option must be set for validation_by "pbx_retour"'
);
}else{
// if PXB_REPONDRE_A is used the signature only sign parameter from PBX_RETOUR without 'Sign' parameter
$param_signed = explode(';', $config['parameters']['pbx_retour']);
$param_signed = array_map(function($param){
return substr($param, 0, strpos($param, ':'));
}, $param_signed);
$param_signed = array_diff($param_signed, array(Paybox::SIGNATURE_PARAMETER));

$container->setParameter('lexik_paybox.pbx_retour', $param_signed);
}
}else{
$container->setParameter('lexik_paybox.pbx_retour', null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should set the parameter with an empty array or you will get a warning when in_array() is called in Response on line 130:
if (in_array($key, $this->pbxRetour)) {

}

$container->setParameter('lexik_paybox.public_key', $config['parameters']['public_key']);
$container->setParameter('lexik_paybox.validation_by', $config['parameters']['validation_by']);


}
}
24 changes: 18 additions & 6 deletions Paybox/System/Base/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,34 @@ class Response
*/
private $publicKey;

/**
* @var string
*/
private $validationBy;

/**
* @var array
*/
private $pbxRetour;

/**
* Contructor.
*
* @param HttpRequest $request
* @param LoggerInterface $logger
* @param EventDispatcherInterface $dispatcher
* @param string $publicKey
* @param string $validationBy
* @param array $pbxRetour
*/
public function __construct(HttpRequest $request, LoggerInterface $logger, EventDispatcherInterface $dispatcher, $publicKey)
public function __construct(HttpRequest $request, LoggerInterface $logger, EventDispatcherInterface $dispatcher, $publicKey, $validationBy, array $pbxRetour)
{
$this->request = $request;
$this->logger = $logger;
$this->dispatcher = $dispatcher;
$this->publicKey = $publicKey;
$this->validationBy = $validationBy;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why creating and injecting a parameter if you don't use it in the class ?

$this->pbxRetour = $pbxRetour;
}

/**
Expand Down Expand Up @@ -113,7 +127,7 @@ protected function initData()
foreach ($this->getRequestParameters() as $key => $value) {
$this->logger->info(sprintf('%s=%s', $key, $value));

if (Paybox::SIGNATURE_PARAMETER !== $key) {
if (in_array($key, $this->pbxRetour)) {
$this->data[$key] = urlencode($value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use validation_by: url_ipn, the lexik_paybox.pbx_retour parameter and $this->pbxRetour will be null, and so $this->data won't be populated with the parameters ???

}
}
Expand All @@ -131,10 +145,7 @@ public function verifySignature()
$this->initData();
$this->initSignature();

$file = fopen($this->publicKey, 'r');
$cert = fread($file, 8192);
fclose($file);

$cert = file_get_contents($this->publicKey);
$publicKey = openssl_get_publickey($cert);

$result = openssl_verify(
Expand Down Expand Up @@ -163,4 +174,5 @@ public function verifySignature()

return $result;
}

}
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ lexik_paybox:
- '826' # GBP
- '840' # USD
- '978' # EUR
validation_by: url_ipn
```

The routing collection must be set in your routing.yml
Expand Down Expand Up @@ -215,6 +216,31 @@ Production
By default, getUrl() returns the preproduction url.
To toggle in production, you just need to specify 'prod' in parameter of the getUrl('prod') method.

Validation IPN response
-----------------------

For security, the status returned by PBX_EFFECTUE, PBX_REFUSE, PBX_ANNULE and PBX_ATTENTE, should
not be trusted as it can by altered by malicous user. You must instead use IPN notification.
IPN notification is send directly from Paybox server to the URL you specified either in PBX_REPONDRE_A
option or in Paybox interface.

If you use PBX_REPONDRE_A option you must specify in your `config.yml` file the following parameters :

```yml
lexik_paybox:
parameters:
validation_by: pbx_retour
pbx_retour: Mt:M;Ref:R;Auto:A;Erreur:E # report the PBX_RETOUR option you defined in your code
```

If you use the Paybox interface, you can let the default parameters :

```yml
lexik_paybox:
parameters:
validation_by: url_ipn
```

Resources
---------

Expand Down
2 changes: 1 addition & 1 deletion Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ services:

lexik_paybox.response_handler:
class: '%lexik_paybox.response_handler.class%'
arguments: ['@request=', '@logger', '@event_dispatcher', %lexik_paybox.public_key%]
arguments: ['@request=', '@logger', '@event_dispatcher', %lexik_paybox.public_key%, %lexik_paybox.validation_by%, %lexik_paybox.pbx_retour%]
tags:
- { name: monolog.logger, channel: payment }

Expand Down
4 changes: 3 additions & 1 deletion Tests/Paybox/System/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ protected function initMock(array $parameters, array $messages)
;

$publicKey = __DIR__ . '/../../../Resources/config/paybox_public_key.pem';
$validationBy = 'url_ipn';
$pbxRetour = array_diff(array_keys($parameters), array('Sign'));

$this->_response = new Response($request, $logger, $dispatcher, $publicKey);
$this->_response = new Response($request, $logger, $dispatcher, $publicKey, $validationBy, $pbxRetour);
}

protected function tearDown()
Expand Down