In-App purchases using PayPal (for any platform)

Previous topic - Next topic

ampos

You have to thanks MSX for this awesome work, not me. I'm just the guilty of this crappy translation.

Any comments about the code are welcomed. Comments about the translation, please, sent me a PM.


PART I: CREATING A PAYPAL TESTING ACCOUNT


We need to create a test account for Paypal. There is a website by PayPal for this purpose, without real money (but fake one). The URL is the same but adding "sandbox": www.sandbox.paypal.com instead of www.paypal.com

The first step is to sign up at https://developer.paypal.com and login.

Then we will create 2 accounts, one as Seller and one as Buyer. These are the accounts we will use for our testing. Choose Create a preconfigured account.



Fill it and create the seller account. Then again the buyer account with $500 for the tests.

To check this accounts status (payments, purchases,...) choose Test accounts in the main page and a list with our accounts will be listed. Choose the one you want to see and click at Enter Sandbox Test Account. You will be redirected to a page similar to the standard PayPal website, but with "Test Site" on the top.


PART II: SETTING OUR SELLER ACCOUNT


It's time to set our seller account. At first, we will do it with our Seller Test Account but once we see it is working, do it with your real seller account.

Choose My account -> Profile -> Website Payment Preferences

Auto return: On
Return URL: http://your domain/donative.php
Payment Data Transfer: On
Encrypted Website Payment: Off
Paypal Account optional: On
Contact Telephone number: Off
Express Checkout Settings: No

And Save

Again, Profile -> Instant Payment Notifications Preferences -> Edit Settings

Notification URL http://your_domain/paypal_ipn.php
Receive IPN messages On

And Save

Ok, we have our seller account ready to receive payments and get notifications to our website.


PART III: IT'S MySQL & PHP TIME!


This part needs a little knowledge of MySQL databases and FTP. The first step is to create a database in our sever (with any name). This is done through the control panel of our website server. It's pretty intuitive, so I will not extend on it.

The database will be used to store information like buyer's device id and some other values, in case of reclamation.

We will create the following fields (you can change the names, but remember to change them in the PHP files):

Code (glbasic) Select

# Column Type Collation Null Default Extra
1 id int(11)         No None       AUTO_INCREMENT
2 transid text     latin1_swedish_ci No None
3 name text latin1_swedish_ci No None
4 date int(11) No None
5 status  text     latin1_swedish_ci No None
6 email   text     latin1_swedish_ci No None
7 city   text     latin1_swedish_ci No None
8 currency text     latin1_swedish_ci No None
9 amount  tinytext latin1_swedish_ci No None
10 paypal text latin1_swedish_ci No None
   

The field "paypal" will be used to store all the info sent by PayPal, in case there is an error or reclamation.

Now it is PHP time!

The first archive will be the one called by GLBasic.

paypal.php
Code (glbasic) Select
<?php
$form5
='<form name="_donations" id="donate" 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="your seller email">
<input type="hidden" name="item_name" value="Name of item">
<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;
?>


In business put the email you created in PayPal as seller.

In item_name place the name of the item/service offered; it will be shown in buyer's screen, something like remove bra from girls.

In currency_code put USD for US Dollars, EUR for euros,... I use USD always, but it is up to you. It could be modified to use the buyer's currency, but this value has to be passed to GLB... forgot for today :)

This was the program that will do from GLBasic to Paypal. When we call this file, we have to pass some values: item_number (device's id) and amount (price of the item).


Now we are going to create the php file that will receive the transaction data, that also will store all the data in the database if the bought was ok.

paypal_ipn.php
Code (glbasic) Select
<?php
$link=mysql_connect("host","user_name","password");
mysql_select_db("BD_Name",$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 (name, transid, date, status, email, city, currency, amount, paypal) 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);

}

?>


To configure this file you will need to change these two lines with your own data:


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


In host put the host of your DB, usually localhost, although it could vary in some servers. In username and password put the name & password of the user of the database. In DB_Name the name of the database created :)


