Módulo de pago TPV Caja Rural osCommerce

Bueno pués me ha tocado realizar una adaptación de un módulo para osCommerce y lo comparto.

==========================================================================
==========================================================================
Release Author: RobiHm
http://www.indomita.org

Cambios respecto al original:
*El modo de generación de firma digital se genera con sha1 en vez de con una .dll.
Así se evita el tener que compilar ni instalar ningún archivo que no sea imprescindible.

Released under the GNU General Public License

==========================================================================
==========================================================================

Descargar Módulo

Funcioncita para evitar datos de la caché en Ajax

Partiendo de que internet explorer guarda en caché las peticiones Ajax, es preferible añadir a cada petición un valor aleatorio. De este modo lograremos obtener un valor siempre válido y no proveniente de la cache.

ejemplo de función (min y max indica el intervalo de valores)

function semilla(min,max){
  num = max - min;
 aleat = Math.random() * num;
 aleat = Math.floor(aleat);

 return parseInt(min) + aleat;
}

ahora simplemente en las llamadas pedimos una semilla aleatoria

$("#prueba1").load("peticion_ajax.php?precio=chistorra&semilla="+semilla(1,1000));

Es un detalle que jode bastante puesto que te puedes volver loco buscando el porque en un navegador te da valores válidos y en otro no da valores actualizados xD

Quitándome el miedo con Ajax

Bueno hasta el no había necesitado utilizar Ajax(Asynchronous JavaScript And XML) para nada. Tenía una especie de sensación que me recorría todo el cuerpo cada vez que pensaba en ponerme con ello y siempre acababa dejándolo para otro día.

Hoy por fin, por mis santos co***** me ha apetecido y me he puesto al lío.

He comenzado buscando información acerca de su estructura y funcionamiento, algo que ya me había mirado con anterioridad.
Y he generado un código, por lo que he visto, bastante estandar.

Vamos a validar un nick de usuario, que no se encuentre repetido.
El ejemplo es bastante simple, consta de un formulario con un campo de texto que pide un nick y un botón que al pulsarlo comprueba si se encuentra en uso.

Comenzamos creando el objeto necesario para tal menester.
Para ello vamos a desarrollar una función.

/* función constructora del objeto request */
function createXMLHttpRequest() {
if (window.ActiveXObject) {
/* si es interné explóre  */
return new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
/* si no es interné explóre  */
return new XMLHttpRequest();
}else{
/* mal comenzamos si no permite crear el objeto */
alert('No existe la posibilidad de AJAX');
}
}

/* generamos la variable por eso de tenerla global */
var xmlHttp;

/* creamos la función que coprobará si el nick ya existe o no */
function comprueba_nick(){
/* generamosla dirección a la que vamos a enviar los datos a validar, en este caso es la página validar.php en la que deberemos tener un código que nos devuelva si es un nick que se encuentra en uso o no. Por ejemplo: 'Nick Disponible' / 'Nick en Uso' */
var nick=document.getElementById("nick").value;
var url = "validar.php?nick="+ escape(nick);
/* creamos el objeto desde nuestra función constructora */
xmlHttp = createXMLHttpRequest();
/* enviamos una llamada a la dirección y le especificamos que queremos la respuesta por GET, también admite por POST y PUT */
xmlHttp.open("GET", url);
/* comprobamos que nos llegan los datos para ello, hemos creado una función llamada Completado */
xmlHttp.onreadystatechange = Completado;
/* ya hemos terminado, cerramos el chiringuito */
xmlHttp.send(null);
}

/* la función Completado nos informa cuando el estado de la petición se ha completado. Existen cinco estados posibles: 0 (sin iniciar), 1 (cargando), 2 (cargado), 3 (en proceso), 4 (completado)</span>
function Completado() {
if (xmlHttp.readyState == 4) {
/* se ha completado, ahora vamos a informar de que el nick es correcto, cargamos un div mismamente con la respuesta, o podemos sustituir el código por un     alert(xmlHttp.responseText); */
document.getElementById("cnick").innerHTML=xmlHttp.responseText;
}
}

