Publi

Implementar un TPV básico con la pasarela Pasat Internet de 4b en PHP

Es una pasarela de pago muy sencilla de implementar, por ejemplo es la opción que tenemos si somos del Banco Santander; aunque en su documentación que es bastante extensa (más de 70 páginas) hay cosas que no dejan muy claras, y lo peor de todo, no hay ningún ejemplo de pasarela básica con PHP (y los ejemplo que indican sólo funcionan en plataformas Windows).

Por otra parte, la comunicación entre nuestra web y la pasarela se hace en texto plano, hubiera preferido algo como XML y que hubiera que firmar las comunicaciones, aunque sean entre servidores… pero bueno.

El funcionamiento es sencillo:

  • Nuestra web envía al TPV una señal de que va a realizar un pago
  • Pasat la recibe y pide a nuestro servidor un desglose de la factura
  • Le enviamos el desglose de la factura
  • Si todo está correcto, el TPV le pide al usuario su número de tarjeta
  • El TPV envía a nuestro servidor la información de si está aceptada o denegada la transferencia
  • El usuario sale del TPV y vuelve a nuestra página

Mientras estamos desarrollando la aplicación, disponemos de un modo simulación, donde nos dan un número de tarjeta válido (sólo para la simulación), con su clave y su caducidad. Para diferenciar el modo simulación del modo real lo haremos a través de la URL a la que pediremos los datos.

Vamos por partes:

Decirle al TPV que queremos pagar

Para ello tenemos que hacer una petición POST a https://tpv2.4b.es/simulador/teargral.exe (si es el modo simulación) o https://tpv.4b.es/tpvv/teargral.exe si estamos en producción. En la petición POST debemos indicar referencia del pedido (order), codigo de tienda [PI00….] (store), idioma de la pasarela (idioma). Por ejemplo, para hacernos una idea de cómo podemos hacerlo con un pequeño formulario que sólo muestre un botón de acceso a la pasarela:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
   $ref=12345;
   $url='https://tpv2.4b.es/simulador/teargral.exe';
   $store='PI00....';
   $idioma='es';
?>
<form action="<?php echo $url; ?>" method="post">
<input type="hidden" name="order" value="<?php echo $ref; ?>" />
<input type="hidden" name="store" value="<?php echo $store; ?>" />
<input type="hidden" name="idioma" value="<?php echo $idioma; ?>" />
<input type="submit" name="enviar" value="Acceder a la pasarela de pago" />
</form>

En el campo store, para que no haya repeticiones, podemos incluir el número de factura, o podemos crear una cadena con todos los datos y pasarle un algoritmo de hash tipo MD5. Bueno, la pregunta del millón, ¿cómo es que tanto order como store están en inglés y luego encontramos un campo idioma ? Podía ser language, por ejemplo…

Desglose de la factura

Cuando el usuario accede al TPV, éste, por detrás, nos pide que le enviemos el desglose de la factura, el precio y alguna cosa más, por lo que el usuario nunca verá esta información, y nosotros deberíamos poder acceder a esta información en cualquier momento gracias al número del pedido (order), por lo que el TPV nos pasará como parámetro este código. Nosotros recibiremos por GET:

  • store = Código del comercio
  • order = Código de pedido

