Skip to content

Commit

Permalink
Improved SHA256 integration
Browse files Browse the repository at this point in the history
  • Loading branch information
eusonlito committed Dec 2, 2015
1 parent 055e002 commit 43e9927
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 41 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ $TPV = new Redsys\Tpv\Tpv($config);
# Realizamos la comprobación de la transacción

try {
$datos = $TPV->checkTransaction($_POST);
$datos = $TPV->checkTransactionXml($_POST);
} catch (Exception $e) {
die(file_put_contents(__DIR__.'/logs/errores-tpv.log', $e->getMessage(), FILE_APPEND));
}
Expand Down
4 changes: 3 additions & 1 deletion src/Redsys/Tpv/Curl.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<?php
namespace Redsys\Tpv;

use DOMDocument, DOMXPath, Exception;
use DOMDocument;
use DOMXPath;
use Exception;

class Curl
{
Expand Down
4 changes: 3 additions & 1 deletion src/Redsys/Tpv/Debug.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ public static function d($info, $title = '', $shift = 1)
}

echo '<code><strong>['.$last['line'].'] '.$row.'</strong></code>';
echo '<pre>'; var_dump($info); echo '</pre>';
echo '<pre>';
var_dump($info);
echo '</pre>';
}

public static function dd($info, $title = '')
Expand Down
40 changes: 34 additions & 6 deletions src/Redsys/Tpv/Signature.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,49 @@

class Signature
{
public static function fromValues($prefix, $values, $key)
public static function fromValues($prefix, array $values, $key)
{
$fields = array('Amount', 'Order', 'MerchantCode', 'Currency', 'TransactionType', 'MerchantURL');

return self::calculate($prefix, $fields, $values, $key);
}

public static function fromTransaction($prefix, $values, $key)
public static function fromTransaction($prefix, array $values, $key)
{
$fields = array('Amount', 'Order', 'MerchantCode', 'Currency', 'Response');

return self::calculate($prefix, $fields, $values, $key);
}

private static function calculate($prefix, $fields, $values, $key)
public static function fromTransactionXML($prefix, array $values, $key)
{
$fields = array('Amount', 'Order', 'MerchantCode', 'Currency', 'Response', 'TransactionType', 'SecurePayment');

return self::calculate($prefix, $fields, $values, $key);
}

public static function fromXML($xml, $key)
{
preg_match('#<DS_MERCHANT_ORDER>([^<]+)</DS_MERCHANT_ORDER>#i', $xml, $order);

if (empty($order[1])) {
throw new Exception('Can not be extracted Order from XML string');
}

return self::MAC256($xml, self::encryptKey($order[1], $key));
}

private static function calculate($prefix, array $fields, array $values, $key)
{
foreach ($fields as $field) {
if (!isset($values[$prefix.$field])) {
throw new Exception(sprintf('Field <strong>%s</strong> is empty and required', $field));
}
}

$key = self::encrypt3DES($values[$prefix.'Order'], base64_decode($key));
$key = self::encryptKey($values[$prefix.'Order'], $key);

return base64_encode(hash_hmac('sha256', base64_encode(json_encode($values)), $key, true));
return self::MAC256(base64_encode(json_encode($values)), $key);
}

private static function encrypt3DES($message, $key)
Expand All @@ -38,4 +56,14 @@ private static function encrypt3DES($message, $key)

return mcrypt_encrypt(MCRYPT_3DES, $key, $message, MCRYPT_MODE_CBC, $iv);
}
}

private static function encryptKey($order, $key)
{
return self::encrypt3DES($order, base64_decode($key));
}

private static function MAC256($string, $key)
{
return base64_encode(hash_hmac('sha256', $string, $key, true));
}
}
98 changes: 68 additions & 30 deletions src/Redsys/Tpv/Tpv.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
namespace Redsys\Tpv;

use Exception;
use DOMDocument, DOMElement;
use DOMDocument;
use DOMElement;
use Redsys\Messages\Messages;