El formulario html sería algo así:

Validando un Nick con AJAX:

<input id="nick" name="nick" type="text">
<div name="cnick"</div>;

<input id="manolete" value="Comprobar" onclick="comprueba_nick();" type="button">

El archivo PHP, no lo pongo, pero debería de tener una estructura, para este caso, similar a esto:
1º – comprobamos que piden datos
2º – conectamos a la base de datos
3º – buscamos un nick que sea igual
4º – si lo encontramos respondemos con un echo mismamente ‘Nick en Uso’
5º – si no lo encontramos respondemos ‘Nick Disponible’

Resumiendo quedaría algo así :

<script language="JavaScript" type="text/javascript">
function createXMLHttpRequest() {
if (window.ActiveXObject) {
return new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
return new XMLHttpRequest();
}else{
alert('No existe la posibilidad de AJAX');
}
}

var xmlHttp;

function comprueba_nick(){
var nick=document.getElementById("nick").value;
var url = "validar.php?nick="+ escape(nick);

xmlHttp = createXMLHttpRequest();

xmlHttp.open("GET", url);

xmlHttp.onreadystatechange = Completado;
xmlHttp.send(null);
}

function Completado() {
if (xmlHttp.readyState == 4) {
document.getElementById("cnick").innerHTML=xmlHttp.responseText;
}
}
</script>

Validando un Nick con AJAX:

<input id="nick" name="nick" type="text">
<div name="cnick"</div>

<input id="manolete" value="Comprobar" onclick="comprueba_nick();" type="button">

Ahora que sabes todo esto intenta crear una llamada en ajax para insertar el usuario sin recargar la página 😀

Bueno, tras aprender los conceptos básicos del tema (aprendo muy deprisa), me he metido con una librería enfocada a multitud de tareas con javascript, la librería se llama Jquery y no tiene mala pinta. También he oído que existe un tal Prototype, pero de momento me quedo con esta.
Tras leer la Documentación Ajax Jquery he creado un código bastante simple para la parráfada anterior, mejor que lo comprobéis con vuestros propios ojos … xD
Si no tienes mucha idea hay que tomarse el asunto con mucha calma.

/* te descargas la librería, la posicionas donde te salga, yo la he puesto en la misma ubicación del documento. Para incorporala a la página con la siguiente instrucción es más que suficiente (recuerda adaptar la ruta a donde la posiciones) */
<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript">
/* no voy a explicar gran cosa ya que en la documentación viene bastante clarito, lástima del inglés xD
El símbolo "$" indica la creación de eventos y demás para dicha librería. La nomenclatura utilizada es muy parecida a la de CSS. El método ".bind" nos permite crear un evento al objeto al que hacemos mención, en este caso, al botón "manolete". Indicamos en que tipo de evento va a saltar, "Click" y que queremos hacer, yo he creado la función directamente ahí :D
lo que hace esa función/bloque_de_acciones es cargar mediante el evento ".load" una página, la librería se encarga de crear el objeto y recibir los datos ... etc ... etc ... Por lo que cuando se clicke en el botón html tendremos el mismo resultado que antes, se cargará en la capa cnick la respuesta obtenida*/

$("#manolete").bind('click', function(){ $("#cnick").load("validar.php?nick="+$("#nick").attr("value"))});
</script>

En fin quedaría algo así, fíjate bien ya que he quitado el onClick del botón «manolete», puesto que lo hacemos desde Jquery.

//Validar un usuario con Ajax#
<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript">
$("#manolete").bind('click', function(){ $("#cnick").load("validar.php?nick="+$("#nick").attr("value"))});
</script>

Validando un Nick con AJAX:

<input id="nick" name="nick" type="text">
<div name="cnick">

<input id="manolete" value="Comprobar" type="button">
</div>

Netdisaster ¡¡Desata tu furia!!

Bueno, hoy me he encontraco con una página web que te permite desatar toda tu furia en cualquier página web online.
Tienes un montón de opciones, disparar con un rifle, lanzar meteoritos, inundarla, un ataque kamikace de zepelines, una invasión alienígena y un largo etcétera. En fin que tienes un montón de opciones/plagas para lanzar sobre esa web que tanto odias xD

www.netdisaster.com

Yo la estoy probando con el cuadro de honor de evobas y estos son algunos de sus resultados xD




Generador automático de avisos de pagos con Paypal

Bueno, hoy voy a explicar el proceso para saber cuando se ha hecho un pago en tu cuenta de PayPal.
Me podrías decir, pués es simple, te avisan por correo.
Pero la cuestión es la siguiente, automatizar el recibo de los avisos para poder generar un informe personalizado o desencadenar acciones en nuestra base de datos y un largo etcétera, las posibilidades son bastante amplias.


Bueno lo primero que tenemos que hacer, es tener una cuenta en PayPal.
Para activar la Notificación de pago instantánea debes acceder a tu cuenta.
Desde el apartado Mi Cuenta accedes al Perfil y bajo la cabecera Preferencias de venta clicamos en Preferencias de Notificación de pago instantánea.
Una vez dentro pulsamos en editar.
Activamos la casilla y asignamos una url, que será el destino de los avisos.

Decir que también se puede activar la Notificación de pago instantánea agregando el campo notify_url=dirección url, pero no creo que sea muy recomendable hacerlo (a menos que tengas varias cuentas de correo asignadas en tu cuenta paypal) puesto que es un campo que puede ser visionado por cualquiera.

Una vez hemos activado el IPN, generamos los botones de donación/pago y los agregamos a nuestro sitio web.

Terminada la «infraestructura de venta visual» solamente nos falta el archivo que hará de receptor de los avisos.

Decir que en la web de PayPal ponen a disposición una serie de scripts de ejemplo para los siguientes lenguajes :
ASP.Net/C#
ASP.Net/VB
ASP/VBScript
Cold Fusion
Java/JSP
PERL
PHP
(Hace falta estar logeado en paypal para poder verlos, para la documentación también)
La documentación así la podéis encontrar en

Si no podéis acceder os la subo a algún sitio.

Yo voy a proseguir a partir del ejemplo de PHP 4.1 que ofrecen en la web, voy a comentarlo y
luego pondré un ejemplo con una base de datos y que cada cual lo adapte a sus necesidades.
Decir que pese a mi esfuerzo por guardar márgenes y demás en los ejemplos para un mejor visionado el muy perrete se ha comido las tabulaciones al publicar el tutorial ¬¬’

PHP 4.1

//comentado por Roberto Herrero para su blog http://blog-indomita.blogspot.com/
// read the post from PayPal system and add 'cmd'
//es la variable señuelo que hay que devolver a PayPal para que compruebe su veracidad
$req = 'cmd=_notify-validate';

//recogemos todos los datos POST que nos envía paypal y los guardamos en el mismo orden
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}

// post back to PayPal system to validate
//creamos la petición de llamada para avisar de la recepción
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
//en la documentación dice que pongas www.paypal.es pero supongo que es un errata, con .es no funciona así que deja el .com
$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);

// assign posted variables to local variables
//tomamos los valores del POST anterior
//nombre que hemos asignado al crear el botón
$item_name = $_POST['item_name'];
//número personal que hemos asignado al crear el botón
$item_number = $_POST['item_number'];
//estado del pago la buena noticia es que este en Completed xD
$payment_status = $_POST['payment_status'];
//ojo aquí que el payment_amount es el mc_gross no liarse que al mirar la documentación puede dar a confusiones
//la cantidad del ingreso
$payment_amount = $_POST['mc_gross'];
//igual que antes ojito xD
//la moneda del ingreso
$payment_currency = $_POST['mc_currency'];
//identificador alfanumérico que PayPal le asigna
$txn_id = $_POST['txn_id'];
//cuenta a la que se ha ingresado
$receiver_email = $_POST['receiver_email'];
//emisor del pago
$payer_email = $_POST['payer_email'];

if (!$fp) {
// HTTP ERROR
//no hemos podido conectar con paypal
} else {
//tenemos respuesta de paypal
fputs ($fp, $header . $req);
//leémos los datos recibidos, realmente estamos buscando si los datos que hemos recogido con anterioridad son válidos, ya que algún graciosillo/lamercillo puede estar intentando tomarnos el pelo
while (!feof($fp)) {
$res = fgets ($fp, 1024);
//comprobación verificada
if (strcmp ($res, "VERIFIED") == 0) {
// check the payment_status is Completed
//ahora tenemos que comprobar que el payment_status es Completed, en la documentación pone Completado pero ni caso xD
// check that txn_id has not been previously processed
//comprobamos en nuestra base de datos que no sea una transacción ya procesada, ¿por qué? para evitar malas intenciones de terceros no tiene sentido procesar algo que ya hemos procesado
// check that receiver_email is your Primary PayPal email
//comprobamos si el email de la transacción es el nuestro
// check that payment_amount/payment_currency are correct
//comprobamos que tanto la cantidad como la moneda son las correctas
// process payment
//si todo ha ido bien ya podemos procesar el pago/donación

//la comprobación es incorrecta
}else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
//aquí puedes guardar un log para ir investigando quién esta jugando con tu sistema
}
}
//cerramos
fclose ($fp);
}

Ejemplo con base de datos MySql
Requisitos previos:
* Conocer como crear el conector $link.
Te dejo el tutorial por si te interesa: Tutorial Conexión a MySql desde PHP
* La base de datos debe tener tantos campos como parametros desees guardar, en este caso guardaremos la fecha(datetime) y una cadena(varchar o text) con todos los parámetros, amén del identificador(varchar) único que otorga PayPal para la transacción

