In-App purchase usando PayPal (válido para todos los dispositivos)

Previous topic - Next topic

msx

Pues a petición del pesado de ampos :nana:, voy a intentar hacer un manual de como realizar un In-App Purchase utilizando PayPal, con la ventaja que admite cualquier forma de pago y que evidentemente evitamos que nos pirateen las aplicaciones, ya que éstas serán activadas solamente cuando confirmemos las recepción del pago.

Lo haré por capítulos porque es un poco largo y dispongo de escasos periodos de tiempo.

Empecemos....


PARTE I: CONFIGURANDO UNA CUENTA DE PRUEBAS EN PAYPAL


Es aconsejable configurar una cuenta para hacer las pruebas en PayPal. Existe una página paralela a la oficial para hacer nuestras pruebas y que no nos cueste el dinero. Las URLs son exactamente las mismas pero anteponiendo sandbox es decir en lugar de www.paypal.com sería www.sandbox.paypal.com.

Lo primero es darnos de alta en https://developer.paypal.com/ y hacemos login.

Una vez dentro crearemos dos cuentas para nuestras pruebas, una de Vendedor (Seller) y otra de Comprador (Buyer). Éstas serán las cuentas que usemos en las pruebas. Para ello seleccionamos Create a preconfigured account.



Rellenamos los datos, creamos una cuenta Seller y después repetimos los pasos para crear una cuenta Buyer. Ponerle un crédito de unos 500$ y así tenemos para ir haciendo pruebas.

Para acceder a estas cuentas y ve el estado en que se encuentran (ingresos recibidos o pagos realizados) entramos en la opción Test Accounts de la pantalla principal y nos saldrá una lista con las cuentas creadas. Ahora para entrar en una solo marcamos la que queramos ver y pulsamos el botón Enter Sandbox Test Account y nos saldrá una pantalla exactamente igual que la página principal de PayPal pero en la parte superior saldrá "Sitio de prueba".



PARTE II: CONFIGURANDO NUESTRA CUENTA DE VENDEDOR


Hemos llegado al momento de configurar nuestra cuenta de vendedor. Nosotros de momento lo haremos en la cuenta de vendedor que hemos creado para pruebas, pero una vez que veamos que todo funciona, haremos lo mismo con nuestra cuenta auténtica.

Seleccionamos Mi cuenta y después Perfil. Ahora hacemos clic en Preferencias de pago en el sitio Web y seleccionamos las siguientes opciones.

Retroceso automático: Activado
URL de retorno: http://Tu_dominio/donativo.php
Transferencia de datos de pago: Activado
Bloquear pago en el sitio Web no codificado: Desactivado
Cuenta PayPal opcional: Activado
Teléfono de contacto: Desactivado
Admitir pagos con giropay y transferencia bancaria: No

Y pulsamos Guardar.

Ahora de nuevo en Perfil, seleccionamos Preferencias de Notificación de pago instantánea.

URL de notificación http://Tu_dominio/paypal_ipn.php
Entrega de mensajes Activada

Y guardamos la configuración.

Pues bien, ahora ya tenemos nuestra cuenta PayPal preparada para recibir pagos y enviar las notificaciones a nuestra web.



PARTE III: ENTRAMOS DE LLENO CON EL PHP Y MYSQL


Esta parte requiere un poco de conocimiento para administrar bases de datos MySQL y el FTP de nuestro servidor. Lo primero es crear una Base de Datos en nuestro servidor a la cual le daremos el nombre que queramos, ésto se hace desde el Panel de Control, es bastante intuitivo por lo que no lo voy a explicar.

La Base de Datos nos servirá para guardar la información del número de serie del dispositivo que vamos a dar de alta, y diversos datos de la persona que hace el pago, para posibles reclamaciones que pudieran surgir.

Creamos los siguiente campos en nuestra base de datos. Si alguien no sabe, buscar manuales de MySQL que hay muchos y explican como hacerlo, aunque es muy sencillo e intuitivo y si os equivocáis siempre se puede borrar y empezar de nuevo.

Los campos a crear serán los siguientes (podéis usar otros nombres para los campos, pero recordar modificarlos también en los archivos PHP):

