Nov 252013
 
Artículo Comercio electrónico

Para sitios web que utilizan un CMS tipo Joomla, Drupal, WordPress, etc…, la manera más sencilla de implementar una página de pago por Paypal es utilizar alguno de los numerosos módulos o plugins existentes que ofrecen esta funcionalidad.

Pero en otras ocasiones, o bien no se utiliza alguno de estos CMS, o bien se quiere añadir una funcionalidad particular que no está disponible.

Para estos casos, en este artículo se explica paso a paso la implementación de una página de pago mediante Paypal en un sitio web, utilizando directamente el API de Paypal.

1. El sandbox y las cuentas de pruebas

Los accesos al servicio de Paypal se realizan mediante solicitudes POST a la url http://www.paypal.com/webscr.

Pero para poder ir probando nuestra aplicación mientras la estamos desarrollando, sin incurrir en gastos realizando transferencias de dinero real, podemos utilizar el entorno de pruebas de Paypal, sustituyendo la url de arriba por http://www.sandbox.paypal.com/webscr.

Para utilizar el sandbox, también debemos disponer de al menos dos cuentras de prueba. Una será la cuenta “Business” (“vendedor”) que corresponde al receptor del dinero, y otra la cuenta “Personal” (“comprador”), que corresponde a quien compra un producto o servicio, y realiza el pago.

Para crear las cuentas de prueba, accedemos a “developer.paypal.com” (dándonos de alta si es preciso):

login-developer

 

Una vez dados de alta debemos acceder a “Applications -> Sandbox Accounts”. Veremos que Paypal ya ha creado una cuenta “Business” por defecto. Haciendo click en “Create Account”, podemos crear una cuenta adicional de tipo “Personal”. La dirección de correo que utilicemos puede ser ficticia, ya que en el entorno sandbox no se envían correos reales:

Listado de cuentas de pruebas de paypal y creación de cuenta personal

Listado de cuentas de pruebas de paypal y creación de cuenta personal

2. Formulario de pago básico

El siguiente paso es crear un formulario para que el usuario pueda realizar la adquisición del producto o servicio.

Su implementación más sencilla sería con un código HTML para el formulario, que simplemente presenta en pantalla un botón de pago.

El código del formulario es:

<html>
    <body>
        <form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
            <input type="hidden" name="cmd" value="_xclick">
            <input type="hidden" name="business" value="openalfa-facilitator@openalfa.com">
            <input type="hidden" name="item_name" value="Premium Subscription">
            <input type="hidden" name="currency_code" value="USD">
            <input type="hidden" name="amount" value="20.00">
            <input type="image" src="http://www.paypal.com/es_XC/i/btn/x-click-but01.gif"
                   name="submit"
                   alt="Make payments with PayPal - it's fast, free and secure!">
        </form>
    </body>
</html>

Al pulsar sobre el botón, el navegador envía el formulario a “http://www.sandbox.paypal.com/cgi-bin/webscr”, y en pantalla se presenta la página de paypal para validarse (como comprador) y realizar la transacción:

sandbox-payment

En esta página introducimos los datos del comprador de pruebas, y pulsamos sobre “Iniciar sesión”.

Una vez finalizada la transacción, paypal presenta una pantalla de confirmación, pero no regresa al sitio web en donde se inición la transacción:

confirmacion-pago

4. Mejoras al proceso de pago

El formulario básico que hemos descrito en el apartado anterior es suficiente para implementar la funcionalidad de pago por paypal, pero hay varios aspectos en los que puede ser mejorado:

  • Por una parte, la transacción finaliza en una página de paypal. Sería deseable regresar al sitio que inició la transacción, quizá con una página de agradecimiento.
  • Por otra parte, durante el proceso en paypal, el usuario puede decidir cancelar la transacción, o bien el pago puede fallar por falta de fondos u otro motivo. Es muy conveniente que el sitio que inicia la transacción disponga de alguna manera de obtener de paypal información sobre el estado de la transacción.
  • Por otra parte, si lo que se ha adquirido es un producto físico, el sitio web necesita conocer la dirección de entrega  para proceder al envío. Paypal solicita al usuario esta información, pero el sitio web debe tener también algún medio de obtenerla. Por el contrario, si lo que se ha adquirido es un producto virtual (una subscripción, una descarga de un fichero,…) la dirección de entrega no tiene sentido.

A continuación vemos como mejorar estos aspectos del proceso.

 4.1. Evitar que paypal pregunte por una dirección de entrega si se está adquiriendo de un producto virtual (suscripción, descarga,…)

Esto se consigue simplemente añadiendo al formulario un campo de la forma:

<input type="hidden" name="no_shipping" value="1">

4.2. Regresar al sitio que inició la transacción.

Una vez finalizada la transacción, se pueden distinguir dos casos: transacción completada con éxito (pago realizado) o transacción cancelada.

En el formulario se puede especificar una página de retorno para cada uno de estos casos, añadiendo al formulario dos campos “return” y “cancel return”:

<input type="hidden" name="return" value="http://www.sitio.com/pagado.php">
<input type="hidden" name="cancel_return" value="http://www.sitio.com/cancelado.php">

Al añadir estos campos, la página que presenta paypal cuando finaliza la transacción incluye los enlaces que se asignan como valores, pero el usuario no regresa al sitio hasta que no hace click en uno de ellos.

Para que el regreso al sitio de partida sea automático, hay que establecer esta opción en la configuración del usuario “business”:

Para ello, hay que hacer login en www.paypal.com (durante el desarrollo, hay que hacer login en www.sandbox.paypal.com) con el usuario “business” receptor del pago (el usuario “-facilitator” que creamos anteriormente), y acceder a “Perfil -> Mis herramientas de venta”.

config-herramientas-de-pago

En la pantalla que aparace, dentro de la sección “Vender en línea”, pulsamos sobre el enlace “Actualizar” a la derecha de “Preferencias del sitio Web”:

actualizar-preferencias

Por último, en la pantalla que aparece introducimos una url de nuestro sitio web para la página de retorno, y seleccionamos la opción de retorno “Automático”:

config-retorno-automatico

Nota: La url configurada en esta página es la página por defecto a la que se regresa una vez finalizada la transacción, pero para cada transacción se puede especificar una página de retorno distinta, tanto para transacciones con éxito como para transacciones canceladas, utilizando los campos del formulario “return” y “cancel_return” que hemos comentado anteriormente.

Por otra parte, la información que se presenta en página de retorno debe cumplir con una serie de requisitos:

  • Según las Condiciones de uso, debe proporcionar información que haga saber al comprador que el pago se ha realizado y que la transacción ha finalizado.
  • Debe explicar que los detalles de la transacción del pago se enviarán por correo electrónico al comprador.
  • Ejemplo: Gracias por su pago. Su transacción ha finalizado y le hemos enviado un recibo de su compra por correo electrónico. Puede acceder a su cuenta para ver los detalles de esta transacción.

4.3. Obtener información del estado de las transacciones realizadas

En ocasiones, puede ocurrir que el pago no se complete de manera inmediata, por ejemplo si es necesario realizar un cambio de divisa. En este caso, el sitio web debe poder realizar un seguimiento del estado del pago, hasta que es completado con éxito o es cancelado.

Paypal ofrece dos mecanismos distintos y complementarios para enviar al sitio web la notificación del estado del pago:

  • PDT (Payment Data Transfer) – Es el mecanismo por el cual Paypal añade a la url de retorno una serie de argumentos con la información relevante sobre el pago realizado. Entre ellos está el identificador de transacción (argumento “tx”). Con el identificador de transacción, el sitio web puede realizar una solicitud POST a paypal para obtener el resto de la información relativa a la transacción.
  • IPN (Instant Payment Notification) – Al activar IPN, Paypal envía una solicitud POST a la url que se especifique, cada vez que una transacción cambia de estado. Los parámetros de la solicitud post incluyen toda la información relevante sobre la transacción.

La principal diferencia entre ambos mecanismos es que en PDT es el servidor web el que activamente solicita de Paypal la información, mientras que en IPN es paypal el responsable de notificar al servidor web de cualquier cambio en el estado de la transacción.

4.3.1. Cómo activar PDT

  • Acceder a la cuenta Business de Paypal en www.paypal.com (o en www.sandbox.paypal.com)
  • Ir a Mi Cuenta → Perfil → Website Payments Preferences (Under ‘Selling Preferences’ heading or ‘Hosted payment settings’)
  • Poner a On Auto Return
  • Especificar una  URL de Retorno (Nota: En el formulario se puede especificar una URL distinta)
  • Poner a On Payment Data Transfer
  • Guardar
  • Copiar el Identity Token que aparece en la caja amarilla. Este token será utilizado en la página de retorno para validar la información recibida.

 4.3.2. Cómo activar IPN

  • Acceder a la cuenta Business de Paypal en www.paypal.com (o en www.sandbox.paypal.com)
  • Ir a Mi Cuenta → Perfil → Instant Payment Notification preferences:
  • Click Turn On IPN on the PayPal site.
  • You will then be prompted for a Notification URL, which should be your store site catalog page (Shopp will override this URL to a proper URL for receiving notifications).
  • Save your Settings

