Ricard Catalan
Hola ♥, soy Ricard Catalan Díaz. Soy diseñador y programador web , quieres conocerme?

9 de diciembre de 2008

En este tercer artículo sobre crear una pasarela de pago con BBVA, explicaré el archivo pasarela.php.

Brevemente expondré los pasos que hará el cliente en nuestro comercio y que importancia tiene el documento PHP pasarela:

  • - El cliente insertará los datos de compra/reserva en el formulario que hayamos creado.
  • - Finalizada la insercción de datos mostraremos el archivo confirm.php (por ejemplo) según la primer parte del artículo
  • - El cliente al confirmar la compra, enviaremos mediante POST la variable $xml, con formato XML que contiene la id transaccion, el precio formateado, la firma de la compra, etc.
  • - Al cliente se le redirecciona al tpv virtual del BBVA donde realiza los pasos finales de la compra.
  • - El BBVA nos enviará mediante GET una variable peticion en formato XML a pasarela.php, independientemete de si se realizó o no la compra
  • - En pasarela.php realizaremos los pasos siguientes:
    • Parseo del XML enviado, extracción de la id transacción y confirmación del coderror (el coderror es devuelto por el bbva y nos indica si la compra se realizó o no)
    • Si la compra se realizó, accedemos al archivo XML guardado en el servidor con el nombre de la id transacción
    • Extraemos los datos del cliente, y lo insertamos en nuestra base de datos
    • Borramos el archivo XML (opcional), y enviamos un e-mail al usuario que realizó la compra/reserva y otro e-mail al comercio.

Pasarela.php es llamado una vez se confirma o no la compra/reserva, por lo tanto no mostraremos ningún dato, solo realizaremos un proceso de la información y realizaremos los pasos según el estado de la compra.
El tpv del BBVA mostrará la confirmación de la compra o el error que hubo, justo después de enviar los datos a pasarela.php, y nos dará la opción mediante un botón de redirigir a nuestro comercio. Esto es opcional y los pondremos al enviar la variable $xml mediante POST dentro de la etiqueta urlredir.

A continuación pongo parte del código:


<?php
//Recibimos variable peticion el BBVA, y confirmamos que hayan datos
if(!empty($_GET['peticion'])){
require_once("includes/admin/Connect_db.php");
require_once("includes/admin/info_db.php");
require_once('pasarela/mailing.php'); //Usado para el envio de e-mails. Usa la clase PHPMAILER
//Abrimos el fichero en modo de escritura
$peticion=$_GET['peticion'];
$file='peticion/pasarela.xml';
$pasarela = fopen($file,"w+");
//Escribimos temporalmente el archivo XML recibido por el BBVA. Siempre estará en modo sobreescritura
fputs($pasarela,$peticion);
chmod($file,0755); //Modificamos los permisos
fclose($pasarela); //Cerramos el fichero
//Leemos el archivo guardado en XML mediante SimpleXML_load_File, esta funcion esta incluida en PHP 5.1.0
//o bien Libxml 2.6.0. Convierte un XML en un objeto, facilitando su parseo y acceso.
$resXml = simplexml_load_file($file);
foreach ($resXml->respago as $res){
$idtrans=$res->idtransaccion;
$coderror=$res->coderror; //Coderror nos indicará si se realizó o no la compra
}
//Recuperamos el XML con la misma IDTransaccion y los datos del cliente
//Coderror=0000; indica que se realizó correctamente la compra.
if($coderror==0000){
$fileTrans="xml/$idtrans.xml";
//Comprovamos que exista el fichero
if(file_exists($fileTrans)){
$resXml = simplexml_load_file($fileTrans);
foreach($resXml->transid as $res){
$nom=$res->nombre;
$apellido=$res->apellido;
$email=$res->email;
$lang=$res->idioma; //Idioma que usa el cliente durante la reserva, para enviar el correo según idioma usado
$precio=$res->precio;
/* ... Aquí extraeremos el resto del XML guardado, dependerá de lo datos guardados */
}


Como se puede ver en pasarela.php realizaremos bastantes comprovaciones de errores. (He implementado varias, pero se pueden realizar todo tipo de comprovaciones).
Este código no necesita mucha explicación:
  • Recibimos una variable, guardamos temporalmente sus datos y comprovamos si se realizó la compra, si es asi, accedemos a los datos del cliente guardados en el XML.

En otro artículo ya dije que este archivo es el más "dinámico", porque según nuestro comercio puede ser diferente, y por lo tanto se debe considerar este ejemplo como tal.
Continuo con otra parte del código:

//Creamos el e-mail con los datos (estos datos serán usados en el caso de error) y nos conectamos a la Base de Datos
$clientname=$nom." ".$apellido; $body="Datos de Contacto del cliente:<br><br>";
$body.="Localizador de reserva <b>$idtrans</b><br>";
//... Aqui va el resto del cuerpo del e-mail
$conexion = new Connect_db($hostname,$dbname,$username,$password); //Creamos instancia del objeto Connect DB
$conexion->getConnect(); //Nos conectamos a la base de datos
//GUARDAMOS LOS DATOS EN LA BASE DE DATOS de la RESERVA si el estado es OK
$sql="INSERT INTO $reservas VALUES($idtrans,'$nom','$apellido','$email','$telf1','$telf2','$nacion','$fecha',$pase,'$servicio',$adults,$nens,'reservado',$precio);";
$res=mysql_query($sql);
if($res>0){ //Si se guardó correctamente en la base de datos
unlink($fileTrans);//Borramos el archivo del XML con los datos del cliente
//Envio de correo al cliente dela reserva y al cliente de la web
//OPCIONAL: Si nuestro comercio esta en varios idiomas, aquí podremos elegir un envio de correo según el idioma del usuario
switch($lang){
case 'es': $asunto="Reserva $nombre-del-comercio num.$idtrans";
//CLIENTE Creamos instancia para enviar mail al cliente
//Esta clase "Mailing" envía un correo mediante PHPMAILER, aquí en vez de usar esta clase, podemos usar directamente una llamada a PHPMAILER o la funcion que usemos para enviar el correo
$mail= new Mailing($email,$nombre-del-comercio,$asunto,$body);
$mail->sendMail(); //enviamos mail al cliente
//Instancia para enviar mail al comercio
$mail2= new Mailing($email,$clientname,$asunto,$bodyAlt); //$bodyAlt es un texto alternativo al que se envia al cliente
$mail2->sendMailWebClient(); //enviamos mail
break;
case 'en': $asunto="$nombre-del-comercio reservation num.$idtrans""; //Idioma inglés
//... Resto del código del envio de correo

En este código creamos una variable $body donde almacenamos el cuerpo del e-mail, y lo usaremos por si surge algún error.
Dentro del switch es donde pondremos todos los datos del envio de e-mail al cliente y lo enviaremos. Hay que tener en cuenta que no será necesario usar un switch si solo usamos un idioma en nuestro comercio.
A continuación pondré el resto del código con los errores, que puedan surgir:

}else{ //Error al guardar los datos, reintentamos la insercción.
$reintento=0;
while($reintento<5){
sleep(1);//Esperamos 1 segundo antes del siguiente reintento
$res=mysql_query($sql);
if($res>0) break; //Si hubo éxito en la insercción, salimos del bucle con un break
$reintento++;
}
if($res>0){
unlink($fileTrans);//Borramos el archivo del XML con los datos del cliente
$bodyAlt.="<b>Esta reserva a requerido $reintento intentos de registro en la Base de Datos.</b>"; //Esto se añadirá solo en el envio al comercio a modo informativo
switch($lang){
case 'es': /*... Aqui va el resto de código igual que el anterior */;
}
}else{ //Hubo error en los siguientes 5 intentos, se envia e-mail al comercio indicando el error y un e-mail al cliente con la reserva, puesto que existe un reserva, sin embargo ha fallado la insercción en la base de datos, tras 5 intentos
$errorMysql=mysql_error(); //GUARDAMOS ERROR DE MYSQL
$error2="Ocurrió un problema en la BD tras $reintento intentos de inserción, el error que devolvió MySQL es el siguiente:<br>"; //esto es parte del e-mail que enviaremos al comercio
$error2.="<b>".$errorMysql."</b><br><br>";
$dia=date('d');
$mes=date('n');
$hora=date('G\:i\:s');
//AQUI va el resto del e-mail para envia el correo al comercio, y posteriormente el envio de e-mail al cliente, igual que en los anteriores casos
}
}
}else{ //En el caso de que no exista el archivo XML
$dia=date('d');
$mes=date('n');
$hora=date('G\:i\:s');
$asunto="Error: xml no encontrado";
$body="Ocurrio un problema el dia $dia del mes $mes a la hora $hora<br>";
//AQUI va el resto de código para el envio de correo al Comercio con el error.
}


Primero hago un reintento de insercción en la base de datos. Es posible que esté saturado el servicio o que pueda surgir algún problema, si usamos transacciones con SQL, este paso no sería necesario.
Para no saturar sel servicio hago un reintento 5 veces con una espera de 1 segundo por reintento. (Podemos cambiar el número de reintentos y tiempo de espera)

Desde mi punto de vista, es preferible hacer varios reintentos, pues estamos trabajando con compras reales, y siempre es preferible saturar el servicio unos segundos, que perder el registro de la compra en nuestra base de datos.

Posteriormente de los reintentos, si tuvo éxito alguno, salimos del bucle y ejecutamos el envio de e-mail, en caso contrario enviaremos un e-mail con el error ocurrido al comercio y un e-mail normal de compra al cliente, pues la compra existe realmente, aunque no aparecerá en nuestra base de datos.

Para finalizar esta parte del código, depuro el error en el caso de no existir el XML.
En este caso, el problema sería grabe, pues no tendremos los datos del cliente que realizó la compra.
Este error será común si a la hora de generar la id transacción no la creamos con 12 dígitos, y el tpv virtual del BBVA añadirá un cero a la izquierda, por lo que ocasionará una id transacción erronea. Hay que tener mucho cuidado con la generación de esta clave única

Por último pongo el resto de código:

//Este Error se produce cuando el coderror devuelto por el BBVA no coincide con la compra realizada (siempre es 0000)
}else{
//Volvemos a leer el XML para parsear la información del DESERROR
foreach ($resXml->respago as $res){
$deserror=$res->deserror; //Aquí esta la descripción del error devuelto por el BBVA
}//Leemos el XML del cliente con su ID Trans
$fileTrans="xml/$idtrans.xml";
if(file_exists($fileTrans)){
$resXml = simplexml_load_file($fileTrans);
foreach($resXml->transid as $res){
$nom=$res->nombre;
$apellido=$res->apellido;
$precio=$res->precio;
//AQUI va el resto de código de extracción de dato del XML
}
$dia=date('d');
$mes=date('n');
$hora=date('G\:i\:s');
$clientname=$nom." ".$apellido;
$asunto="Compra no realizada: coderror no correcto";
$body="Se intentó realizar una compra el dia $dia del mes $mes a la hora $hora<br>";
$body.="El BBVA devolvió este error (code error:<b>$coderror</b>): <b>$deserror</b><br>";
//Enviamos un e-mail al comercio con los datos, por si les intersa ponerse en contacto con el cliente.
$mail= new Mailing('un e-mail','Compra-no-realizada',$asunto,$body);
$mail->sendMailWebClient(); //enviamos mail al comercio
unlink($fileTrans);//Borramos el archivo del XML con los datos del cliente, al enviar un e-mail con los datos
} //Hay que tener en cuenta que aquí compruebo si existe o no el XML, se puede realizar.
}
}

Esta última parte, compruebo si el BBVA devolvió un error, y recojo que tipo de error es y envio un e-mail al comercio.
En este caso no se debe enviar ningún e-mail al cliente, puesto que la compra no existe, es un e-mail informativo para el comercio, indicandole los datos del cliente que intentó realizar una compra.

Este es el útimo artículo de la creación de un pasarela de pago con bbva, estos son los otros 2 artículos:

Puede descargar los archivos del ejemplo aquí:

Etiquetas: , , , ,


29 comentarios:

Anonymous Anónimo ha dicho...
A las 31 de enero de 2009, 18:05

muy, muy buen articulo. Es el mas clarito y mejor documentado de los que he encontrado sobre pasarelas de pago.

enhorabuena.

Nacho.

 
Anonymous Anónimo ha dicho...
A las 2 de febrero de 2009, 19:37

He probado el codigo, comprobado que la generacion de la firma y del idtrans es correcto, pero el tpv me retorna el error "508 - Operacion No Procesada (02020000008)".

Un saludo.

 
Blogger Katan ha dicho...
A las 9 de febrero de 2009, 13:10

Gracias Nacho por tu comentario.

El error que retorna, comprueba en la documentación a que puede ser debido. Lo más seguro es que sea porque la Tarjeta que has introducido no es válida.

Recordar que en entorno de pruebas se debe usar las tarjetas de prueba facilitadas en la documentación y como CVV 123, y en el caso de tener 3D Secure poner también 123.

Espero que te sirva, un saludo.

 
Anonymous Anónimo ha dicho...
A las 26 de febrero de 2009, 11:41

QUE GRANDE!
La verdad que la documentación del BBVA es PENOSA!

Además, solamente viene ASP y JSP, nada de PHP, al menos lo que tenemos aqui.

Muchas gracias, y ya tenemos una base sobre la cual empezar a implementar esta solución.

 
Anonymous Anónimo ha dicho...
A las 2 de marzo de 2009, 20:13

Hola SicilianGirl,

Un gran Post!!!!. Me ha venido de fabula, Gracias. Los del BBVA poner un link a tu página :)