Code (glbasic) Select
# Columna   Tipo   Cotejamiento Atributos Nulo Predeterminado Extra
1 id   int(11)         No Ninguna         AUTO_INCREMENT
2 transid   text    latin1_swedish_ci No Ninguna
3 nombre   text    latin1_swedish_ci No Ninguna
4 fecha   int(11)         No Ninguna
5 estado   text    latin1_swedish_ci No Ninguna
6 email   text    latin1_swedish_ci No Ninguna
7 ciudad   text    latin1_swedish_ci No Ninguna
8 moneda   text    latin1_swedish_ci No Ninguna
9 cantidad  tinytext latin1_swedish_ci No Ninguna
10 variables text    latin1_swedish_ci No Ninguna
   

El campo variables servirá para guardar toda la información que nos envía PayPal, por si se produce algún error y/o reclamación, podremos hacer una investigación más exhaustiva para exclarecer lo ocurrido.

Ahora comenzamos con nuestro archivos PHP.

Comenzamos con el primer archivo que será el que llamemos desde GLBasic.

paypal.php
Code (glbasic) Select
<?php
$form5
='<form name="_donations" id="donar" action="https://www.sandbox.paypal.com/es/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_donations">
<input type="hidden" name="business" value="email_vendedor">
<input type="hidden" name="item_name" value="nombre_del_servicio_ofrecido">
<input type="hidden" name="item_number" value="'
.$_GET["id"].'">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="amount" value="'
.$_GET["am"].'"><p align="Center"><br/><br/><font size="5"><b>Processing, please wait...</b></font></p></form><body onload="document._donations.submit();">';

echo 
$form5;
?>


En el campo business colocamos el email que tenemos registrado en PayPal como vendedor.

En el campo item_name pondremos el nombre del servicio que ofrecemos, es lo que aparecerá en la factura del cliente, podria ser Versión Completa de tal juego.

En currency_code pondremos USD para dólares americanos o EUR para euros, etc... Yo uso USD para todos los pagos independientemente del País, pero eso lo dejo a vuestra elección. Con una pequeña modificación se podría hacer que se usara la moneda del país desde donde se hace el pago, sería cuestión de que ese valor lo pasaramos desde GLBasic.

Bueno pues ya tenemos el archivo que hará de plataforma para pasar los datos desde GLBasic hasta PayPal. Cuando hagamos la llamada a este archivo tendremos que pasarle una serie de parámetro como son item_number (número de serie del dispositivo) y amount (precio del servicio).


A continuación vamos a crear el archivo que será el encargado de recibir los datos de la transacción y el encargado de almacenar todos los datos en nuestra BD en caso de que dicha transacción se haya completado.

paypal_ipn.php
Code (glbasic) Select
<?php
$link=mysql_connect("host","nombre_usuario","password");
mysql_select_db("nombre_BD",$link);

$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.sandbox.paypal.com'80$errno$errstr30);
 
if($_POST['payment_status']=='Completed' || $_POST['payment_status']=='Processed') {

$query="insert into paypal (nombre, transid, fecha, estado, email, ciudad, moneda, cantidad, variables) VALUES ('".$_POST['first_name']." ".$_POST['last_name']."', '".$_POST['item_number']."', ".time().", '".$_POST['payment_status']."', '".$_POST['payer_email']."', '".$_POST['address_state']."', '".$_POST['mc_currency']."', '".$_POST['payment_gross']."','".$req."')";
$result=mysql_query($query,$link);

}

?>


Para configurar este archivo es necesario rellenar estas dos líneas con nuestros datos:

   $link=mysql_connect("host","nombre_usuario","password");
   mysql_select_db("nombre_BD",$link);


En host colocaremos el host de nuestra BD, generalmente será localhost aunque podría variar en algún servidor. En nombre_usuario y password ponemos el nombre de usuario que le hayamos dado a nuestra Base de Datos y el password. Y finalmente en nombre_BD pues el nombre de la Base de datos que creamos anteriormente.

Por último configuraremos el archivo donde remitirá PayPal a nuestro cliente cuando haya terminado de realizarse la transacción.

