diff --git a/README.md b/README.md
index 1334879..351c20d 100644
--- a/README.md
+++ b/README.md
@@ -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));
}
diff --git a/src/Redsys/Tpv/Curl.php b/src/Redsys/Tpv/Curl.php
index f72aa11..827718b 100644
--- a/src/Redsys/Tpv/Curl.php
+++ b/src/Redsys/Tpv/Curl.php
@@ -1,7 +1,9 @@
['.$last['line'].'] '.$row.'';
- echo '
'; var_dump($info); echo '
';
+ echo '';
+ var_dump($info);
+ echo '
';
}
public static function dd($info, $title = '')
diff --git a/src/Redsys/Tpv/Signature.php b/src/Redsys/Tpv/Signature.php
index 4814e97..0d3cbeb 100644
--- a/src/Redsys/Tpv/Signature.php
+++ b/src/Redsys/Tpv/Signature.php
@@ -5,21 +5,39 @@
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('#([^<]+)#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])) {
@@ -27,9 +45,9 @@ private static function calculate($prefix, $fields, $values, $key)
}
}
- $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)
@@ -38,4 +56,14 @@ private static function encrypt3DES($message, $key)
return mcrypt_encrypt(MCRYPT_3DES, $key, $message, MCRYPT_MODE_CBC, $iv);
}
-}
\ No newline at end of file
+
+ 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));
+ }
+}
diff --git a/src/Redsys/Tpv/Tpv.php b/src/Redsys/Tpv/Tpv.php
index 1cdfbc1..d0bd3ca 100644
--- a/src/Redsys/Tpv/Tpv.php
+++ b/src/Redsys/Tpv/Tpv.php
@@ -2,7 +2,8 @@
namespace Redsys\Tpv;
use Exception;
-use DOMDocument, DOMElement;
+use DOMDocument;
+use DOMElement;
use Redsys\Messages\Messages;
class Tpv
@@ -152,22 +153,17 @@ 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('')
@@ -175,7 +171,9 @@ public function sendXml(array $options)
$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()
@@ -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);
@@ -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;
@@ -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)) {
@@ -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);
}
diff --git a/src/autoload.php b/src/autoload.php
index ad5b819..afc95ef 100644
--- a/src/autoload.php
+++ b/src/autoload.php
@@ -1,6 +1,5 @@