¡Ya estamos en Twitter!

Perl en Español

  1. Home
  2. Tutoriales
  3. Foro
  4. Artículos
  5. Donativos
  6. Publicidad

Vender suscripciones con PayPal

por Uriel Lizama

Así que tienes tu sitio y ahora quieres empezar a vender membresías, con este tutorial vamos a ver como puedes vender membresías de forma sencilla por medio de PayPal. Verás que puedes configurar en menos de 1 hora tu sitio para empezar a vender tus membresías.

Así que empecemos.

Tu cuenta en PayPal

Como es lógico lo primero que tienes que hacer es crear tu cuenta en PayPal, es realmente muy sencillo y puedes crear tu cuenta cliqueando aquí.

Una vez que hayas creado tu cuenta de PayPal ahora podemos empezar a configurar todo.

Creando tu área de miembros

Normalmente cuando vendemos membresías a nuestro sitio lo que hacemos es crear un área especial en donde solamente pueden entrar miembros.

Para poder accesar, nuestros miembros deben de usar una clave y contraseña la cual les es proporcionada una vez que hayan hecho el pago de su membresía.

Lee el siguiente tutorial para que configures tu directorio protegido.

Una vez que ya hayas probado que tu directorio protegido funciona de manera correcta, ahora es momento configurar nuestra interfase con PayPal.

Usando el IPN de PayPal

Vender membresías con PayPal es sumamente sencillo, todo lo que tenemos que hacer es crear una vía de comunicación entre PayPal y nuestro servidor. PayPal se encarga de dar de alta y de baja a nuestro usuarios dependiendo del término de sus membresías, por lo que una vez que hayamos configurado todo ya no nos tenemos que preocupar.

La forma en que PayPal se comunica con nuestro servidor para avísarnos que hay que dar de alta o de baja un usuario es por medio de una tecnología que se llama IPN.

El IPN o Instant Paymen Notification de PayPal es el medio por el cual PayPal se comunicará de manera automática e instantenéa con tu sitio para notificar de cambios. A continuación podrás ver un gráfico que te muestra como funciona esto.

¿Cómo funciona IPN?
¿Cómo funciona el IPN?

1. El pago de un usuario o un refund activa el IPN.

2. PayPal hace un post de con campos de un FORM HTML al URL que pongas en tu perfil. Este POST es el corazón de IPN. Dentro de la notificación vienen incluidas la información de pago de tu cliente (como nombre del cliente y monto de pago).

3. Tu servidor debe entonces validar la notificación para asegurarse que es legítima.

Para configurar el URL de nuestro IPN debemos de accesar a nuestra cuenta de PayPal.

Ahí nos debemos de dirigir a Profile, ahí bajo Selling Preferences encontraremos una liga que dice Instant Payment Notification Preferences.

Muy bien, ahí debemos de ingresar el URL de nuestro CGI de IPN y activarlo. Dependiendo del lugar en donde quieras poner tu CGI es el URL que debes de poner, por ejemplo: "http://www.misitio.com/cgi-bin/paypal.cgi"

Configurando Nuestro IPN

Creando nuestro programa de IPN

Ahora vamos a la parte más delicada de todo el proceso, que es la creación de nuestro programa de IPN. Este programa es el CGI que se va a encargar de realizar la comunicación de PayPal y de llevar a cabo las tareas de alta y de baja de nuestros miembros.

Recuerda que este CGI es el cual diste de alta en tu Perfil de PayPal, así que recuerda guardarlo en el mismo URL.

Nuestro CGI consta de varias partes, primero recibimos los datos, luego verificamos que los datos realmente provengan de PayPal y finalmente decidimos que hacer de acuerdo con los datos que PayPal nos ha envíado.

Como siempre, primero empezamos con la cabeza de nuestro CGI, usando el pragma strict y cargando los módulos necesarios.

Recuerda cambiar la primera línea del script #!/usr/bin/perl con el path de Perl en tu servidor.

#!/usr/bin/perl

use strict;

# Cargamos los Módulos
use CGI::Carp;
use CGI qw(:standard escapeHTML);
use LWP::UserAgent;