donativo.php
Code (glbasic) Select
<?php
// read the post from PayPal system and add 'cmd'
$req 'cmd=_notify-synch';

$tx_token $_GET['tx'];
$auth_token "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$req .= "&tx=$tx_token&at=$auth_token";

// 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.sandbox.paypal.com'80$errno$errstr30);
// If possible, securely post back to paypal using HTTPS
// Your PHP server will need to be SSL enabled
// $fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);

if (!$fp) {
// HTTP ERROR
} else {
fputs ($fp$header $req);
// read the body data
$res '';
$headerdone false;
while (!
feof($fp)) {
$line fgets ($fp1024);
if (
strcmp($line"\r\n") == 0) {
// read the header
$headerdone true;
}
else if (
$headerdone)
{
// header has been read. now read the contents
$res .= $line;
}
}

// parse the data
$lines explode("\n"$res);
$keyarray = array();
if (
strcmp ($lines[0], "SUCCESS") == 0) {
for (
$i=1$i<count($lines);$i++){
list(
$key,$val) = explode("="$lines[$i]);
$keyarray[urldecode($key)] = urldecode($val);
}
// check the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
$firstname $keyarray['first_name'];
$lastname $keyarray['last_name'];
$itemname $keyarray['item_name'];
$amount $keyarray['payment_gross'];

echo (
"<table align='center'><tr><td align='center'><h3>Thank you for your purchase!<br>¡Gracias por su donación!</h3>");

echo (
"<b>Payment Details/Detalles del pago</b><br>\n");
echo (
"<table><tr><td align='left'><li>Name/Nombre: <i>$firstname $lastname</i></li>\n");
echo (
"<li>Item/Servicio: <i>$itemname</i></li>\n");
echo (
"<li>Amount/Cantidad: <i>$amount </i>USD</li>\n</p></td></tr></table></td></tr></table>");
echo (
"");
}
else if (
strcmp ($lines[0], "FAIL") == 0) {
echo (
"<table align='center'><tr><td align='center'><h3>Transaction failed!<br>¡Error en la transacción!</h3></td></tr></table>");

}


Aquí leemos los datos que nos envía PayPal referente a la transacción que nos servirá para informar al cliente que todo ha ido bien o, de lo contrario, se ha producido un error. Son varios los datos que se reciben, aunque yo personalmente solo muestro el nombre, el servicio que ha comprado y la cantidad que ha pagado. Si necesitáis algo más pues podéis comentarlo en el foro.

Es importante rellenar el siguiente dato correctamente:

$auth_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

Porque de lo contrario no recibiremos nada. Este dato se obtiene de nuestra cuenta de PayPal, concretamente en Perfil->Preferencias de pago en el sitio Web en el apartado Transferencia de datos de pago (opcional), el campo llamado Código personal de identidad. Solo tendremos que sustituir las XXXXXX por el valor que tengamos ahí.   

Una vez terminado el proceso de pago, tan solo nos queda el archivo que se encargará de hacer la consulta a nuestra base de datos para comprobar si existe el número de serie del dispositivo que estamos autorizando. Devolveremos un valor u otro a GLBasic en función de que exista o no exista, y de esta forma podremos dar la correspondiente autorización o por el contrario la negación de los servicios.

Este archivo podría ser algo así:

auth.php
Code (glbasic) Select
<?php
$ID
=$_GET['id'];

$link=mysql_connect("host","nombre_usuario","password");
mysql_select_db("nombre_BD",$link);
$query=sprintf("SELECT * FROM paypal WHERE transid='%s'",
addcslashes(mysql_real_escape_string($ID),'%_'));
$result=mysql_query($query$link);
$numero_rows mysql_num_rows($result);

if (
$numero_rows>0)
echo "1";
else 
        echo 
"0";

?>


Este archivo recibe el número de serie que tenemos que comprobar y devuelve 1 si existe en la base de datos (significa que está autorizado) o devuelve 0 si no existe (no hay autorización).

Aquí podríamos usar nuestra imaginación con unos pocos conocimientos de PHP, ya que este archivo es muy básico. Por ejemplo, podrían encontrar la forma de desviar la llamada y que en lugar de hacerla a auth.php en nuestro servidor, lo hiciera a otro servidor que respondiera siempre 1 por lo que conseguirían autorización siempre. Para el caso que yo lo uso (para donación) la verdad es que no me preocupa, pero para próximos proyectos, lo que yo haré es que en lugar de responder 1 devuelva el valor de ese número de serie encriptado (como lo hace GLBasic, creo que hay un script de php por algún lado que encripta igual), de forma que GLBasic al recibirlo lo desencriptaría (con la misma semilla) y lo compararía con PLATFORMINFO$("ID") y si el valor coincide daría autorización. Evidentemente este sistema sí sería seguro porque no se podría suplantar la respuesta porque no se conoce la semilla. Tener la precaución de no usar la misma semilla para todas las aplicaciones, porque entonces sí que se sabría la respuesta y pagando una aplicación podrían tener todas las aplicaciones, para ese dispositivo, autorizadas.



PARTE IV: CERRANDO EL CÍRCULO


Bueno pues ya está todo prácticamente terminado. Tan solo nos queda enlazarlo todo con GLBasic, y ¿cómo se hace ésto?, pues my fácil. Primero haremos una llamada a paypal.php y le pasamos los valores necesarios para la transacción es decir, el ID y la cantidad a pagar:

NETWEBEND "http://tu_dominio/paypal.php?id="+PLATFORMINFO$("ID")+"&am="+Cantidad_a_pagar


Ésto llamará a nuestro servidor que se desviará automáticamente a PayPal pasándole los valores, una vez se realice el pago, PayPal devuelve el resultado de la transacción a nuestro servidor (archivo paypal_ipn.php) que comprobará si todo ha ido bien y en caso afirmativo almacena los datos en la Base de Datos, después se contesta a PayPal que hemos recibido correctamente la información y PayPal da por concluída la operación por lo que regresa a nuestro servidor, concretamente al achivo donativo.php entonces le pedimos a PayPal que nos envíe todos los datos de esa operación y el resultado final y si ha sido todo correcto mostramos al ususario los datos que queramos que vea, en mi caso su nombre, el servicio que ha pagado y la cantidad además de informarle que todo ha sido correcto y que ya tiene los servicios activado.

Por último necesitamos comprobar si el usuario está autorizado desde nuestro GLBasic, hacemos la siguiente llamada:

AUTH=NETWEBGET$("tu_dominio","/auth.php?id="+PLATFORMINFO$("ID"),80,512)


IF AUTH=1
    // AUTHORIZED CUSTOMER
ELSE
    // UNAUTHORIZED CUSTOMER. SHOW ME THE MONEY!.
ENDIF




PARTE V: CONCLUYENDO


Todo ésto es orientativo, evidentemente podéis mejorar el código PHP para darle más seguridad o para adaptarlo a vuestras necesidades aunque tal y como está aquí descrito funciona perfectamente. Es importante que no toquéis la parte del código que comunica con los servidores de PayPal porque esa parte es delicada y podría dejar de funcionar, pero podéis trabajar con los datos que obtenéis para mejorarlo. Si necesitáis ayuda con el PHP os puedo intentar echar una mano.

IMPORTANTE: Hasta ahora hemos estado trabajando con el SandBox de PayPal, por lo que los cargos son ficticios. Una vez que comprobéis que todo va bien, debéis editar los archivos PHP poniendo vuestros datos verdaderos de PayPal (email, auth_token) y cambiar todas las URL que comiencen por www.sandbox.paypal.com por www.paypal.com.

NOTA: Es conveniente colocar en nuestra aplicación una opción para consultar el número de serie del dispositivo tal y como lo muestra el comando PLATFORMINFO$("ID"), nos puede servir para que en caso de que haya algún error en la activación, el usuario pueda darnos su número de serie para que podamos activarlo de forma manual.

...THE END






ampos

THIS IS A TUTORIAL TO USE PAYPAL AS PAYMENT SYSTEM IN ANY PLATFORM (SOME KIND OF IN-APP PURCHASE). AS MSX's ENGLISH IS A BIT LIMITED, IT WILL BE TRANSLATE INTO "REAL" ENGLISH ONCE THIS TUTORIAL IS DONE.

THANK YOU FOR YOUR PATIENCE AND NICE WORK, MSX!
check my web and/or my blog :D
http://diniplay.blogspot.com (devblog)
http://www.ampostata.org
http://ampostata.blogspot.com
I own PC-Win, MacBook 13", iPhone 3G/3GS/4G and iPAC-WinCE

msx

Tú eres un poco cabroncete, que es eso de bit, será very bit  :nana:

ampos

check my web and/or my blog :D
http://diniplay.blogspot.com (devblog)
http://www.ampostata.org
http://ampostata.blogspot.com
I own PC-Win, MacBook 13", iPhone 3G/3GS/4G and iPAC-WinCE

Kitty Hello

If you make this work, I'll contribute the library with GLBasic. You're the best!!

msx

Seguimos completando el manual en el primer post  :whip:

Kitty Hello

ah. Last time I checked the lower part was missing. AWESOME!

Now, can someone please translate it for us?


Quote$fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30);
You can do that with port 80!? Then the whole validation-process could be written in GLBasic?