Saludos,

 
Blogger Katan ha dicho...
A las 17 de marzo de 2009, 9:48

Gràcies Albert.
La verdad es que debería arreglarlo un poco, porque repasandolo hay partes de código que sobran y mejorables, pero al menos es un punto de partida para entender como funciona.

Una salutació.

 
Anonymous Raquel ha dicho...
A las 11 de mayo de 2009, 13:11

Muy buen post!

Estoy haciendo pruebas para ver si me funciona con una página. Pero me da error de sintaxis en la línea 9 de IdTransaccion...

Los ejemplos que has puesto funcionan bien o hay que retocarlos por completo??
Es por descartar que el error sea del código ;)

Un saludo!

 
Blogger dpBook - DARweb ha dicho...
A las 3 de agosto de 2009, 9:54

Hola, muy buenos artículos; lastima que no estén en ASP.NET

De todas formas me gustaría probar los ejemplos, pero no funciona el link de descarga de los ejemplos: http://www.gruasorion.com/sg/ejemplos/pasarela-de-pago-con-bbva/pasarela-de-pago-con-bbva.zip

 
Blogger Unknown ha dicho...
A las 21 de enero de 2010, 8:14

Muchas gracias por tu gran aporte, yo estoy implementando pero para otro banco y mi problema es que al enviar mediante post se queda esperando y al final dice que no se puede mostrar la página ¿que será? estoy probando en un servidor de pruebas que me dio el banco.