5. Proceso de mensajes PDT

Cuando se ha activado PDT, al finalizar la transacción, paypal redirige el navegador a la página de retorno especificada, añadiendo una serie de argumentos entre los que se encuentra el identificador de transacción, identificado por la clave “tx”.

Ejemplo:

www.ejemplo.com/return.php?tx=1XA11350TU8279492&st=Pending&amt=10.00&cc=EUR&cm=&item_number=

En el ejemplo, el identificador de transacción es “1XA11350TU8279492”

También vemos que el estado de la transacción, indicado por el valor del argumento “st”, es “Pending”

Para recabar más información de paypal hacerca del estado de la transacción, la aplicación puede hacer una solicitud “POST” al servidor de paypal, pasando los siguientes parámetros:

  • cmd : literal “_notify-synch”
  • tx: identificador de transacción recibido como argumento
  • at: “PDT identity token” obtenido durante la activación de PDT (ver punto 4.3.1)

La solicitud POST para obtener los datos PDT se puede implementar en PHP con la siguiente función:

function paypal_PDT_request($tx,$pdt_identity_token) {
    $request = curl_init();

    // Set request options
    curl_setopt_array($request, array
        (
          CURLOPT_URL => 'https://www.sandbox.paypal.com/cgi-bin/webscr',
          CURLOPT_POST => TRUE,
          CURLOPT_POSTFIELDS => http_build_query(array
              (
                'cmd' => '_notify-synch',
                'tx' => $tx,
                'at' => $pdt_identity_token,
              )
          ),
          CURLOPT_RETURNTRANSFER => TRUE,
          CURLOPT_HEADER => FALSE,
          // CURLOPT_SSL_VERIFYPEER => TRUE,
          // CURLOPT_CAINFO => 'cacert.pem',
        )
    );

    // Realizar la solicitud y obtener la respuesta
    // y el código de status
    $response = curl_exec($request);
    $status   = curl_getinfo($request, CURLINFO_HTTP_CODE);

    // Cerrar la conexión
    curl_close($request);
    return $response;
}

Si todo es correcto, la respuesta recibida es un string que consta de varias líneas.

La primera línea debe ser el literal “SUCCESS”. A continuación están los parámetros, uno por línea.

Ejemplo de respuesta PDT:

SUCCESS
transaction_subject=
txn_type=web_accept
payment_date=09%3A15%3A01+Nov+29%2C+2013+PST
last_name=OpenAlfa
residence_country=ES
pending_reason=multi_currency
item_name=Professional+Subscription
payment_gross=
mc_currency=EUR
business=openalfa-facilitator%40openalfa.com
payment_type=instant
protection_eligibility=Ineligible
payer_status=verified
tax=0.00
payer_email=comprador%40openalfa.com
txn_id=1XA11350TU8279492
quantity=1
receiver_email=openalfa-facilitator%40openalfa.com
first_name=Compradorconvisa
payer_id=SBWU6QRGHTUY2
receiver_id=DQ56QJ7NG9FFS
item_number=
handling_amount=0.00
payment_status=Pending
shipping=0.00
mc_gross=10.00
custom=
charset=windows-1252

En el ejemplo podemos ver que la transacción no se ha completado todavía (payment_status=Pending) y que el motivo es que es necesario realizar un cambio de divisa (pending_reason=multi_currency).

6. Proceso de mensajes IPN

Para el proceso de mensajes IPN debemos crear un script que recibirá solicitudes POST desde paypal.

Opcionalmente, en el formulario de pago podemos añadir un campo “notify_url” para indicar la url de dicho script específica para la transacción, si no es la url por defecto especificada duranta la activación de IPN.

Para poder identificar el pago concreto, añadimos también un campo “invoice”, que contiene el identificador único del pago:

<input type="hidden" name="invoice" id="invoice" value="ID-DE-LA-TRANSACCION" >
<input type="hidden" name="notify_url" id="notify_url" value="http://ejemplo.com/notify.php"/>

Cuando se produce un cambio en el estado del pago, paypal envía una solicitud POST a la url de notificación, con toda la información relevante.

A continuación vemos un ejemplo de los campos enviados por paypal cuando se produce un cambio de estado de una transacción:

 nombre: notify_version, valor: 3.7
 nombre: txn_type, valor: web_accept
 nombre: txn_id, valor: 0WE43036TL1664806
 nombre: invoice, valor: compra+1375284
 nombre: payment_status, valor: Completed
 nombre: item_name, valor: Premium+Subscription
 nombre: item_number, valor:
 nombre: quantity, valor: 1
 nombre: memo, valor: Nota+para+el+vendedor

 nombre: payment_date, valor: 14%3A05%3A46+Nov+25%2C+2013+PST
 nombre: business, valor: openalfa-facilitator%40openalfa.com
 nombre: receiver_id, valor: DQ56QJ7NG9FFS
 nombre: receiver_email, valor: openalfa-facilitator%40openalfa.com
 nombre: payment_type, valor: instant
 nombre: payment_fee, valor: 1.08
 nombre: payment_gross, valor: 20.00
 nombre: mc_gross, valor: 20.00
 nombre: mc_currency, valor: USD
 nombre: mc_fee, valor: 1.08
 nombre: tax, valor: 0.00

 nombre: payer_id, valor: SBWU6QRGHTUY2
 nombre: payer_email, valor: comprador%40openalfa.com
 nombre: payer_status, valor: verified
 nombre: first_name, valor: Compradorconvisa
 nombre: last_name, valor: OpenAlfa
 nombre: residence_country, valor: ES

 nombre: protection_eligibility, valor: Ineligible
 nombre: charset, valor: windows-1252
 nombre: custom, valor:
 nombre: verify_sign, valor: A2LftYh1M4NdDYRDAU2FiV57ordBA-68.3SplFhQeGGOh-st7ZsRBRnR
 nombre: test_ipn, valor: 1
 nombre: handling_amount, valor: 0.00
 nombre: transaction_subject, valor:
 nombre: shipping, valor: 0.00

El script “notify.php” debe procesar estos campos, actualizar como corresponda la base de datos para indicar el estado de la compra, y conceder al usuario acceso al servicio o la descarga adquiridos, o bien enviar el producto adquirido una vez el pago se ha completado con éxito (payment_status=”Completed”).

Por otra parte, el script debe responder a paypal para indicar que se ha recibido correctamente la notificación.

A continuación se presenta la estructura que debe tener el script “notify.php”:

<?php
$header = '';
 $req = 'cmd=_notify-validate';

 foreach($_POST as $clave => $valor) {
    //Hay que utilizar codificación URL en todas las solicitudes a PayPal
    $valor = urlencode(stripslashes($valor));
    //Añadir clave => valor al string CMD
    $req .= "&$clave=$valor";
 }

 //Enviar un POST de vuelta a Paypal para verificació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";
 $fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30);

 if(!$fp) {
     //Procesar Error HTTP
     $message .= "\n HTTP ERROR. \n";

 } else {
     fputs ($fp, $header . $req);
     while (!feof($fp)) {
         $res = fgets ($fp, 1024);
         if (!strcmp ($res, "VERIFIED")) {
             //TRANSACCION VERIFICADA
             //PAGO
             if($_POST['payment_status'] == 'completed') {

                 //THE PAYMENT IS COMPLETE, UPDATE INFORMATION IN SUBSCRIPTION & ADD PAYMENT
                 $subscription = //Obtener la subscripción identificada por $_POST['invoice']

                 //UPDATE SUBSCRIPTION
                 // We know that the payment has succeeded, so we can modify
                 // the last_paid fields and set the paypal id
                 // Using $_POST['txn_id']

                 //ADD PAYMENT
                 // We can now add the payment information 
                 // using $_POST['payer_id'], ['auth_id'] ['mc_gross'] ['payment_status']

             } elseif ($_POST['payment_status'] == 'failed' ||
                      $_POST['payment_status'] == 'expired' ||
                      $_POST['payment_status'] == 'voided') {
                 // El pago no se ha realizado, eliminar la suscripción
             }

         } elseif (!strcmp($res, "INVALID")) {
             // INVALID - EMAIL FOR INVESTIGATION
         }
     }
     fclose ($fp);
 }

 

Referencias

HTML Variables for Paypal Payments Standard

PDT Tutorial

 

 Publicado por en 4:58 pm

 Deja un comentario

(requerido)

(requerido)