Luego nosotros tenemos que enviar el código de moneda (978 para el euro, según la norma ISO 4217), el precio en céntimos, el número de elementos del carrito, y por cada elemento del carrito, diremos su referencia, descripción, cantidad y precio todo esto a un elemento por línea (menos la moneda y el precio total que irán en la misma línea. En PHP podemos poner esto como ejemplo:

Por lo tanto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
define ('CRLF', "\r\n"); // Debemos utilizar esto como salto de línea (obligatorio)

function calcula_total($carrito)
{
   $total=0;
   for ($i=0; $i<count($carrito); $i++)
   {
      $total+=$carrito[$i]['Precio']*$carrito[$i]['Cantidad'];
   }

   return $total;
}

$store = (isset($_GET['store']))?$_GET['store']:false;
$order = (isset($_GET['order']))?$_GET['order']:false;

if (!$store || !$order)
  throw new Exception ("Datos insuficientes."); // Error, no hemos recibido uno de los dos datos

if ($store!='PI00...')
  throw new Exception ("Código de tienda erróneo"); // Error, no me han pasado el código de tienda bien

// Aquí debemos implementar un método de acceso al pedido, esto es sólo un ejemplo
if ($order!=12345)
  throw new Exception ("Pedido no valido"); // Error, el código de pedido no es válido

$carrito = array (
                  array('Ref' => 123,
                        'Descripcion' => 'Camiseta de Angry Birds',
                        'Precio' => 12,
                        'Cantidad' => 2
                       ),
                  array('Ref' => 124,
                        'Descripcion' => 'Gorra de Tux',
                        'Precio' => 6,
                        'Cantidad' => 1
                       )
                 );
$total = calcula_total($carrito) * 100; // Multiplicamos por 100 porque son céntimos

echo "M978".$total.CRLF;
echo count($carrito).CRLF;  // Ponemos el número de elementos del carrito
for ($i=0; $i<count($carrito); $i++)
{
   echo $carrito[$i]['Ref'].CRLF.$carrito[$i]['Descripcion'].CRLF.$carrito[$i]['Cantidad'].CRLF;
   echo ($carrito[$i]['Precio']*100).CRLF;
}

El TPV nos dice si la transacción es aceptada o denegada

De la misma forma que nos solicitó el desglose, el servidor nos avisará de la aceptación o denegación del pago, por lo que tenemos que utilizar este paso para conocer realmente el estado del pago, ya que este dato no pasa por el usuario, es comunicación entre servidores.
El TPV nos enviará (por GET):

  • pszPurchorderNum : el nombre más feo que le podía dar a nuestro order de toda la vida.
  • store : nuestro código de tienda
  • result : resultado de la operación (2 = denegada, 0 = aceptada)
  • nos envía más cosas, como el motivo de la denegación, tiempos, etc, pero para eso mejor miramos la documentación

Por lo tanto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
define('OPERACION_ACEPTADA', 0);
define('OPERACION_DENEGADA', 2);

$store = (isset($_GET['store']))?$_GET['store']:false;
$order = (isset($_GET['pszPurchorderNum']))?$_GET['pszPurchorderNum']:false;
$result = (isset($_GET['result']))?$_GET['result']:false;

if (!$store || !$order || $result===false) // === porque con 0 nos puede devolver un falso false
  throw new Exception ("Datos insuficientes."); // Error, no hemos recibido uno de los dos datos

if ($store!='PI00...')
  throw new Exception ("Código de tienda erróneo"); // Error, no me han pasado el código de tienda bien

// Aquí debemos implementar un método de acceso al pedido, esto es sólo un ejemplo
if ($order!=12345)
  throw new Exception ("Pedido no valido"); // Error, el código de pedido no es válido

if ($result==OPERACION_ACEPTADA)
{
// Aquí tendremos que almacenar en base de datos que todo ha ido bien
}
elseif ($result==OPERACION_DENEGADA)
{
// Aquí tenemos que almacenar en base de datos que ha sido denegada
} else {
// Almacenamos que ha ocurrido un error con la operación (para comprobar el dato manualmente)
/* No deberíamos hacerlo, pero como no hemos implementado nosotros el TPV no sabemos los secretos
   con los que nos puede sorprender en este aspecto. */

}

El usuario vuelve a la página

El TPV nos da la opción de volver a la página de la tienda, y cuando volvamos, la tienda nos enviará algunos parámetros sobre la operación, aunque éstos sí que los puede ver el usuario, así que no debemos hacer caso a ninguno de los datos que se nos mande por aquí. Como mucho a pszPurchorderNum, nuestro order de toda la vida.
Digo esto porque aquí también aparecerán parámetros para aceptar o denegar la transacción, pero es tan fácil como que el usuario cambie un valor, para que en nuestra tienda aparezca un artículo como pagado, por lo que tenemos que hacer caso del paso anterior, ya que habíamos guardado el resultado de la transacción.
Aquí nos limitamos a recibir el order y a decirle al usuario si la operación fue aceptada o denegada (repito, de los valores que obtuvimos antes). Si por casualidad, queremos filtrar un poco el código del pedido, podemos comprobar la fecha, si hace más de 1h que se hizo la transacción, el usuario no tendrá derecho a ver su estado (por ejemplo si un aprendiz de hacker se pone a introducir números de pedido al azar a ver lo que dice la página).

URLs involucradas

En nuestra tienda, tenemos que habilitar URLs para:

  • Que el TPV nos pida el desglose
  • Para que el TPV nos diga que la transacción ha sido aceptada
  • Para que el TPV nos diga que la transacción ha sido denegada : puede ser la misma de antes, ya que el TPV nos comunicará el código result con el que de verdad sabremos qué ha pasado.
  • Para que el usuario vuelva a nuestra página, algo así como una URL de transacción completada.

Configurando las URLs en nuestro sistema

Para ello debemos entrar en: https://tpv.4b.es/consultas, e introducir nuestros datos (la clave de comercio y el número de usuario suele ser el mismo) y entrar en la configuración, o en https://tpv.4b.es/config ahí nos encontraremos algo como esto:


Y rellenar los datos.

Paso a producción

Cuando todo esté listo, podemos dar el paso a producción, no es reversible, no podremos volver al entorno de pruebas (eso dicen, aunque no lo he probado). Lo malo, también es que el paso a producción tarda unas 6h, por lo que también hay que tenerlo en cuenta.

Actualización 13/02/2012 17:48: Gracias a Rodrigo por informar de un fallo en el código de ejemplo. Faltaba un CRLF y sobraba una tilde (manías de querer escribir bien) 🙂

También podría interesarte...

There are 15 comments left Ir a comentario

  1. Pingback: Bitacoras.com /

  2. oriol /
    Usando Mozilla Firefox Mozilla Firefox 17.0 en Windows Windows 7

    Perfecto!!! justo lo que andaba buscando! gracias a gente como tu y a sus aportaciones altruistas el mundo es un poco mejor. 😉 gracias!

    1. admin / Post Author
      Usando Mozilla Firefox Mozilla Firefox 17.0 en Ubuntu Linux Ubuntu Linux

      Muchas gracias Oriol !

  3. Albert /
    Usando Mozilla Firefox Mozilla Firefox 18.0 en Windows Windows 7

    Muy buenas! estoy intentando configurar correctamente la pasarela de pago, pero me está dando errores todo el rato… seria posible que me echaras un cable?
    estoy siguiendo tus pasos y cuando lo pongo en marcha, me salta este error en teargral:

    Su cesta no devuelve el desglose correcto

    Compruebe el formato del importe total de la compra: MXXXNNN…
    (XXX c�digo de moneda ISO, NNN… cifra del importe)

    El importe no es correcto

    que me estoy saltando?
    gracias por anticipado!

  4. admin / Post Author
    Usando Mozilla Firefox Mozilla Firefox 17.0 en Ubuntu Linux Ubuntu Linux

    @Albert
    ¿Te pasa con el ejemplo que tengo en la página? Si no es así podrías poner la parte que emite el desglose por aquí o en pastebin para echarle un ojo ?

  5. Rodrigo /
    Usando Google Chrome Google Chrome 24.0.1312.57 en Mac OS X Mac OS X 10.8.2

    A mi también me da ese error, me dice que no recibe el número de elementos. He usado el código tal cual y creo que es un problema con el salto de linea ya que lo esta imprimiendo en una linea y el sistema lo espera uno por linea. ¿Alguna idea?

  6. admin / Post Author
    Usando Mozilla Firefox Mozilla Firefox 18.0 en Ubuntu Linux Ubuntu Linux

    @Rodrigo
    De hecho la constante CRLF es la que hace que se envíen en líneas separadas. Ahora mismo no tengo acceso al TPV por lo que no puedo probar que este código funcione, aunque lo probé en su día.
    Ahora que lo miro, probad metiendo un CRLF al final de esta línea:

    echo $carrito[$i][‘Ref’].CRLF.$carrito[$i][‘Descripcion’].CRLF.$carrito[$i][‘Cantidad’];

    dejándola así:

    echo $carrito[$i][‘Ref’].CRLF.$carrito[$i][‘Descripcion’].CRLF.$carrito[$i][‘Cantidad’].CRLF;

    Me haríais un gran favor si me decís si funciona o no.

  7. Rodrigo /
    Usando Google Chrome Google Chrome 24.0.1312.57 en Mac OS X Mac OS X 10.8.2

    Ya he dado con el error del código. Es una tontería como casi siempre.

    1.- El primer campo descripción lo tienes escrito con tilde por lo que lo interpreta mal.
    2.- Falta un CLRF al final de cantidad dentro del bucle.

    Saludos y gracias por tu código 😉

  8. admin / Post Author
    Usando Mozilla Firefox Mozilla Firefox 18.0 en Ubuntu Linux Ubuntu Linux

    @Rodrigo
    Muchas gracias Rodrigo, voy a actualizar el post.

  9. Tony /
    Usando Google Chrome Google Chrome 25.0.1364.97 en Windows Windows 7

    Hola debido al mal servicio de tpv 4b para solventar problemas sobre la implementación de su pasarela de pago. Cosa que no me ha pasado con paypal ( excelente documentación y foro de ayuda ) he decidido realizarte una pregunta sobre implementación a ver si tu podrías ayudarme.

    En las URLs de transacción aceptada y denegada, he indicado la misma URL y en ella compruebo si la operación ha sido aceptada o denegada. Cuando me llega una operación denegada inmediatamente le pongo el estado a erróneo.
    Bien, el problema es que estoy viendo que una misma referencia de pedido puede llegar varias veces por lo que si una operación con referencia 0001 me llega como errónea al rato puede llegarme esa misma referencia 0001 como aceptada y al haberla puesto en mi BD errónea ese pedido se queda como erróneo aunque en la pagina de TPV aparezca como pagado.

    Mi duda es:
    ¿Si un pedido llega erróneo debo dejarlo como pendiente hasta pasado un tiempo y sólo cambiarle el estado si es aceptada la operación? ¿te ha sucedido lo mismo y lo has solventado?

    Un saludo y muchas gracias por la ayuda que proporcionas.

    1. admin / Post Author
      Usando Mozilla Firefox Mozilla Firefox 20.0 en Ubuntu Linux Ubuntu Linux

      ¿A lo mejor utilizas pago retrasado, o soporte para ingresos en cuenta? Lo que puedes intentar, no sé si estarás a tiempo, es ver la salida completa del TPV, es decir, todos los datos que te envía, para ver si por algún lado hay alguna variación con un estado de transacción errónea; por ejemplo un campo que diga que se ha retrasado, o se ha pospuesto; en el caso de seguir con resultados de este tipo, yo prefiero dar al usuario la opción de que verifique manualmente que la operación se ha realizado y la valide… no es automático, pero tal y como está hecha la pasarela no quiero que alguien descubra alguna forma de amañar el sistema.

  10. Nooby /
    Usando Mozilla Firefox Mozilla Firefox 20.0 en Ubuntu Linux Ubuntu Linux

    Hola! Gracias por el aporte. Una duda:
    -Cada parte es un mismo php o son varios?

    1. admin / Post Author
      Usando Mozilla Firefox Mozilla Firefox 20.0 en Ubuntu Linux Ubuntu Linux

      Lo podemos poner todo en el mismo si queremos, aunque es un ejemplo muy básico; ya dependerá de cómo lo quieras modularizar luego.

  11. solrak /
    Usando Google Chrome Google Chrome 36.0.1985.143 en Windows Windows 7

    Hola, una pregunta:
    La pasarela pasat 4b sigue activa?
    Intento acceder al panel de administración https://tpv.4b.es/consultas y me da un error 404

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 30.0 en Linux Linux

      La plataforma yo creo que se encuentra en migración, porque se han fusionado con Sermepa y ahora la plataforma es Redsýs. De todas formas, a los clientes antiguos les sigue funcionando la plataforma de pago bien, sin necesidad de modificar nada (sólo las IPs de la plataforma, si las tienen restringidas).

      Para las consultas, yo creo que tienen que entrar en redsys.es directamente.

      Saludos

Leave a Reply