So you write a website, you want to sell website templates, webhosting, ebooks or whatever it is that you do. You go looking for a way to take payments and automate some of the business your website has to carry out, only you hit a brick wall, because all the IPN examples that are available on the internet do not work, are out of date, or are too complicated to actually use. So I come along and write a walkthrough tutorial with working code to make your headache go away ... here goes ....
IPN: Instant Payment Notification
Paypal provide a way for your website to automate some of your business, in the shape of IPN. When someone clicks on a "Buy Now" button on your website they are taken to paypal, they login and send a payment, asynchronously ( at the same time ) paypal send data concerning the payment to a predefined script. The payment is sent by way of HTTP POST, your predefined script parses the data and sends it back to paypal. Paypal's server will reply with a response such as "VERIFIED" or "INVALID", this allows your script to send an email containing the template, ebook, webhosting details or whatever, all while you were sleeping or drinking beer.
IPN seems quite confusing at first, there's a ton of variables that paypal could or should send and reading the documentation can be quite daunting, what I plan to do here is dumb it down ( not that you're stupid !! ), so you can start using it, with experience your knowledge will grow as it did with PHP.
So, first of all, you login to your paypal account and enable IPN in your profile. So click on "Profile" in the paypal menu. On the right hand side under the "Selling Preferences" heading, you will see "Instant Payment Notification Preferences", you need to enable this and set the url to something like http://youdomain.com/ipn.php ( ipn.php is what we're writing here ).
The most generic code will send emails about payments to a predefined address, that's what we're doing here, of course you can have the script do whatever suits your website best, this is a working but very generic example of what to do and how it works.
Now, coding standards dictate that anything constant should be defined, so at the top of the script, you'll have the following
These are commented already, so read the comments.
As always, when recieving input, even though it's from what you would expect to be a safe source, you should sanitize the input, if you're using mysql in any way then best mysql_real_escape_string, I'll use stripslashes if magic_quotes are on...
You'll notice, I use _REQUEST instead of _POST, this isn't necessary, nor is it unsafe in any way. _REQUEST commonly contains phpsessid, which can sometimes be usefull for tracking bugs.
Next, we open a socket to paypal, on port 80 ( assuming you dont have a way to support SSL here, infact you should use ssl where available, as that's not generic I won't )
Notice, I make sure there is data in _REQUEST before I do anything, dont want to waste anyones time. I'll take some time to explain this line by line ....
As mentioned I check _REQUEST has data, we then attempt to open a socket ( actually a file descriptior to a socket ).
Before we build a request to send to paypal, we need to tell paypal what we're doing, this is IMPORTANT
You'll have noticed when you go to paypal, or do anything there is a cmd variable in the url, _notify-validate is as it sounds, tells paypal we want to validate a transaction, if this is not added, no dice...
Next, we build the request to send to paypal, we use an array just to make code neater and less prone to errors because you forgot a . before the assignment operator
So, now out $post array conatins whatever _REQUEST did, but urlencoded and with the added cmd variable. to transform the array into a query string simply
now $post contains the formatted and urlencode'd query string to send to paypal.
Next, we build a standard HTTP POST header to write to our socket, as always the first line is
Notice, NO \r\n at the end of the line, again, we're going to implode an array. The next line will be the content length of the request we just formatted in $post
As it's a post request, you should also
When you post data to a server there should be a newline between headers and the post data, so
Then the post string from $post
Next we write the headers and post request to the socket with
Notice, this part adds \r\n to the end of each header line, but NOT after post, so sprintf'ing the string is necessary to append the final newline feed.
Paypal will now respond with a result, so
$response now contains both the response headers and body from paypal.
and then we
Next we want to build an email to send if something goes wrong to PAYPAL_ATTEND defined at the top of the code.
To make it simple
This gets everything that's happening into our email array, including paypals response.
Then finalize the email as a string.
Now, what we're looking for in the $response is the word "VERIFIED", in capital letters just like that, so we're going to start testing the response and taking action accordingly...
First, we check for VERIFIED, if it's found we then see if the payment was completed ( or whatever status you defined at the top of script ), we then check the payment went to the correct address, and finally, we check that the currency of the payment was as expected ( obviously a different currency could mean less money ) ...
Now, I've written as much as I can, the rest is up to you, and your site, the place to put your successfull handling code is
and the final script look like...
IPN: Instant Payment Notification
Paypal provide a way for your website to automate some of your business, in the shape of IPN. When someone clicks on a "Buy Now" button on your website they are taken to paypal, they login and send a payment, asynchronously ( at the same time ) paypal send data concerning the payment to a predefined script. The payment is sent by way of HTTP POST, your predefined script parses the data and sends it back to paypal. Paypal's server will reply with a response such as "VERIFIED" or "INVALID", this allows your script to send an email containing the template, ebook, webhosting details or whatever, all while you were sleeping or drinking beer.
IPN seems quite confusing at first, there's a ton of variables that paypal could or should send and reading the documentation can be quite daunting, what I plan to do here is dumb it down ( not that you're stupid !! ), so you can start using it, with experience your knowledge will grow as it did with PHP.
So, first of all, you login to your paypal account and enable IPN in your profile. So click on "Profile" in the paypal menu. On the right hand side under the "Selling Preferences" heading, you will see "Instant Payment Notification Preferences", you need to enable this and set the url to something like http://youdomain.com/ipn.php ( ipn.php is what we're writing here ).
The most generic code will send emails about payments to a predefined address, that's what we're doing here, of course you can have the script do whatever suits your website best, this is a working but very generic example of what to do and how it works.
Now, coding standards dictate that anything constant should be defined, so at the top of the script, you'll have the following
PHP:
define( "PAYPAL_WEBSCR", "www.paypal.com" ); # Server to use
define( "PAYPAL_ADDRESS", "you@yourdomain.com" ); # paypal address expected
define( "PAYPAL_CURRENCY", "USD" ); # Currency of transaction
define( "PAYPAL_REQUIRED_STATUS", "completed" ); # All payments should be completed before we take automated action
define( "PAYPAL_ATTEND", "you@yourdomain.com" ); # Address to send notifications generated by this code to
As always, when recieving input, even though it's from what you would expect to be a safe source, you should sanitize the input, if you're using mysql in any way then best mysql_real_escape_string, I'll use stripslashes if magic_quotes are on...
PHP:
if( get_magic_quotes_gpc() ) $_REQUEST = array_map( 'stripslashes', $_REQUEST );
Next, we open a socket to paypal, on port 80 ( assuming you dont have a way to support SSL here, infact you should use ssl where available, as that's not generic I won't )
PHP:
if( count( $_REQUEST ) )
{
if( ( $sock = fsockopen( PAYPAL_WEBSCR, 80, $errno, $errstr, 5 ) ) ) # Open a socket to paypal
{
$post[ ] = "cmd=_notify-validate"; # Append what we're doing here
foreach( $_REQUEST as $k => $v ) $post[ ] = sprintf( "%s=%s", $k, urlencode( $v ) ); # Build postback
$post = implode( '&', $post ); # Finalize postback
$request[ ] = "POST /cgi-bin/webscr HTTP/1.0"; # POST request
$request[ ] = sprintf( "Content-length: %d", strlen( $post ) ); # Length of final postback
$request[ ] = "Content-type: application/x-www-form-urlencoded"; # Duh
$request[ ] = "" ; # HTTP Compliant post request
$request[ ] = $post ; # Final post string
fwrite( $sock, sprintf( "%s\r\n", implode("\r\n", $request ) ) ); # Write postback to paypal
while( !feof( $sock ) ) $response .= fgets( $sock ); # Get response from paypal server
fclose( $sock ); # Close socket
}
As mentioned I check _REQUEST has data, we then attempt to open a socket ( actually a file descriptior to a socket ).
Before we build a request to send to paypal, we need to tell paypal what we're doing, this is IMPORTANT
PHP:
$post[ ] = "cmd=_notify-validate";
Next, we build the request to send to paypal, we use an array just to make code neater and less prone to errors because you forgot a . before the assignment operator
PHP:
foreach( $_REQUEST as $k => $v ) $post[ ] = sprintf( "%s=%s", $k, urlencode( $v ) );
PHP:
$post = implode( '&', $post );
Next, we build a standard HTTP POST header to write to our socket, as always the first line is
PHP:
$request[ ] = "POST /cgi-bin/webscr HTTP/1.0";
PHP:
$request[ ] = sprintf( "Content-length: %d", strlen( $post ) );
PHP:
$request[ ] = "Content-type: application/x-www-form-urlencoded";
PHP:
$request[ ] = ""
PHP:
$request[ ] = $post
PHP:
fwrite( $sock, sprintf( "%s\r\n", implode("\r\n", $request ) ) );
Paypal will now respond with a result, so
PHP:
while( !feof( $sock ) ) $response .= fgets( $sock );
and then we
PHP:
fclose( $sock ); # Close socket
To make it simple
PHP:
foreach( $_REQUEST as $k => $v ) $email[ ] = sprintf( "%s:\t%s", $k, urldecode( $v ) );
$email[ ] = sprintf( "RAW RESPONSE:\n%s\n", $response )
PHP:
$email = implode("\n", $email );
Now, what we're looking for in the $response is the word "VERIFIED", in capital letters just like that, so we're going to start testing the response and taking action accordingly...
PHP:
if( ereg( "VERIFIED", $response ) ) # Ensure the customer was verified
{
if( strtolower( $_REQUEST['payment_status'] ) == PAYPAL_REQUIRED_STATUS ) # Check for completed payment, echeck etc dont' complete instantly
{
if( $_REQUEST['receiver_email'] == PAYPAL_ADDRESS ) # Check for correct email address
{
if( $_REQUEST['mc_currency'] == PAYPAL_CURRENCY ) # Check for correct currency
{
}
else mail( PAYPAL_ATTEND, "Non standard currency used", $email );
}
else mail( PAYPAL_ATTEND, "Payment to wrong address", $email );
}
else mail( PAYPAL_ATTEND, "Non completed payment", $email );
}
else mail( PAYPAL_ATTEND, "Suspicious payment recieved", $email );
Now, I've written as much as I can, the rest is up to you, and your site, the place to put your successfull handling code is
PHP:
if( $_REQUEST['mc_currency'] == PAYPAL_CURRENCY )
{
// your site specific code here
}
else mail( PAYPAL_ATTEND, "Non standard currency used", $email );
PHP:
<?php
define( "PAYPAL_WEBSCR", "www.paypal.com" ); # Server to use, should normally be www.paypal.com
define( "PAYPAL_ADDRESS", "you@yourdomain.com" ); # paypal address expected
define( "PAYPAL_CURRENCY", "USD" ); # Currency of transaction
define( "PAYPAL_REQUIRED_STATUS", "completed" ); # All payments should be completed before we take automated action
define( "PAYPAL_ATTEND", "warning@yourdomain.com" ); # Address to send notifications generated by this code to
if( get_magic_quotes_gpc() ) $_REQUEST = array_map( 'stripslashes', $_REQUEST );
if( count( $_REQUEST ) )
{
if( ( $sock = fsockopen( PAYPAL_WEBSCR, 80, $errno, $errstr, 5 ) ) ) # Open a socket to paypal
{
$post[ ] = "cmd=_notify-validate"; # Append what we're doing here
foreach( $_REQUEST as $k => $v ) $post[ ] = sprintf( "%s=%s", $k, urlencode( $v ) ); # Build postback
$post = implode( '&', $post ); # Finalize postback
$request[ ] = "POST /cgi-bin/webscr HTTP/1.0"; # POST request
$request[ ] = sprintf( "Content-length: %d", strlen( $post ) ); # Length of final postback
$request[ ] = "Content-type: application/x-www-form-urlencoded"; # Duh
$request[ ] = "" ; # HTTP Compliant post request
$request[ ] = $post ; # Final post string
fwrite( $sock, sprintf( "%s\r\n", implode("\r\n", $request ) ) ); # Write postback to paypal
while( !feof( $sock ) ) $response .= fgets( $sock ); # Get response from paypal server
fclose( $sock ); # Close socket
}
foreach( $_REQUEST as $k => $v ) $email[ ] = sprintf( "%s:\t%s", $k, urldecode( $v ) ); # Build an email for later
$email[ ] = sprintf( "RAW RESPONSE:\n%s\n", $response ); # Append raw response to email
$email = implode("\n", $email ); # Finalize email
if( ereg( "VERIFIED", $response ) ) # Ensure the customer was verified
{
if( strtolower( $_REQUEST['payment_status'] ) == PAYPAL_REQUIRED_STATUS ) # Check for completed payment, echeck etc dont' complete instantly
{
if( $_REQUEST['receiver_email'] == PAYPAL_ADDRESS ) # Check for correct email address
{
if( $_REQUEST['mc_currency'] == PAYPAL_CURRENCY ) # Check for correct currency
{
// your site specific code here
}
else mail( PAYPAL_ATTEND, "Non standard currency used", $email );
}
else mail( PAYPAL_ATTEND, "Payment to wrong address", $email );
}
else mail( PAYPAL_ATTEND, "Non completed payment", $email );
}
else mail( PAYPAL_ATTEND, "Suspicious payment recieved", $email );
mail( PAYPAL_ATTEND, "TEST", $email );
}
?>