ampos

check my web and/or my blog :D
http://diniplay.blogspot.com (devblog)
http://www.ampostata.org
http://ampostata.blogspot.com
I own PC-Win, MacBook 13", iPhone 3G/3GS/4G and iPAC-WinCE

Nathan

Does the mean you totally get around Apple taking 30% of your in-app purchase?  Really sweet!   :booze:

ampos

check my web and/or my blog :D
http://diniplay.blogspot.com (devblog)
http://www.ampostata.org
http://ampostata.blogspot.com
I own PC-Win, MacBook 13", iPhone 3G/3GS/4G and iPAC-WinCE

Crivens

Out of interest what does the interface look like on a mobile device? It all sounds really good, but would it be almost as simple as current iOS in-app purchasing (ie. just type your password although I assume you will need your Paypal login too)?

It would be nice if (like Openfeint) it knew what your paypal login is because of a pre-payment on a different app (ie. storing the device ID on your website) so you just needed to enter a password for further payments (on any app) after the first ever payment on a device.

But does it then expect card details, CV2 security numbers, 3D secure passwords etc? Personally I see all that and I switch off. As close to the apple in-app solution the better really.

Cheers
Current fave quote: Cause you like musicians and I like people with boobs.

msx

Quote from: Nathan on 2011-Oct-13
Does the mean you totally get around Apple taking 30% of your in-app purchase?  Really sweet!   :booze:

I think that PayPal charges 3.4% + € 0.35 EUR for credit card payments. It will be better than Apple if the amount we charge is higher than 1 EUR, otherwise it is similar.

msx

Quote from: Crivens on 2011-Oct-13
Out of interest what does the interface look like on a mobile device? It all sounds really good, but would it be almost as simple as current iOS in-app purchasing (ie. just type your password although I assume you will need your Paypal login too)?

It would be nice if (like Openfeint) it knew what your paypal login is because of a pre-payment on a different app (ie. storing the device ID on your website) so you just needed to enter a password for further payments (on any app) after the first ever payment on a device.

But does it then expect card details, CV2 security numbers, 3D secure passwords etc? Personally I see all that and I switch off. As close to the apple in-app solution the better really.

Cheers

Always would enter all personal information when you pay by credit card, since it manages the secure PayPal page. We can do nothing. Everything is easier if the user have a PayPal account.

If you accept credit card payments on your website would be as easy as managing it all from the same site, then yes you could do that only need to enter the registration data, but this is not the topic proposed here.

ampos

Lo del auth_token del final no lo pillo muy claro...
check my web and/or my blog :D
http://diniplay.blogspot.com (devblog)
http://www.ampostata.org
http://ampostata.blogspot.com
I own PC-Win, MacBook 13", iPhone 3G/3GS/4G and iPAC-WinCE

msx

Perfil->Preferencias de pago en el sitio Web baja hasta que encuentres Transferencia de datos de pago (opcional) y verás un campo alfanumérico muy largo. Ése es tu auth_token.