Podrías subir por favor el código, no funciona el link de descarga, muy agradecido.

Saludos.

 
Anonymous Anónimo ha dicho...
A las 22 de enero de 2010, 12:41

Comprobaciones es con B

 
Blogger goLaDeD ha dicho...
A las 21 de febrero de 2010, 15:16

Enhorabuena por el articulo. Es una gran guia para entender el funcionamiento, y no solo con el BBVA.

Gracias!

 
Anonymous Anothnio ha dicho...
A las 17 de abril de 2010, 18:02

Muy bueno el artículo, y mejor y ameno que la documentación de BBVA. De todas formas y como ya comentan, falla el link para descargar los documentos.

De todas formas gracias por el ejemplo :)

 
Blogger Banda Artistico-Cultural de Montaverner ha dicho...
A las 9 de septiembre de 2010, 14:05

Muchas gracias por tu articulo, estoy implementando una pasarela y me ha sido de gran ayuda.
Gracias!!!

 
Blogger Katan ha dicho...
A las 13 de octubre de 2010, 22:13

Gracias por los comentarios.

He estado buscando los ejemplos sin éxito, y debería realizarlos de nuevo :-( ... aunque el código que puse en los 3 artículos sale todo, pero los ejemplos facilitaban un poco la vida eso si :-D

Si alguien los tuviera antes de romperse el link ... pues sería de agradecer que me los facilitara, o bien si lo ha resuelto y tiene unos ejemplo que ayuden al resto, enviármelo a zicard[arroba]gmail.com

Aunque será algo difícil ... esperaré un poco, sino me pondré un día a realizarlo.

Gracias por la paciencia.

 
Blogger Daniel ha dicho...
A las 13 de abril de 2011, 12:10

fantástico, aún no lo he probado pero me pongo ahora mismo. Saludos y gracias.

 
Blogger Jesus ha dicho...
A las 12 de agosto de 2011, 18:43

el link no funciona para descargar todos los ficheros.¿podrias repararlo por favor?

gracias de antemano.

 
Blogger Jesus ha dicho...
A las 12 de agosto de 2011, 19:46

Hola, sigo empeñado en eso y la firma la estoy validando con la que da bbva y no coincide.

Creo que el problema está en desofuscar...ya que el trozo de código que indicas aquí no está completo. Lo he completado con lo que pone el ejemplo y finalmente haciendo que:
$pal_sec .= chr(bindec($xor[$i]));
$this->pal_sec=$pal_sec ;

De ese modo se recibe al llamar al metodo getDesofuscar();

De todas formas...no coincide la firma que genero con la que da el bbva. Por favor, me echais una mano? ¿qué puedo ir mirando?

 
Anonymous Anónimo ha dicho...
A las 29 de agosto de 2011, 14:03

gracias por tu aporte, pero alguien lo tiene para ASP.NET?
Ya les vale a los del put.. banco, 2011 y siguen sin tener el código para estos lenguajes

 
Blogger Roberto Santana ha dicho...
A las 7 de noviembre de 2011, 12:43

No va el enlace para descargar el código, ¿lo puedes revisar? Gracias!

 
Blogger david ha dicho...
A las 8 de noviembre de 2011, 16:41

Hola a todos me da un error en xml Error de lectura XML: mal formado cuando le pongo un header al codigo para ver los datos. Estoy loco buscando y no encuentro nada a ver si alguien ha tenido mi mismo problema. Gracias por todo.

 
Blogger david ha dicho...
A las 8 de noviembre de 2011, 16:42

Hola a todos me da un error en el xml, Error de lectura XML: mal formado cuando le pongo un header al codigo para ver el xml, he mirado por todos lados pero no encuentro la solucion a ver si a alguien le ha pasado lo mismo que a mi. Gracias

 
Anonymous Anónimo ha dicho...
A las 29 de diciembre de 2011, 18:50

Alguien podría subir los archivos php necesarios como si fuesen para tontos, completos de principio a fin?
Gracias.

 
Blogger stormbringer ha dicho...
A las 24 de agosto de 2012, 15:15

Gracias por la orientación, los ejemplos que da BBVA parece "roto"

Hay posibilidad de conseguir una copia de los ficheros de ejemplo

Un saludo.

 
Anonymous Javier ha dicho...
A las 16 de enero de 2013, 9:34

Hola,

Un gran artículo para complementar la penosa documentación del BBVA.

El enlace a los archivos está roto,¿habría manera de conseguir los ejemplos?

Gracias,
Un saludo


 
Blogger Katan ha dicho...
A las 16 de enero de 2013, 13:37

Buenas,

Como ya dije no dispongo de los ejemplo a pesar de haberlos buscado en un par de ocasiones.
El código es el mismo que los ejemplos, y esta troceado para ir explicando cada paso.

Leérlo detalladamente porque a pesar de no tener los ejemplos, y haber código algo confuso u obsoleto, se debería comprender su función.

El artículo es un guía nada más, y los ejemplo que hice no sustituyen a la documentación proporcionada por el BBVA.

Un saludo

 
Anonymous Anónimo ha dicho...
A las 2 de julio de 2013, 18:21

La documentación de BBVA es una auténtica MIERDA, pero hija mía, los métodos e instrucciones que das tampoco son una maravilla, te agradezco el aporte pero desde luego voy a hacerlo desde cero y paso de la documentación y de los artículos, eso de estar creando y borrando archivos, generando ID de transacción, no es profesional.

En una tienda online con ventas, lo haces todo con el ID de pedido, es decir con la BD, y no estar creando ficheros, que por casualidad el servidor se satura y pierdes datos, lo siento este artículo no vale nada, que pena porque se nota que te ha llevado tiempo.

Son las 18,20 horas del 2 de Julio de 2013, antes de que sea de noche lo tendré terminado y si quieres te paso el código en condiciones, ni usar POO ni hostias.

Un saludo.

 
Blogger Katan ha dicho...
A las 2 de julio de 2013, 18:43

Hola anónimo.
En ningún momento cree este artículo con intención de ser profesional. Solo a modo de guia.
algunos les sirve y a otros no.
Aunque no sirva de excusa este código lo hice hace casi 5 años y no tenia los conocimientos que tengo ahora.

Si quieres eres libre de publicar una solución diferente y seguramente mejor.

Saludos.

 
Anonymous Anónimo ha dicho...
A las 2 de julio de 2013, 19:38

Hola hija perdona, quizás también el cabreo con los capullos del BBVA lo pagué contigo, mi más sincera disculpa, ya lo tengo integrado.

Un saludo.

 
Blogger felipe ha dicho...
A las 17 de noviembre de 2013, 11:22

Alguien me puede ayudar a integrar el tpv en mi web?, pues sigo todo el hilo y no consigo resultados.
Un saludo

 

Añadir un comentario Publicar un comentario

Suscribirse a Enviar comentarios [Atom]

◄ Inicio