# CGI.pm versioning issue
$CGI::USE_PARAM_SEMICOLONS = 0;
Después lo que vamos a hacer es configurar nuestras variables globales, estás las debes de customizar de acuerdo a tu información personal:
# URL a PayPal
my $PAYPAL_URL = 'http://www.paypal.com/cgi-bin/webscr';

#Archivo .htpasswd
my $HTPASSWD = '/home/ruta/a/mi/.htpasswd';

# Dirección de nuestro sendmail
my $SENDMAIL_PATH = '/usr/sbin/sendmail';

# Dirección donde queremos las notificaciones de error
my $ADMIN_EMAIL = 'notificaciones@misitio.com';

# Nuestro Email de la cuenta de PayPal
my @PAYMENT_EMAILS = ('pagos@misitio.com');
# -------------------------------------------------------------------
Veamos con cuidado cada una de las variables.

$PAYPAL_URL Ésta variable no la debes de modificar, a menos que estes bajo un servidor https, si ese es el caso entonces debes de conectarte a PayPal por medio de https (https://www.paypal.com/cgi-bin/webscr).

$HTPASSWD Aquí debes de poner la ruta completa a tu archivo .htpasswd, recuerda que lo debes de tener configurado. Para más detalles lee este tutorial.

$SENDMAIL_PATH El CGI va a estar configurado para mandarte mails de cualquier error que pueda haber durante el proceso. Aquí debes de poner la ruta al sendmail de tu servidor, para más detalles checa este tutorial.

$ADMIN_EMAIL Email donde quieres recibir las notificaciones de errores que pudieron haber surgido en el CGI. Es muy imporante que pongas un mail que cheques constantemente, pues de está manera podrás revisar en caso de cualquier error.

@PAYMENT_EMAILS Cuando activas tu cuenta en PayPal tu cuenta es el email que registraste, aquí lo debes de poner. Es muy importante que lo pongas bien, de lo contrario todas las transacciones serán rechazadas.

A continuación se muestra en detalle lo que hace cada subrutina de nuestro script de IPN. Si deseeas puedes optar por descargar el script y seguir con la siguiente sección, de lo contrario continúa leyendo.

Explorando dentro del script de IPN

Después de definir todas las variables, iniciamos el proceso llamando a nuestra subrutina principal main().

sub main {

        # Verificamos el IPN
        if (verificar_ipn()) {
           # Ha sido verificado vamos a procesarlo
           procesar_ipn();
        }

        # IPN was successfully processed
        respuesta(1);
}
La subrutina principal es muy sencilla, lo primero que hacemos es verificar el IPN, si es correcto entonces los procesamos llamando a la subrutina procesar_ipn(), finalmente damos la respuesta.

La verificación del IPN es sumamente importante, pues nos permite comprobar que PayPal fue el que realmente mandó la notificación. Si no tuvieramos la comprobación, cualquiera podría mandar notificaciones y dar de alta membresías no permitidas.

sub verificar_ipn {

    # Nuevo UserAgent
    my $ua = new LWP::UserAgent;

    # Creamos el request
    my $req = new HTTP::Request("POST", $PAYPAL_URL);
       $req->content_type("application/x-www-form-urlencoded");
       $req->content(query_string() . "&cmd=_notify-validate");

    # Recibimos la respuesta
    my $resp = $ua->request($req);

    # Checamos si fue exitosa la respuesta
    if (($resp->is_success) && ($resp->content eq "VERIFIED")) {
        return 1;
    } else {
        # Intentamos identificar el error
        if (($resp->is_success) && ($resp->content eq "INVALID")) {
            procesar_error("La notificación recibida no fue de PayPal ".
             "- El mensaje fue ignorado ", "verificación de IPN", 0, 0);
        } else {
            procesar_error("La notificación no pudo ser procesada",
             "verificación de IPN", 1, 0);
        }

        return undef;
    }

}
Lo que estamos haciendo es mandando a PayPal todos los campos recibidos incluyendo el campo cmd=_notify-validate que le indica a PayPal que estamos pidiendo una verificación. Entonces PayPal nos va a regresar una respuesta, ya sea VERIFIED indicando que la notificación si fue envíada por ellos o INVALID indicando que la notificación no salió de PayPal.

Si la respuesta es correcta entonces seguimos con el proceso, de lo contrario llamamos a nuestra subrutina procesar_error(); que es la encargada de manejar todos los errores que surjan durante el proceso.

Seguimos con el procesamiento de nuestro IPN por medio de la subrutina procesar_ipn():

sub procesar_ipn {

        # Nueva membresía
        if ((param("txn_type") eq "subscr_signup") && (verificar_cuenta())) {
                # agregamos el usuario
                activar_usuario(param("username"),param("password"));
        } elsif (param("txn_type") eq "subscr_eot") {
                # eliminamos al usuario
                desactivar_usuario(param("username"));
        } else {
                # ignoramos el mensaje
        }

}
La subrutina se encarga de ver que es lo que PayPal está notificando. Para hacer esto checa el campo txn_type. En este caso si el campo tiene un valor de subscr_signup entonces significa que PayPal está notificando una nueva membresía, en cuyo caso activamos al usuario llamando a la subrutina activar_usuario(), al llamar la subrutina manda dos variables como argumentos el username y el password, los cuales fueron proporcionados por medio de PayPal de manera automática y que el usuario usará para ingresar a la página protegida.

En caso de que el campo tenga un valor de subscr_eot, entonces PayPal nos está notificando que la cuenta debe de ser desactivada, por lo que llamamos a la subrutina desactivar_usuario(), y envíamos a la subrutina el campo username como argumento.

Si te das cuenta cuando vamos a dar de alta un usuario antes pedimos verificar la cuenta, esto lo puedes ver en la siguiente línea:

if ((param("txn_type") eq "subscr_signup") && (verificar_cuenta())) {
La subrutina verificar_cuenta() se encarga de verificar si el pago fue hecho a una de tus cuentas, recuerda que esto lo configuras en la variable @PAYMENT_EMAILS. Esto es muy importante para evitar dar de alta membresías que no te corresponden, aunque esto es muy poco probable, vale la pena comprobarlo:
sub verificar_cuenta {

    # validamos que el pago se haya realizado a nuestra cuenta
    my $valid = undef;

    for my $pay_mail (@PAYMENT_EMAILS) {
        if (param("receiver_email") eq $pay_mail) {
            $valid = 1;
            last;
        }
    }

    if (!$valid){
        procesar_error("Se recibió un IPN de una cuenta que no es tuya ".
                        "- El mensaje fue ignorado.", "validación de email ", 0, 0);
        return undef;
    }

return 1;

}
Ahora vamos a checar nuestras subrutinas encargadas de dar de alta y de baja a nuestros usuarios. En caso de que hayas leído el tutorial acerca de como proteger directorios con contraseña esto te será muy conocido.

Primero demos un vistazo a la subrutina encargada de dar de alta a los usuarios:

sub activar_usuario{

my $user = shift;
my $pass = shift;


#Verificamos que el usuario no exista ya
my $existe = undef;

open my $in, '<', $HTPASSWD
   or procesar_error("No se pudo abrir archivo .htpasswd - $!",
                       "abrir htpasswd", 0, 1);
    while(my $line = <$in>){
        my ($auser,$apass) = split(/\:/, $line);
            if($auser eq $user){
                $existe = 1;
                last;
            }
     }
close $in;


if($existe){
    # El usuario si existe, mandamos error
    procesar_error("El usuario $user ya existe ".
      "- No se ha hecho ninguna acción", "crear usuario", 0, 1);
    return 1;
}else{
    # Introducimos el usuario a nuestro .htpasswd
    open my $out, '>>', $HTPASSWD
        or procesar_error("No se pudo abrir archivo .htpasswd - $!",
                           "abrir htpasswd", 0, 1);
        print {$out} $user . ":" . $pass;
        print {$out} "\n";
    close $out;

    return 1;
}


}
Es sumamente sencillo lo que hacemos, primero recibimos el username y pass envíados al llamar la subrutina. Después de ello abrimos nuestro .htpasswd y checamos si no existe ya el username que queremos dar de alta, en caso de que exista notificamos acerca de esto y ya no realizamos ninguna acción.

Si el usuario aún no existe, entonces introducimos el nuevo usuario y contraseña dentro de nuestro .htpasswd, ya a partir de ese momento podrá tener nuestro usuario acceso a la página protegida.

Como podrás ver no hay necesidad de encriptar la contraseña, pues PayPal ya nos manda la contraseña encriptada.

Seguimos ahora con la subrutina encargada de desactivar la cuenta:

sub desactivar_usuario{

my $user = shift;


open my $in, '<', $HTPASSWD
   or procesar_error("No se pudo abrir archivo .htpasswd - $!",
                      "abrir htpasswd", 0, 1);
my @usuarios = <$in>;
close $in;

    # Introducimos el usuario a nuestro .htpasswd
    open my $out, '>', $HTPASSWD
          or procesar_error("No se pudo abrir archivo .htpasswd - $!",
                             "abrir htpasswd", 0, 1);
        for my $linea_usuario(@usuarios){
            chomp($linea_usuario);

            my($auser,$apass) = split(/\:/, $linea_usuario);

            if($auser ne $user){
                print {$out} $linea_usuario, "\n";
            }
        }

    close $out;


return 1;

}
De igual manera recibimos el username que queremos eliminar como argumento. Abrimos el archivo .htpasswd y guardamos todo el contenido en el array @usuarios, después volvemos a abrir nuestro archivo pero ahora vamos a sustituir todo el contenido. Hacemos una iteración a través de nuestro array y escribimos en el archivo todas las líneas excepto aquellas donde el username sea el que queremos eliminar.

Listo, a partir de ese momento el usuario ya no podrá tener acceso a nuestra página protegida.

Ahora vamos a crear la subrutina encargada de hacer las notificaciones de error:

sub procesar_error {
        # sends notification that an error has occured
        my $err_str = shift;
        my $action = shift;
        my $kill = shift;
        my $req_action = shift;

     my $message = "El siguiente error fue encontrado al intentar hacer la $action:";
        $message .= "\n\t$err_str\n\n\n";
        $message .= "Información del Usuario\n";
        $message .= "\tUsername: " . param("username") . "\n";
        $message .= "\tEmail: " . param("payer_email") . "\n";
        $message .= "\tNúmero de Suscripción: " . param("subscr_id") . "\n";
        $message .= "\tTipo de Transacción: " . param("txn_type") . "\n";

        my $subject = "Error de Suscripción";
        if ($req_action) {
                $subject .= " - Necesita una acción";
        } else {
                $subject .= " - No se necesita realizar nada";
        }

        # if an email is not specified write to error_log only
        if (($ADMIN_EMAIL) && ($SENDMAIL_PATH)) {

            if (!open(SENDMAIL, "|$SENDMAIL_PATH -t")) {
                    carp "Unable to open sendmail pipe.";
            }

            print SENDMAIL "To: $ADMIN_EMAIL\n";
            print SENDMAIL "From: $ADMIN_EMAIL\n";
            print SENDMAIL "Subject: $subject\n";
            print SENDMAIL "Content-type: text/plain\n\n";
            print SENDMAIL "$message";

            if (!close(SENDMAIL)) {
                carp "Unable to close sendmail pipe.";
            }

        }

        # put it into the error log
        if ($kill) {
                # IPN will retry
                respuesta(0);
                croak $message;
        } else {
                carp $message;
        }
}
La subrutina actúa de acuerdo a los siguientes argumentos recibidos:
        my $err_str = shift;
        my $action = shift;
        my $kill = shift;
        my $req_action = shift;
El primero es la descripción del error, sigue la acción en la cual se encontró el error, después viene $kill que indica si debe de terminarse en ese momento el CGI y después viene si se necesita alguna acción de parte de uno.

Se crea un reporte y se manda a la dirección de correo configurada al principio del CGI.

Finalmente viene la subrutina que va a dar la respuesta final de nuestro script, al cual llamamos respuesta():

sub respuesta {
        # handle the http reponse
        my $is_success = shift;

        if ($is_success) {
                print header(-status=>('204 No Content'));
        }
        else {
                print header(-status=>('500 Internal Server Error'));
        }
}
Si vemos, en caso de que haya algún error el script va a regresar un error 500 usando el http-status, de lo contrario un código 204.

Esto es muy importante, pues PayPal seguirá mandando la notificación hasta que el script regrese un código exitoso.

Eso es todo, nuestro script está completo y listo para utilizarse, ahora todo lo que nos falta es crear el botón de compra.

Crear nuestro botón

Con nuestro script de IPN ya tenemos todo listo para empezar a vender nuestras membresías. Ahora necesitamos hacer el botón de compra. El botón de compra de PayPal es más bien un formulario por medio del cual llamamos a PayPal con todos los argumentos que definen el tipo de compra que van a realizar nuestros usuarios.

Hacer los botones es sumamente sencillo usando la fábrica de botones de PayPal.

Lo que vamos a hacer es entrar a nuestra cuenta de PayPal y ahí en la barra de botones de arriba vamos a cliquear sobre Merchant Tools.

Ahí debes de buscar la liga que dice Subscriptions & Recurring Payments bajo el apartado Key Features. Se te va a mostrar la fábrica de botones.

Ahí podemos poner todas las características de nuestra membresía, el nombre, la descripción, el término de la misma, ya sea en días, meses o años, etc.

Es muy importante que al crear tu botón cheques que tengas activada la opción Subscriptions Password Management para que PayPal se encargue de la administración de tus usuarios.

Activar Administración de Usuarios

Una vez que hayas creado el botón copia el código que fue generado de manera automática y ponlo en tu página de suscripción.

Haciendo Pruebas

Es muy sencillo realizar pruebas para ver que todo funcione de manera correcta.

Lo que puedes hacer es crear un membresía que cueste $0.01 y que tenga una duración de un día. Entonces realiza la compra de la membresía como si fueras un cliente. Esto te va a servir para checar que los usuarios se están dando de alta automáticamente y aparte al día siguiente podrás ver si realmente se dió de baja el usuario.

Ya después de hacer las pruebas necesarias y estar seguro que todo funciona bien, entonces ya puedes poner a la venta tus membresías.

Acerca del Script

El script que uso en el tutorial es el mismo que uso para mis sitios en los que vendo membresías. Aunque sé que funciona y de hecho lo tengo funcionando en varios sitios en este momento, NO puedo dar ninguna garantía de esto. Es necesario que hagas las pruebas necesarias para que no tengas ningún problema.

Puedes descargar el script completo cliqueando aquí.

Al script se le pueden hacer varios cambios, por ejemplo se puede subir la seguridad verificando el tiempo de membresía, el costo de la membresía, etc. Aparte si deseas poner más funciones que realizar dependiendo del tipo de notificación, también lo puedes realizar de manera sencilla.

Más Información

PDF: Manual de "Suscripciones y Pagos Recurrentes" de PayPal
https://www.paypal.com/en_US/pdf/subscriptions.pdf

PDF: Manual de "Order Management Integration Guide" de PayPal
https://www.paypal.com/en_US/pdf/PP_OrderManagement_IntegrationGuide.pdf

¿Necesitas ayuda para vender con PayPal?

Ofrecemos un servicio de ayuda en el cual configuramos lo necesario en tu sitio web para que empieces a vender por medio de PayPal, ya sean productos o membresías. Puedes preguntar por nuestras tarifas en: phelpers@baboonsoftware.com

¿Quiéres más tutoriales como este? Escribir tutoriales toma una gran cantidad de tiempo y esfuerzo. Si este tutorial te ayudó a aprender o a solucionar algo, por favor considera dejar alguna donación en apoyo a Perl en Español.

Cliquea en el botón de abajo para dejar tu donación por medio de PayPal.

Comparte:
Categorías de Tutoriales:
En Nuestros Foros:
Socializa:
Síguenos por Twitter

Suscríbete GRATUITAMENTE al Boletín de Perl en Español

Perl en Español es mantenido con Movable Type
Todo el contenido de Perl en Español está bajo una licencia CC:
Creative Commons License