6.2 Getting a Transaction Notification

When an order is processed, PayPal generates a notification that is then posted to the URL specified in the form as the hidden field notify_url. This is an example of a web service that relies on ordinary HTTP?no fancy SOAP or other RPC underpinnings.

Using a simple HTTP request/response is perhaps the most basic, universal real world web service. It works with virtually every programming language and requires no special configuration to use. It's a classic case of the simple solution being the best solution.

Example 6-2 shows the JSP used to receive notifications from the PayPal server.

Example 6-2. Notification JSP
<%@ page contentType="text/html; charset=iso-8859-1" 

language="java" import="com.cascadetg.ch06.*" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 



new PayPalReciept( ).handleRequest(request, response); 



The line response.setStatus(200) notifies PayPal that the notification has successfully been handled. By default, PayPal resends the notification until it gets a status code of 200 back, indicating that the transmission was successful. Disabling this line or substituting a different response code is an easy way to generate additional test load for your server without having to manually enter a lot of transactions.

The bulk of the work for this application is handled by supporting Java classes, as shown in Figure 6-5.

Figure 6-5. PayPal and Fax classes

The first class, shown in Example 6-3, exposes one main method, handleRequest(), to the JSP page. In the handleRequest( ) method, the first thing to be done is verify that the data sent by PayPal is correct with the verifyRequest( ) method. The verifyRequest( ) method retrieves the parameters sent by PayPal and then posts them back to PayPal via an HTTPS connection, with an additional parameter added (cmd=_notify-validate).

Example 6-3. A PayPal receipt
package com.cascadetg.ch06;

import java.util.*;

import java.net.*;

import java.io.*;

import javax.servlet.http.*;

import javax.mail.*;

import javax.mail.internet.*;

public class PayPalReciept


    // Used to store retrived values from the PayPal submission.

    String paymentStatus;

    String txnId;

    String receiverEmail;

    String payerEmail;

    private static final boolean debug = false;

    // System.getProperty line to get the proper new line character[s].

    String newLine = System.getProperty("line.separator", ".");

    // Keep track of sent transactions. Note that this is in-memory

    // storage only - for a "real" system you would want to persist

    // this information, as well as the rest of fields of this object.

    // (mostly likely to a database of some sort).

    private static Hashtable processedTxnId = new Hashtable( );

    // This method takes an incoming request and validates it both

    // against the PayPal server and some internal logic.

    public boolean validateRequest(HttpServletRequest request)




            // Read the post from PayPal system.

            Enumeration parameters = request.getParameterNames( );

            // We then add a "cmd" attribute to send back to PayPal

            // to indicate that we want to validate the request.

            StringBuffer send =

                new StringBuffer("cmd=_notify-validate");

            // Here, we put all of the parameters passed in from the

            // PayPal notification POST.

            while (parameters.hasMoreElements( ))


                String paramName = (String)parameters.nextElement( );

                String paramValue = request.getParameter(paramName);






            if (debug)

                System.out.println(send.toString( ));

            // This next sequence opens a connection to the PayPal

            // server, sets up the connection, and writes the sent

            // parameters back to the PayPal server.

            URL paypalServer =

                new URL("https://www.paypal.com/cgi-bin/webscr");

            URLConnection paypalConnection =

                paypalServer.openConnection( );





            PrintWriter paypalServerWriter =

                new PrintWriter(paypalConnection.getOutputStream( ));


            paypalServerWriter.close( );

            if (debug)

                System.out.println("Sent to PayPal server.");

            // We then need to read the response from the PayPal

            // server.

            BufferedReader in =

                new BufferedReader(

                    new InputStreamReader(

                        paypalConnection.getInputStream( )));

            String paypalResponse = in.readLine( );

            in.close( );

            if (debug)


                    "Read PayPal server response = " + paypalResponse);

            // Set the values of this object from the values sent

            // by the initial request. If these values are verified,

            // we'll want them for later. If the values aren't

            // verified, or something else is wrong, we'll want

            // to track them for logging purposes.


            // If everything is ok, the response back should be

            // VERIFIED. Otherwise, something went wrong.

            if (paypalResponse.equals("VERIFIED"))


                // If it isn't completed, it's a status message of

                // some sort. We're only interested in Completed

                // payments.

                if (!paymentStatus.equals("Completed"))

                    return false;

                // Make sure that we are the actual recipients of

                // the money.

                if (receiverEmail


                    != 0)

                    return false;

                // Check the in-memory cache to verify that we

                // haven't already handled this transaction.

                if (processedTxnId.get(this.getTxnId( )) != null)

                    return false;

                // Everything looks good, so let's add this to the

                // transaction cache.

                processedTxnId.put(this.getTxnId( ), this.getTxnId( ));

                return true;

            } else


                System.out.println("Invalid PayPal transaction!");

                System.out.println(this.toString( ));

                return false;


        } catch (Exception e)


            System.out.println("Unable to connect to PayPal server.");

            e.printStackTrace( );

            return false;



    // "Flatten" the object to a String.

    public String toString( )


        StringBuffer output = new StringBuffer( );

        Enumeration outEnum = this.getAttributes( ).keys( );

        while (outEnum.hasMoreElements( ))


            String outputStr = (String)outEnum.nextElement( );


            output.append(" : ");

            output.append(paypalAttributes.get(outputStr).toString( ));



        return output.toString( );


    public String toHTMLString( )


        StringBuffer htmlString = new StringBuffer( );


        htmlString.append("<TABLE HEIGHT='100%' WIDTH='100%'>");


        Enumeration myValues = this.getAttributes( ).keys( );

        while (myValues.hasMoreElements( ))


            String next = (String)myValues.nextElement( );


            htmlString.append(" : ");

            htmlString.append(this.getAttribute(next).toString( ));





        return htmlString.toString( );


    // PayPal can send a variety of attributes back as part of a

    // transaction. We're interested in all of them, so we'll note

    // them all.

    Hashtable paypalAttributes = new Hashtable( );

    public String getAttribute(String attribute)


        return (String)paypalAttributes.get((String)attribute);


    public Hashtable getAttributes( )


        return paypalAttributes;



     * Reads the incoming values and fills out the object. Notice that

     * we are also recording the incoming IP address - if someone is

     * sending fake requests, the IP address can be an important bit of

     * information.


     * @param request


    private void setValues(HttpServletRequest request)


        paypalAttributes = new Hashtable(request.getParameterMap( ));

        Enumeration attributes = request.getParameterNames( );

        while (attributes.hasMoreElements( ))


            String temp = (String)attributes.nextElement( );

            paypalAttributes.put(temp, request.getParameter(temp));




            request.getRemoteAddr( ).toString( ));

        paymentStatus = request.getParameter("payment_status");

        txnId = request.getParameter("txn_id");

        receiverEmail = request.getParameter("receiver_email");

        payerEmail = request.getParameter("payer_email");



     * The main entry point from the JSP page request. We'll look at

     * the request and validate it, and depending on the results, we'll

     * either send a notification email OR a fax and a notification

     * email.


    public void handleRequest(

        HttpServletRequest request,

        HttpServletResponse response)


        if (validateRequest(request))


            if (debug)

                System.out.print("Sending fax... " + this.toString( ));

            FaxSender myFaxSender = new FaxSender( );

            myFaxSender.setText(this.toHTMLString( ));


            if (debug)

                System.out.print("fax prepped...");

            myFaxSender.sendFax( );



                myFaxSender.getResult( ).toString( ));

            if (debug)

                System.out.println("Fax sent.");


        sendEmail(this.toString( ));


    // These are the usual retrieval methods a'la the JavaBean

    // patterns. Notice that there are only retrieval methods - no

    // setters are provided.

    public String getPayerEmail( )


        return payerEmail;


    public String getPaymentStatus( )


        return paymentStatus;


    public String getReceiverEmail( )


        return receiverEmail;


    /** Returns the transaction ID (aka TxnId) */

    public String getTxnId( )


        return txnId;


    public void sendEmail(String text)




            java.util.Properties myProperties = new Properties( );

            myProperties.put("mail.smtp.host", PayPalTokens.mailhost);

            myProperties.put("mail.smtp.auth", "true");

            Session mySession = Session.getInstance(myProperties);


            Transport myTransport = mySession.getTransport("smtp");





            Message myMessage =

                new javax.mail.internet.MimeMessage(mySession);


                new InternetAddress(


                    "PayPal Listener"));



                new InternetAddress(PayPalTokens.paypalEmail));

            myMessage.setSubject("PayPal Notification");

            myMessage.setContent(this.toHTMLString( ), "text/html");

            Address[] temp =

                { new InternetAddress(PayPalTokens.paypalEmail)};


            myMessage.saveChanges( );



                myMessage.getAllRecipients( ));

            myTransport.close( );

        } catch (Exception e)


            e.printStackTrace( );



    public static void main(String[] args)


        (new PayPalReciept( )).sendEmail("test");



If PayPal doesn't respond with a VERIFIED token, it's possible that someone is attempting to forge a transaction. If you see this occur in a real world environment, you should take immediate, aggressive steps to deal with this; it may be an attempt by a hacker to steal funds or even your identity.