class Tpv
Expand Down Expand Up @@ -152,30 +153,27 @@ public function sendXml(array $options)
{
$this->values = array();

if (isset($options['Order'])) {
$options['Order'] = $this->getOrder($options['Order']);
}

if (isset($options['Amount'])) {
$options['Amount'] = $this->getAmount($options['Amount']);
}
$options['Order'] = $this->getOrder($options['Order']);
$options['Amount'] = $this->getAmount($options['Amount']);

$this->setValueDefault($options, 'MerchantCode');
$this->setValueDefault($options, 'MerchantName');
$this->setValueDefault($options, 'MerchantData');
$this->setValueDefault($options, 'MerchantURL');
$this->setValueDefault($options, 'Currency');
$this->setValueDefault($options, 'Terminal');
$this->setValueDefault($options, 'TransactionType');
$this->setValueDefault($options, 'Order');
$this->setValueDefault($options, 'Amount');

$this->setValue($options, 'TransactionType');
$this->setValue($options, 'Order');
$this->setValue($options, 'Amount');

$Curl = new Curl(array(
'base' => $this->getPath('')
));

$Curl->setHeader(CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

return $Curl->post('/operaciones', array(), 'entrada='.$this->xmlArray2string($this->setXmlValues()));
return $Curl->post('/operaciones', array(
'entrada' => $this->xmlArray2string($this->setXmlValues())
));
}

private function setXmlValues()
Expand All @@ -192,19 +190,18 @@ private function setXmlValues()
private function xmlArray2string($xml)
{
$doc = new DOMDocument();
$doc->formatOutput = true;

$data = $doc->createElement('DATOSENTRADA');

foreach ($xml as $key => $value) {
$data->appendChild((new DOMElement($key, $value)));
$data->appendChild((new DOMElement(strtoupper($key), $value)));
}

$root = $doc->createElement('REQUEST');

$root->appendChild(new DOMElement('DS_SIGNATUREVERSION', $this->options['SignatureVersion']));
$root->appendChild(new DOMElement('DS_SIGNATURE', $this->getValuesSignature()));
$root->appendChild($data);
$root->appendChild(new DOMElement('DS_SIGNATUREVERSION', $this->options['SignatureVersion']));
$root->appendChild(new DOMElement('DS_SIGNATURE', Signature::fromXML((string)$doc->saveXML($data), $this->options['Key'])));

$doc->appendChild($root);

Expand All @@ -216,6 +213,31 @@ public function xmlString2array($xml)
return json_decode(json_encode(simplexml_load_string($xml)), true);
}

public function checkTransactionXml(array $post)
{
$prefix = 'Ds_';

if (empty($post) || empty($post[$prefix.'Signature']) || empty($post[$prefix.'MerchantParameters'])) {
throw new Exception('_POST data is empty');
}

$data = $this->getTransactionParameters($post);
$data = $this->xmlString2array($data['datos']);

if (empty($data)) {
throw new Exception('_POST data can not be decoded');
}

$this->checkTransactionError($data, $prefix);
$this->checkTransactionResponse($data, $prefix);

$signature = Signature::fromTransactionXml($prefix, $data, $this->options['Key']);

$this->checkTransactionSignature($signature, $post[$prefix.'Signature']);

return array_merge($post, $data);
}

private function setValueDefault(array $options, $option)
{
$code = $this->option_prefix.$option;
Expand Down Expand Up @@ -291,16 +313,33 @@ public function checkTransaction(array $post)
throw new Exception('_POST data can not be decoded');
}

$this->checkTransactionError($data, $prefix);
$this->checkTransactionResponse($data, $prefix);

$signature = Signature::fromTransaction($prefix, $data, $this->options['Key']);

$this->checkTransactionSignature($signature, $post[$prefix.'Signature']);

return array_merge($post, array_map('urldecode', $data));
}

private function checkTransactionError(array $data, $prefix)
{
$error = isset($data[$prefix.'ErrorCode']) ? $data[$prefix.'ErrorCode'] : null;

if ($error) {
if ($message = Messages::getByCode($error)) {
throw new Exception(sprintf('TPV returned error code %s: %s', $error, $message['message']));
} else {
throw new Exception(sprintf('TPV returned unknown error code %s', $error));
}
if (empty($error)) {
return null;
}

if ($message = Messages::getByCode($error)) {
throw new Exception(sprintf('TPV returned error code %s: %s', $error, $message['message']));
} else {
throw new Exception(sprintf('TPV returned unknown error code %s', $error));
}
}

private function checkTransactionResponse(array $data, $prefix)
{
$response = isset($data[$prefix.'Response']) ? $data[$prefix.'Response'] : null;

if (is_null($response) || (strlen($response) === 0)) {
Expand All @@ -314,19 +353,18 @@ public function checkTransaction(array $post)
throw new Exception(sprintf('Response code is unknown %s', $response));
}
}
}

$signature = Signature::fromTransaction($prefix, $data, $this->options['Key']);

$postSignature = strtr($post[$prefix.'Signature'], '-_', '+/');
private function checkTransactionSignature($signature, $postSignature)
{
$postSignature = strtr($postSignature, '-_', '+/');

if ($signature !== $postSignature) {
throw new Exception(sprintf('Signature not valid (%s != %s)', $signature, $postSignature));
}

return array_merge($post, array_map('urldecode', $data));
}

private function getTransactionParameters(array $post)
public function getTransactionParameters(array $post)
{
return json_decode(base64_decode(strtr($post['Ds_MerchantParameters'], '-_', '+/')), true);
}
Expand Down
3 changes: 1 addition & 2 deletions src/autoload.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?php
spl_autoload_register(function ($className)
{
spl_autoload_register(function ($className) {
if (strpos($className, 'Redsys\\Tpv') !== 0) {
return;
}
Expand Down

0 comments on commit 43e9927

Please sign in to comment.