//comentado por Roberto Herrero para su blog https://www.miblog.indomita.org/
//recomiendo añadir un parámetro personal a la url que utilizaremos para gestionar los pagos, nuestro señuelo y si esta encriptado pos mejor
//con comprobar si la variable ha sido enviada creo que es suficiente,
pero si eres un paranoico y no te fias de los mamones que navegan por la red
puedes añadir && $_GET["antimamones"]==md5("nointentesjodermequetevasaenterar")
//el parámetro que pases por GET recuerda que tienes que agregarlo en el perfil de url IPN del comienzo de este manual.
if(isset($_GET["antimamones"]))
nuevo_ingreso();


    function nuevo_ingreso(){
      //esto ya lo he comentado antes así que paso xD
      // read the post from PayPal system and add 'cmd'
      $req = 'cmd=_notify-validate';

      foreach ($_POST as $key => $value) {
        $value = urlencode(stripslashes($value));
        $req .= "&$key=$value";
      }

    // post back to PayPal system to validate
    $header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
    $fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);

    // assign posted variables to local variables
    $item_name = $_POST['item_name'];
    $item_number = $_POST['item_number'];
    $payment_status = $_POST['payment_status'];
    $payment_amount = $_POST['mc_gross'];
    $payment_currency = $_POST['mc_currency'];
    $txn_id = $_POST['txn_id'];
    $receiver_email = $_POST['receiver_email'];
    $payer_email = $_POST['payer_email'];
    //podemos añadir el campo custom en nuestros botones (en el código una vez generados) con la información que deseemos, esto viene bien para identificar al usuario
    $custom = $_POST['custom'];


    //creamos una conexión a nuestra base de datos
    conecta($link); 
    if (!$fp) {
      // HTTP ERROR
    } else {
      //recorremos respuesta      
      fputs ($fp, $header . $req);

      while (!feof($fp)) {
        $res = fgets ($fp, 1024);
        if (strcmp ($res, "VERIFIED") == 0) {
       // check the payment_status is Completed
       if(strcmp ($payment_status, "Completed")==0){
         //la transacción esta completada
        // check that txn_id has not been previously processed

       //la función es_idenfiticador_alfanumerico comprueba que no inyecten sentencias SQL (usease antimamoneo) esta definida al final
       if(es_identificador_alfanumerico($txn_id)) {
         //comprobamos si no la hemos procesado ya
        $r=mysql_query("select count(txn_id) as repes from ingresos_recibidos where txn_id='".$txn_id."'",$link);
        $f=mysql_fetch_array($r);
        $repetida=(int)$f["repes"];
       }else{ $repetida=1;}
      //en el caso de que no sea válido tendremos valor 1
      if($repetida==0){
      //no es una transacción repetida
      // check that receiver_email is your Primary PayPal email
      if(strcmp ($receiver_email, "micorreo@loquesea.com")==0){
      //es un correo válido
      // check that payment_amount/payment_currency are correct
      //ahora pasamos a comprobar que tipo de pago ha realizado tenemos que anidar los diferentes casos de alguna manera, en el ejemplo lo he hecho con 2 casos
     //obtengo el usuario que ha hecho el ingreso del campo custom
     //supongo que tendrás tu propia clase de acceso y recogida de datos, si no la tienes lo que pongo es funcional
     $r=mysql_query("select usuario from tabla_usuarios where usuario=".((int)$custom), $link);
     $f=mysql_fetch_array($r);
     $usuario=(int)$f["usuario"];
     //caso 1: el pago es de 1.99 la moneda el EURO que son los valores que debería de tener el elemento identificador personal 1
     if($payment_amount==1.99&&strcmp($payment_currency, "EUR")==0
    &&$item_number==1&&$usuario>0){
    //aquí van las operaciones de guardado de informe y las que desencadenas al recibir un pago
    //ingresamos los datos
    $cadena=sprintf("guardando informe
    item_name=%s

    item_number=%s

    custom=%s

    payment_status=%s

    mc_gross=%s

    mc_currency=%s

    txn_id=%s

    receiver_email=%s

    payer_email=%s",
    $item_name,$item_number,$custom,$payment_status,$payment_amount,$payment_currency,$txn_id,$receiver_email,$payer_email);
    mysql_query("insert into ingresos_recibidos (cadena,txn_id,fecha) values ('".$cadena."'
    ,'".$txn_id."','".date("Y-m-d H:i:s")."')",$c);
    //actualizamos las gracias que le damos al usuario
    (mil gracias)

    mysql_query("update tabla_usuarios set gracias=gracias+1000 where usuario=".$usuario,$link);
    //caso 2: el pago es de 6.99 la moneda el EURO que son los valores que debería de tener el elemento identificador personal 2
    }elseif($payment_amount==6.99&&strcmp($payment_currency, "EUR")==0
    &&$item_number==2&&$usuario>0){
    //aquí van las operaciones de guardado de informe y las que desencadenas al recibir un pago
    //lo mismo que antes aunque yo recomiendo hacerse una funcioncita o algo que evite repetir código
    //actualizamos las gracias que le damos al usuario
    (un millón de gracias)
    mysql_query("update tabla_usuarios set gracias=gracias+1000000 where usuario=".$usuario,$link);
    }
    }
    }
    }
    }elseif (strcmp ($res, "INVALID") == 0) {
    // log for manual investigation
    }
    }
    fclose ($fp);
    }
    }

    //funcion antimamoneo, realmente no se el rango total de caractéres raros que envia paypal así que habría que adaptarla xD
    function es_identificador_alfanumerico($cadena){
      return ereg("^[A-Za-z0-9_]*$",$cadena);
    }

No he probado el código y seguramente tenga algun pequeño fallo (falta de paréntesis, ; o alguna } )

Reitero de que es un código orientativo y que cada uno tiene que adaptarlo y optimizarlo.
Paypal ofrece otros métodos IPN que incluyen conexión https y demás en el servidor.
El código del ejemplo no necesita una conexión segura para llevarse a cabo.
Si alguien ve algun agujero de seguridad le agradecería que me lo comunicase para modificarlo.

Anexo: Realizar pruebas de pagos con PayPal

Espero que a alguien le sirva de ayuda.