And now, the PHP file used by PayPal when the buyer finish his transaction.

donative.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 in the transaction!!</h3></td></tr></table>");

}


Here we read the data sent by PayPal regarding the transaction, that will help us to inform the buyer if all was ok or there was an error. Many data are received, although I only show name, item bought and price. If you need something else, just ask.

It is important to fill the following data correctly

$auth_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

or nothing will be received. This data can be got in our PayPal account, in My account -> Profile -> Website Payment Preferences in the field called Payment Data Transfer (optional), in Identity Token.

Once the payment has finished, we only need the PHP that will query our DB to check if our device's id is authorized. We will send back to GLBasic a value if it does or not, so we can check if the item is available or not in the program.

Something like this:

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

$link=mysql_connect("host","user_name","password");
mysql_select_db("BD_name",$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";

?>


The PHP file gets the device's id to check, and returns 0 if it does not exist, or 1 it does.

We a bit more knowledge of PHP we can improve this routine, as currently is very basic. It could be "easy" to change the call to our database to somewhere else and always return "1", so the queries would be always authorized. For the use I do (just extra donations) I don't care a lot, but for next projects, instead of "1" I will return the device's id encrypted (using the same algorithm as GLBasic, I think there is a php file that does this somewhere), so when GLBasic receives the authorization, it could be decrypted and checked against PLATFORMINFO$("ID"), giving authorization if the two values are equal, making it a more secure system, as the answer could not be supplanted as the seed is unknown. Do not use the same seed in all your programs, as the same answer could validate any application for the same device.


PART IV: CLOSING THE CIRCLE


Ok, it is almost done. Just join all together in GLBasic. How? First we have to call paypal.php with the needed values for the transaction, device's id and amount to pay:

NETWEBEND "http://your domain/paypal.php?id="+PLATFORMINFO$("ID")+"&am="+amount_to_pay

This will call our server, that will call Paypal with the correct values; once the payment is done, PayPal sent back the result of the transaction to our server (file paypal_ipn.php) that will check if all was ok, and if it was, fill our database with the results and answer PayPal with "data received". PayPal will set the transaction as finished, going back to our server at the file donative.php that will ask PayPal all the data of the transaction and the final result. If ok, we will show to the user the data we want to (I am showing name, item purchased and amount), and that all was fine (or not!) and the girl's bra were removed.

Now we need in GLB to check if the user/app is authorized using this call:


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


IF AUTH=1
    // The bras are removed!
ELSE
    // No, no bras for you. Show me the money!
ENDIF




PARTE V: CONCLUDING


All of this is orientative; PHP code can be modified for more security or to suit your needs, although as it is here it is working. Do not touch the code that calls PayPal servers, as it is delicate and could stop working, although you could work with the received data and improve it. If you need PHP help I could.

IMPORTANT: we have been working with PayPal's Sandbox, so the money is fake. Once all is working fine, you just need to replace the fields in the php files with your true ones (email & auth token). Also you will have to remove all the sandbox word from the domain names (www.sandbox.paypal.com -> www.paypal.com.

NOTE: The device's id as returned by PLATFORMINFO$("ID") should be shown to the user, so he can sent it to us, so we can activate his device in "manual mode" (or just our friend's devices).

...THE END
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

Qube

Thanks :) - Very handy and will save hours of work, good one :good:

Couple of questions though about the use of PayPal.

1.. Don't Apple refuse external payment providers from the appstore as they loose their 30% commission on in-app sales?

2.. PLATFORMINFO$("ID") - Apple recently changed this so apps that need a unique ID must generate their own rather than the unique ID of the device (something to do with device tracking). So I would assume that using that command by itself would too stop Apple from allow the app in the store?

With No.2 I think the easiest way would be to still use PLATFORMINFO but either hash it in app to even easier use the encrypt command which would produce a unique string / ID off of it.

No.1 is the issue though. I'd love to be able to offer in-app purchase without the need for Apple to grab 30% each time :/

ampos

1.- I thought it will be rejected, but the funny thing is that in Glowing Sky, I put a register in-app and 3 other buttons called "donations", in case someone wants to give me more money. The app was rejected because donations were not allowed with in-app purchases, and has to be using a external payment system as paypal. Go imagine!
I just have to change the name of the button (extra tip!) to have it approved.

Anyway, perhaps another reviewer could interpret it differently.

2.- Yes, I remember reading something like this, but not sure. What does platforminfo("id") returns in iOS5?
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

the return vlaue should be returning something with encode$ (blowfish.php) to make it a tad harder for crackers that just add a proxy server. For products < 2$ I'd leave it as it is.
AWESOME!!!

bigsofty

Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

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

bigsofty

OK, good job(thanks MSX! :)) all round!  :good:
Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

spacefractal

... until Paypal freeze you money. I have to advice I do not like Paypal for bussniss use, since you never known when they freeze that...... So its must been smaller thing of course.
Genius.Greedy Mouse - Karma Miwa - Spot Race - CatchOut - PowerUp Elevation - The beagle Jam - Cave Heroes 2023 - https://spacefractal.itch.io/

erico

HArdcore work MSX! you are great!
Now for some tougher work I want to see you finish La Abadía del Crimen game (the original of course)!!

Falstaff

This looks awesome, and the fact I could use it on android as well as iOS specifically makes this incredibly interesting. But is this really ok with Apple? If your app is free and you only try to make money through IAP, then you'd be completely avoiding their 30% fees.. surely there's something in their ToS about this? Why wouldn't everyone implement their own payment gateway for IAP already then?

I guess the downfall is you'll need people to enter in their account info (paypal login), and of course this assume people have Paypal, whereas with IAP you benefit from Apple already having their payment info on file.. Still, this would be unbelievably cool and maybe something to offer in addition to IAP, with incentive like extra virtual currency as a result for choosing that route.

edit:
As I feared, according to Apple's own Developer Guidelines, under section 11: "Purchasing and currencies"

Quote11.1
Apps that unlock or enable additional features or functionality with mechanisms other than the App Store will be rejected
11.2
Apps utilizing a system other than the In App Purchase API (IAP) to purchase content, functionality, or services in an app will be rejected

So.. while you could probably get away with a "donate" link, it's against the rules to code that to obtain any in-game items or unlock content. I know for my game that's what I'll be using IAP for so I'll have to pass.. too bad, it'd be nice to have a cross-platform solution for IAP..!

*edit x 2:
Well I guess it *is* cross platform and should be ok for anything non apple :) I don't have a license for the android marketplace so I'm not sure.. but I wouldn't be surprised if they had similar rules..

ampos

Quote from: Falstaff on 2011-Oct-24
*edit x 2:
Well I guess it *is* cross platform and should be ok for anything non apple :) I don't have a license for the android marketplace so I'm not sure.. but I wouldn't be surprised if they had similar rules..

The code itself is cross-platform :)

About Android, there is not a review team. Your apps are uploaded and ready in the market as soon as you upload it.
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

The iPhone ebay-App has an PayPal in-app pruchase...

Falstaff

#12
Hm.. This is true.. It's pretty much just because they're using a payment method for a transaction for an item that isn't inside the actual app itself. This guys explains it in his blog: http://www.quora.com/How-do-eBay-and-Groupon-get-around-Apples-in-app-purchase-system-despite-being-native-apps

What most people do with IAP (and what I'll be doing), is provide a virtual currency to be used in-app for things like content. For this, I will have to use apple's own IAP system.

Kitty Hello

OK, then sell "printable vouchers", that you can enter in your program to get real currency.
Sort of "virtual" iTunes gift cards.

ampos

iOS users will be much against any payment method outside Apple/InApp system. Also, most of iOS users will have his credit card associated to iTunes/iOS, a much lower percentaje than the ones with Paypal+ìOS device.
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