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 únicaPor ú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: bbva, pasarela de pago, php, proyecto, tpv