Closed Thread
Results 1 to 14 of 14

Thread: Automating website payments with Paypal IPN

  1. #1
    stop staring krakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to behold krakjoe's Avatar
    Join Date
    May 2006
    Location
    UK
    Posts
    3,616

    Automating website payments with Paypal IPN

    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

    PHP Code:
    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 
    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...

    PHP Code:
    if( get_magic_quotes_gpc() ) $_REQUEST array_map'stripslashes'$_REQUEST ); 
    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 )

    PHP Code:
    if( count$_REQUEST ) )
    {
        if( ( 
    $sock fsockopenPAYPAL_WEBSCR80$errno$errstr) ) ) # Open a socket to paypal
        
    {
            
    $post[ ] = "cmd=_notify-validate"# Append what we're doing here
            
            
    foreach( $_REQUEST as $k => $v $post[ ] = sprintf"%s=%s"$kurlencode$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$socksprintf"%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
        

    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

    PHP Code:
    $post[ ] = "cmd=_notify-validate"
    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

    PHP Code:
    foreach( $_REQUEST as $k => $v $post[ ] = sprintf"%s=%s"$kurlencode$v ) ); 
    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

    PHP Code:
    $post implode'&'$post ); 
    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

    PHP Code:
    $request[ ] = "POST /cgi-bin/webscr HTTP/1.0"
    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

    PHP Code:
    $request[ ] = sprintf"Content-length: %d"strlen$post ) ); 
    As it's a post request, you should also

    PHP Code:
    $request[ ] = "Content-type: application/x-www-form-urlencoded"
    When you post data to a server there should be a newline between headers and the post data, so

    PHP Code:
    $request[ ] = "" 
    Then the post string from $post

    PHP Code:
    $request[ ] = $post 
    Next we write the headers and post request to the socket with

    PHP Code:
    fwrite$socksprintf"%s\r\n"implode("\r\n"$request ) ) ); 
    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

    PHP Code:
    while( !feof$sock ) ) $response .= fgets$sock ); 
    $response now contains both the response headers and body from paypal.

    and then we

    PHP Code:
    fclose$sock ); # Close socket 
    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

    PHP Code:
    foreach( $_REQUEST as $k => $v $email[ ] = sprintf"%s:\t%s"$kurldecode$v ) );
    $email[ ] = sprintf"RAW RESPONSE:\n%s\n"$response 
    This gets everything that's happening into our email array, including paypals response.

    PHP Code:
    $email implode("\n"$email ); 
    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...

    PHP Code:
    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 
    mailPAYPAL_ATTEND"Non standard currency used"$email );
                }
                else 
    mailPAYPAL_ATTEND"Payment to wrong address"$email );
            }
            else 
    mailPAYPAL_ATTEND"Non completed payment"$email );
        }
        else 
    mailPAYPAL_ATTEND"Suspicious payment recieved"$email ); 
    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

    PHP Code:
    if( $_REQUEST['mc_currency'] == PAYPAL_CURRENCY )
    {
                        
    // your site specific code here
    }
    else 
    mailPAYPAL_ATTEND"Non standard currency used"$email ); 
    and the final script look like...

    PHP Code:
    <?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 fsockopenPAYPAL_WEBSCR80$errno$errstr) ) ) # Open a socket to paypal
        
    {
            
    $post[ ] = "cmd=_notify-validate"# Append what we're doing here
            
            
    foreach( $_REQUEST as $k => $v $post[ ] = sprintf"%s=%s"$kurlencode$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$socksprintf"%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"$kurldecode$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 
    mailPAYPAL_ATTEND"Non standard currency used"$email );
                }
                else 
    mailPAYPAL_ATTEND"Payment to wrong address"$email );
            }
            else 
    mailPAYPAL_ATTEND"Non completed payment"$email );
        }
        else 
    mailPAYPAL_ATTEND"Suspicious payment recieved"$email );

        
    mailPAYPAL_ATTEND"TEST"$email );
    }
    ?>
    (\__/) Joe Watkins
    (='.'=) Software Architect
    (")_(") http://pthreads.org
    Copy and paste bunny into your sig, help him gain world domination.

  2. #2
    Sup, Recoil here. themoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond reputethemoose has a reputation beyond repute themoose's Avatar
    Join Date
    Jun 2005
    Location
    Uzbekistan
    Posts
    8,805
    IPN is such an important tool in any e-commerce, used it a few times but this looks like one of the best I've seen (unsurprisingly).

  3. #3
    Jay Street iBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond reputeiBrightDev has a reputation beyond repute iBrightDev's Avatar
    Join Date
    Oct 2005
    Location
    Not sure, need a GPS.
    Posts
    7,125
    ah, yes, the wonderful IPN. i would have posted something like this sooner if i would have thought of it first. i am currently using IPN for a clients site, and it is great. nice script Joe.
    Full-service digital agency based in Scottsdale, Arizona - iBright Development

  4. #4
    NLC Starcraftmazter has disabled reputation
    Join Date
    Dec 2005
    Location
    Australia
    Posts
    1,276
    Sure beats my IPN script. Though not by much

    Excellent guide krakjoe.
    Liway - One of the Best Free, Paid & Reseller Hosts
    http://www.liway.com

  5. #5
    Pro Member [AS]Richard has disabled reputation [AS]Richard's Avatar
    Join Date
    Jun 2008
    Location
    United States
    Posts
    260
    Very nice script!
    Maybe I'll use this for my hosting...

  6. #6
    The Hosting Tool JonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to all JonnyH's Avatar
    Join Date
    Oct 2006
    Location
    UK
    Posts
    2,616
    Joe, this is fantastic, could I use it for a Paypal module in cP Creator?
    Ofcourse credits will be given.
    ~ The Hosting Tool ~ cPanel WHM/DA Support! cP Creator Importer! Post 2 Host! Fully Automated! ~ thehostingtool.com

  7. #7
    stop staring krakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to behold krakjoe's Avatar
    Join Date
    May 2006
    Location
    UK
    Posts
    3,616
    You certainly could, in it's current form, you'd need to invoice users for payment using your own code and this to handle the actual payment, although you could just as easily, read a little bit about paypal, make it do subscriptions and handle cancellations and refunds etc automatically...

    Anything I write in a forum is public domain so you can do whatever you want with it, although if I helped in some way credit is always nice
    (\__/) Joe Watkins
    (='.'=) Software Architect
    (")_(") http://pthreads.org
    Copy and paste bunny into your sig, help him gain world domination.

  8. #8
    The Hosting Tool JonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to all JonnyH's Avatar
    Join Date
    Oct 2006
    Location
    UK
    Posts
    2,616
    What can I say, credit given when credit deserved.

    I've already got a sandbox account on paypal which I'll read up upon and play about with, I'll take a look at the invoices aswell and see what I can do with those.
    ~ The Hosting Tool ~ cPanel WHM/DA Support! cP Creator Importer! Post 2 Host! Fully Automated! ~ thehostingtool.com

  9. #9
    stop staring krakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to behold krakjoe's Avatar
    Join Date
    May 2006
    Location
    UK
    Posts
    3,616
    The paypal sandbox is the worst place in the world, up until recently I would use eliteweaver's ipntest script, only that seems to be offline which is crap because that only leaves the sandbox, and like I said there's no worse place to be, with all the logging in and out and timing in and out and oh ... my ... god ...
    (\__/) Joe Watkins
    (='.'=) Software Architect
    (")_(") http://pthreads.org
    Copy and paste bunny into your sig, help him gain world domination.

  10. #10
    The Hosting Tool JonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to all JonnyH's Avatar
    Join Date
    Oct 2006
    Location
    UK
    Posts
    2,616
    With a quick google search I came across this one:
    http://www.belahost.com/pp/index.php

    Don't know if that's the same thing.. looks useful.
    ~ The Hosting Tool ~ cPanel WHM/DA Support! cP Creator Importer! Post 2 Host! Fully Automated! ~ thehostingtool.com

  11. #11
    stop staring krakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to behold krakjoe's Avatar
    Join Date
    May 2006
    Location
    UK
    Posts
    3,616
    That is the same thing, nice find ...
    (\__/) Joe Watkins
    (='.'=) Software Architect
    (")_(") http://pthreads.org
    Copy and paste bunny into your sig, help him gain world domination.

  12. #12
    The Hosting Tool JonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to all JonnyH's Avatar
    Join Date
    Oct 2006
    Location
    UK
    Posts
    2,616
    Question, if a subscription is completed, would the same code with handles the invoice completion work?
    ~ The Hosting Tool ~ cPanel WHM/DA Support! cP Creator Importer! Post 2 Host! Fully Automated! ~ thehostingtool.com

  13. #13
    stop staring krakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to beholdkrakjoe is a splendid one to behold krakjoe's Avatar
    Join Date
    May 2006
    Location
    UK
    Posts
    3,616
    The same sort of idea, only subscriptions use different variables and there are more checks to do, there's a pdf on the paypal website documenting the variables and their usage, I'd read that, I don't really wanna confuse this post with that information, plus I'd just be copying and pasting.
    (\__/) Joe Watkins
    (='.'=) Software Architect
    (")_(") http://pthreads.org
    Copy and paste bunny into your sig, help him gain world domination.

  14. #14
    The Hosting Tool JonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to allJonnyH is a name known to all JonnyH's Avatar
    Join Date
    Oct 2006
    Location
    UK
    Posts
    2,616
    Quote Originally Posted by krakjoe View Post
    The same sort of idea, only subscriptions use different variables and there are more checks to do, there's a pdf on the paypal website documenting the variables and their usage, I'd read that, I don't really wanna confuse this post with that information, plus I'd just be copying and pasting.
    Thanks, that's fair enough. I used that PDF earlier for the buttons used and some return variables, custom values for security checks.

    I'll check through it again Joe. Thanks.
    ~ The Hosting Tool ~ cPanel WHM/DA Support! cP Creator Importer! Post 2 Host! Fully Automated! ~ thehostingtool.com

Closed Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts