NO MORE CAREER
POLITICIANS!
Get Out Of Our House: Replacing congress with TRUE citizens!
Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Web Hosting

best web hosting - top web hosting sites, thetop10bestwebhosting.com

Czech your Page Rank!
Check Page Rank of any web site pages instantly:
This free page rank checking tool is powered by Page Rank Checker service
Surf's Up!
Visit Egosurf.org and massage YOUR web ego!
My Score: 9,001
Doug's Books

Read (and recommend)

  • Men are from Mars, Women are from Venus
  • The Wisdom of Crowds: Why the Many Are Smarter Than the Few and How Collective Wisdom Shapes Business, Economies, Societies and Nations
  • Blink: The Power of Thinking Without Thinking
  • Head First Design Patterns
  • Transact-SQL Programming
  • What's So Amazing About Grace?
  • Just So Stories (Rudyard Kipling collection)

Reading

  • Prayer: Does it Make Any Difference?
  • Data Mining (Practical Machine Learning Tools and Techniques)
<< September, 2010 >>
SMTWTFS
1234
567891011
12131415161718
19202122232425
2627282930
Search Blog

Recent Comments
Re: Railo 3.1 on Windows Server 2008 and IIS7 - Part 3 of 3 (by Jon at 8/27 2:04 PM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Spacy at 8/24 3:46 PM)
Re: THE DAY CFUNITED DIED (by ComboFusion at 8/23 10:50 AM)
Re: My Grandpa (by Tasha at 8/10 4:29 PM)
Re: Just What IS a 'Service Layer', Anyway? (by dougboude at 8/02 10:10 AM)
Re: Just What IS a 'Service Layer', Anyway? (by Isaac at 8/02 2:25 AM)
Re: PayPal IPN Coldfusion CFC (by Soyestudiambre at 7/25 6:12 PM)
Re: PHP vs COLDFUSION (by Tony Garcia at 7/17 11:24 AM)
Re: PHP vs COLDFUSION (by dougboude at 7/14 8:45 AM)
Re: PHP vs COLDFUSION (by Lola LB at 7/14 5:51 AM)
Categories
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

30 November 2009
PayPal IPN Coldfusion CFC
ppIPN
Thursday morning I overheard our finance lady mentioning an online form she had created to handle registrations for a conference we're hosting in a few months. My curiosity piqued (since usually things like this end up on my plate, at least in part), I inquired further and found that she was also including a "Pay Now" PayPal button so the registrants could complete the process. I decided to make some work for myself and suggested that we save the registration information to our database (as opposed to just emailing form content to designated people, as was the plan), and leveraging PayPal's Instant Payment Notification (IPN) service to automatically capture payment information as it happens.

In a nutshell, PayPal's IPN works like this:

1. A customer creates an order record on your site and is then sent to PayPal to provide payment for the goods or services they are purchasing from you;
2. Upon completion of their payment, a "silent ping" takes place from PayPal to the URL of your choice. This ping contains several pieces of information about the transaction.
3. The code at the URL you told PayPal to ping then pings PayPal back again with the exact same information posted to it; If the information you sent back is valid, PayPal will respond simply with the word "Valid"; if the information you pinged back was NOT valid, PayPal will respond with "Invalid".
4. If your response from PayPal was "Valid", you should feel good about proceeding with the update of your customer's purchase record in the database using the information contained in the original ping; If the response was "Invalid", handle that as you will.


Even though the process is fairly simple, writing the code to deal with IPN can be somewhat challenging, made mostly so by the fact that it's an "invisible ping". Troubleshooting any kind of asynchronous process (which is what IPN is) requires special approaches. So, since I already went through the process to produce what I call "ppIPN.cfc", I figured I'd go ahead and share it (if you want, you can skip directly to the download). I tried to keep it easily configurable to accommodate different apps, but at the very least it's a good set of starter code if it doesn't meet your needs as is.

ppIPN.cfc actually consists of TWO CFCs: ppIPN.cfc and ppSettings.cfc, which ppIPN extends. As you probably guessed, ppSettings is where you will put the different variables that affect how your implementation of ppIPN will work. I made it a separate CFC so that you didn't have to maintain potentially sensitive information within the one component, and so that you could put ppSettings wherever your heart desires.

USE

Note:
ppIPN assumes three things:
1. that a record for a customer already exists in your table, and the payment notification ping from PayPal is for the purpose of updating that existing record with transaction details;
2. that you are sending the ID of your customer's transaction record to PayPal by using PayPal's "CUSTOM" form field (<input type="hidden" name="custom" value="16" /> or ...&custom=16&....)
3. your cfmail tag doesn't require a mail server, username, or password to be specified (they are already set up in your cfadmin)

To use ppIPN, simply place it and the corresponding ppSettings.cfc on your webserver where they can be called via http. Adjust the 'extends' path in ppIPN.cfc if you located your ppSettings.cfc anyplace other than the same folder where ppIPN.cfc lives.

Next, edit your PayPal account to enable IPN and enter the following URL as your post back URL:

http://[your web server]/ppIPN.cfc?method=postback

Lastly, edit your ppSettings.cfc to accurately reflect your environment.

That's it!

Here are the settings and a sample:

dsn - name of the datasource you wish to use
receiverEmail - the primary email address associated with your paypal account. (Though there can be several email addresses associated with your account, it is the primary email address that will be included in the IPN call from paypal, regardless of which address the payment was actually sent to)
notifyEmails - comma delimited list of email addresses you wish for notifications and status messages to go to
adminemail - the email address that will be used in the FROM of emails sent by ppIPN
ppURL - the url to use when re-posting data back to PayPal for verification

tblPayment - name of the table you're using to store transaction information
fldID - the name of the identity field for the table named above. ppINC assumes this will be an autoincrementing integer
fldFirstName - the name of the first name field in the tblPayment table. This will be the purchaser's first name.
fldLastName - the name of the last name field in the tblPayment table. This will be the purchaser's last name.
fldTransactionID - the name of the field in tblPayment in which you will be storing PayPal's transaction ID value. Should be varchar.
fldPaymentAmount - the name of the field in tblPayment in which to store the payment amount (PayPal's 'mc_gross' variable value). ppINC assumes this field is of type DOUBLE
fldPaymentDate - name of the field in tblPayment to store the transaction date. ppINC assumes type DATETIME


Here is a sample ppSettings.cfc:
<cfcomponent output="false">
    <cfscript>
        instance = structnew();
        instance.settings.dsn = "absolutezero";
        instance.settings.receiverEmail = "sclause@northpole.com";
        instance.settings.notifyEmails = "sclause@northpole.com,rreindeer@northpole.com";
        instance.settings.adminemail = "administrator@northpole.com";
        instance.settings.ppURL = "https://www.paypal.com/cgi-bin/webscr";
       
        //DATABASE SETTINGS
        instance.settings.tblPayment = "REGISTRATIONS";
        instance.settings.fldID = "ID";
        instance.settings.fldFirstName = "FIRSTNAME";
        instance.settings.fldLastName = "LASTNAME";
        instance.settings.fldTransactionID = "PPTXID";
        instance.settings.fldPaymentAmount = "PMTAMOUNT";
        instance.settings.fldPaymentDate = "REGDATE";
    </cfscript>
</cfcomponent>


TESTING
Here is a sample form that can be used to test your installation of ppIPN outside of using a PayPal Sandbox (feel free to add additional PayPal fields, but currently these are the only ones ppIPN looks for). To ppIPN, this form's post appears the same as a post from PayPal:

<html>
    <head>
        <title>
       
        </title>
    </head>
    <body>
        <form action="ppIPN.cfc?method=postback" method="post">
            <input type="hidden" name="payment_status" value="completed" />
            <input type="hidden" name="mc_gross" value="155.00" />
            <input type="hidden" name="txn_id" value="asdfsdgerweresfggzz" />
            <input type="hidden" name="receiver_email" value="rniemeier@masonclaims.com" />
            <input type="hidden" name="payer_email" value="dougboude@gmail.com" />
            <input type="hidden" name="custom" value="16" />
            <br>
            <input type="submit" value="submit" />
        </form>
    </body>
</html>


HOW PPIPN WORKS
1. Your customer performs a checkout on your site, creating a record in your transaction table without a payment status. Via whatever mechanism you select, they are sent to PayPal to complete the transaction, along with the ID of their record tucked within the "custom" field. VERY IMPORTANT: DO pass along the ID for the transaction record in the CUSTOM field!
2. Customer completes their transaction on PayPal. PayPal pings ppIPN with several different values.
3. ppIPN captures the incoming values, arranges them into a query string, and posts them back to PayPal.
4. PayPal validates the values ppIPN passed back and responds with either "Valid" or "Invalid".
5. If "Invalid" is received, ppIPN wraps up all the relevant values received and emails them to the addresses specified in the "notifyEmails" setting.
6. If "Valid" is received, ppIPN performs several additional checks:
  is the status 'Completed'?
  does this a unique transaction id that does not already exist in the db?
  is the incoming receiver email value equal to that specified in the settings?
If the answer to the above is 'yes', we proceed to update the db. Otherwise, we put together a nice status email and send it out.


ADDITIONAL VALUES FROM PAYPAL

There are many variables that get returned to you via IPN calls from PayPal; ppIPN only captures and saves a few of them. To find out more about the other variables available to you, you can visit this page on PayPal's site: http://bit.ly/paypalIPN

If you wish to capture more values than what ppIPN is currently capturing, it will require some editing of the cfc. To save you some time, here are some hints on what you'll need to touch should you desire to alter what data is currently persisted:

1. Edit ppSettings.cfc and add the additional fields you want to capture (use existing settings as a guide);

2. Edit the query in ppIPN.getPaymentRecord; add your additional fields to the SELECT clause (using the variable names, not the variable values); Be SURE to alias those fields!

3. Add code in ppIPN.doValid, just below line 113, to set the values you wish to capture. In this area, you will utilize the Alias values you created in step 2. Note: All incoming PayPal variables are initially captured to the global structure "instance.incomingArgs", so any and all values that arrived in the call will be there.

Here is how to capture a new value for persistence:
<!--- set this record's value for txn_type... --->
<cfset existingPaymentRecord.tran_type = instance.incomingArgs.txn_type />

In this example, tran_type is the alias we gave to our transaction type field. The corresponding value we want to save coming from PayPal was captured in the instance.incomingArgs.txn_type field. txn_type is the name PayPal provided, and can be found in the IPN documentation.

4. Edit ppIPN.updatePaymentRecord, altering the query to include your new fields. You can use the existing field/value pairs as an example for adding additional ones.

That's it!

Hope this saves someone a little time.

Download the ppIPN ZIP



Posted by dougboude at 11:11 AM | PRINT THIS POST! |Link | 6 comments
Subscription Options

You are not logged in, so your subscription status for this entry is unknown. You can login or register here.

Re: PayPal IPN Coldfusion CFC
Thanks for the code...I've been looking for something like this for 2 months. With that said, everytime i use their test harness and do an IPN test, it always comes back with Invalid. Have you experienced something like this? I haven't modified your code in relation to that. I can't figure it out.
Posted by AJ on December 16, 2009 at 1:53 AM

Re: PayPal IPN Coldfusion CFC
@AJ - I have had issues in the past with paypal's test harness returning invalid no matter WHAT I tried; I honestly don't have an answer for it, and often have to resort to (here comes blasphemy) performing real transactions of 1 cent to myself in order to test. With the exact same code sets, I have had the test sandbox return invalid, and the real account return valid.

Sorry I don't have more info for you!

Doug
Posted by dougboude on December 16, 2009 at 3:03 AM

Re: PayPal IPN Coldfusion CFC
Thank you! I will go ahead and try a real transaction...here I go!
Posted by AJ on December 16, 2009 at 9:00 PM

Re: PayPal IPN Coldfusion CFC
Doug, thanks for posting this! You did save me some time. One thing tho - it was not clear to me where to put the postback url until I was troubleshooting - but u made it sound so easy so I knew it had to be something stupid. Low and behold, the "postback" url MUST BE the IPN "Notification URL" in PayPal where you can enable/disable IPN (Profile > More > IPN). At first I was placing the postback url under the Website Payment Preferences "Return URL" - which is incorrect. Here you can place whatever you want to be auto directed after a trans is completed. As soon as I corrected this, it worked! AWESOME! Thanks again! Jason
Posted by Jason on December 18, 2009 at 1:40 AM

Re: PayPal IPN Coldfusion CFC
Hey Doug,

I loved this post! Thanks for writing some sweet code to help ease some of the PayPal headaches. I was having a problem inserting a record before posting to PayPal. I was getting the post to a blank page problem, but I figured out a cfajaxproxy call mixed with jquery to fix the problem. I just thought I would leave a link to my blog about this here so other could get some help on this topic as well if they needed to.
Thanks again for the code!

Joel

http://www.wecodethings.com/blog/?p=63
Posted by Joel Hill on December 24, 2009 at 4:43 PM

Re: PayPal IPN Coldfusion CFC
Excelent information and thanks for the codes! Im using it soon :)
http://soyestudiambre.com
Posted by Soyestudiambre on July 25, 2010 at 6:12 PM

Name:   Required
Email:   Required your email address will not be publicly displayed.

Want to receive notifications when new comments are added? Login/Register for an account.

Time to take the Turing Test!!!

4 plus 9 equals
Type in the answer to the question you see above:

Your comment:

Sorry, no HTML allowed!