Recent Entries
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!
You may also be interested in...
Web Hosting

<< November, 2009 >>
SMTWTFS
1234567
891011121314
15161718192021
22232425262728
2930
Search Blog

Recent Comments
Re: November 7th, 2012 - Day 1 of the Restoration of America's Greatness (by dougboude at 5/04 9:47 AM)
Re: November 7th, 2012 - Day 1 of the Restoration of America's Greatness (by Simon Magnus at 5/04 9:24 AM)
Re: Providing for Service Layer Objects in CFWheels: Hello Wirebox! (by Brad at 5/03 11:37 AM)
Re: PayPal IPN Coldfusion CFC (by No at 4/29 9:28 AM)
Re: Java: The Journey Begins (by jwilliam at 4/23 11:56 PM)
Re: Providing for Service Layer Objects in CFWheels: Hello Wirebox! (by geirman at 4/21 10:24 AM)
Re: Providing for Service Layer Objects in CFWheels: Hello Wirebox! (by dougboude at 4/20 11:32 AM)
Re: Providing for Service Layer Objects in CFWheels: Hello Wirebox! (by geirman at 4/20 10:27 AM)
Re: The Model-Glue Event Lifecycle in Layman's Terms (by Ashwini at 4/18 2:51 PM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by cormac at 4/18 4:18 AM)
Re: Providing For Service Layer Objects in CFWheels (by Chris Geirman at 4/16 2:04 PM)
Re: Why Provide for Service layer objects in CFWheels? (by dougboude at 4/13 9:13 AM)
Re: Why Provide for Service layer objects in CFWheels? (by Eric Cobb at 4/13 8:26 AM)
Re: Why Provide for Service layer objects in CFWheels? (by Chris Geirman at 4/13 6:44 AM)
Re: Why Provide for Service layer objects in CFWheels? (by Per Djurner at 4/12 3:39 PM)
Re: Refreshing Cached ColdFusion Webservices Through the Back Door (by Paul at 4/11 3:26 PM)
Re: Adding and Customizing Mura Editor Styles (by dougboude at 4/05 3:54 PM)
Re: Adding and Customizing Mura Editor Styles (by mike at 4/05 2:28 PM)
Re: Adding Mura Components to a Page Without Cascading (by Suzy Naschansky at 3/28 10:38 AM)
Re: Adding Mura Components to a Page Without Cascading (by Suzy Naschansky at 3/28 4:36 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 | 8 comments



17 November 2009
Coldbox 101 Training Review

This past weekend I had the privilege of attending Ortus Solution's Coldbox 101 training class in Grapevine, Texas. It is true that I did pay for the opportunity, but the price was obscured and more than justified by the quantity, quality, and cohesiveness of the knowledge that was shared. Coldbox's master architect himself, Luis Majano, was the instructor, and it was with the same level of zeal and attention to detail that he puts into his framework that he also enlightened and empowered his students on Coldbox. In this case, the "no student left behind" policy was absolutely successful. With a student body composed of everyone from absolute CF beginner to CF expert, and from designer to "design-blind", Luis ensured that the content was both palatable and applicable to one and all.

I came to the class being what I would best describe as semi-proficient in Coldbox. I've written a large Coldbox app, done a few Coldbox presentations, blogged about some aspects of Coldbox, and was even hired recently to present a full day instructional course on Coldbox to a local group of San Antonio developers. But, as with any subject that one teaches themselves, there are always gaps in the knowledge you glean from reading docs and your own experiences. It was these gaps I came to the training hoping to fill, and fill them I did. I was given solid insight into how the framework itself is architected, allowing me to fully visualize the request lifecycle and the framework components that interact with and manipulate it; I honed my knowledge of ALL of Coldbox's core concepts, such as interceptors, views,layouts, viewlets, and plugins; I gained insight into the workflow and approaches that Luis himself uses on a daily basis; and my mind was opened up to a whole new facet of the development lifecycle that I personally have always avoided: unit testing.

Coldbox is SO keen on the idea of unit/integration testing that it has the built in ability to simulate itself! In a nutshell, Coldbox makes it VERY simple and easy to test entire events (the equivalent of fuseactions, if that's a more relevant term for you) without the need for the developer to write any additional code. It was truly beautiful to see integration and unit tests run, and just as easy to write them.

I also had an opportunity to flip through the pre-copy of the official Coldbox book, and I must say I had a hard time handing it off to the next student. The Table of Contents was lengthy and robust, and the associated content was clear, concise, and easy to read. Oh, and it is in color! The plethora of code snippets really jump out at you, and  highlighted items are truly highlighted. I would say that the book is definitely one that you will want to have on your desk as a reference. If I understood correctly, we should be seeing it released into the wild in short order. :)


The excitement that new knowledge brings is great to those of us who love it and derive our livelihood from it, and I could easily spend a day sharing all of the very useful information and understandings I received from attending this class. I wasn't asked not to, nor was I asked to sign any NDAs; but for the sake of honoring the information that Luis earns some of his sustenance from, I'll refrain from divulging any more details of the class. I will, however, leave you with this thought...


If you are doing Coldbox development and you have not attended Luis' Coldbox 101 training class, I guarantee you that what you are writing is not all that it could be. It won't be architected as well as it could be, it won't be leveraging all of the shortcuts and features that it could be, and it won't be top of the line. That isn't to say that you aren't capable of writing a good Coldbox app, but without the in-depth understandings that this class offers, your app will very likely not be a great Coldbox app.

It's challenging to get department heads to budget in training sometimes, but my fellow developers, if your shop is or is considering being a Coldbox shop, then it is worth the effort to justify this training to them. I typically ask myself the question, "if it was my  money, would I spend it on this?", and regarding this class the answer is an unequivocable YES! If you would like any additional input to help justify the training, I and I'm sure anybody else who attended this training class would be more than happy to provide our own personal feedback and testimonial.

If you are serious about writing solid, load-bearing apps in Coldbox, do this for yourself: attend Coldbox 101 training.

Doug out.

Coldbox Certification

Posted by dougboude at 10:33 AM | PRINT THIS POST! | Link | 2 comments
10 November 2009
MySQL Query to Find the Following Thursday of a Given Date

I found myself needing to update a MySQL table today with a calculated date, so thought I'd blog the sql in case it saves someone else some time later.

The Scenario

You have a table that contains a date field, but you need to know the date of the Thursday following that date. In this query, the number 5 represents the 5th day of the week (sunday=1, Monday=2, etc.), so if your scenario is looking for a different day of the week, just substitute your day's number everywhere you see the number 5 occurring in mine. The number 12 in the query below is really 7+5, so again, substitute your day's number for 5 in the equation 7+5 and plug in the result where you see 12. If you're looking for Saturday (day 7), you would put a 14 in place of my 12.

The Query 

SELECT adjusterpaydate,
dayofweek(adjusterpaydate)-5 as diff,
adddate(adjusterpaydate,if(dayofweek(adjusterpaydate)-5<=0,abs(dayofweek(adjusterpaydate)-5),12-dayofweek(adjusterpaydate))) as followingThursday
FROM `invoicepayment`
WHERE adjusterpaydate is not null

To update an existing field in the table with the calculated date of the following Thursday, I used a modified version of the above query that looks like this:

update `invoicepayment` set checkdate = adddate(adjusterpaydate,if(dayofweek(adjusterpaydate)-5<=0,abs(dayofweek(adjusterpaydate)-5),12-dayofweek(adjusterpaydate)))
where adjusterpaydate is not null

Posted by dougboude at 5:17 PM | PRINT THIS POST! | Link | 0 comments
09 November 2009
The Joys of Arachnids!

For the past four years I've had the privilege of caring for and learning about several tarantulas and a scorpion. Today I donated them to a local Montessori school so that other people can benefit from them as much as me and my kiddos have. To help the teachers, I created a short document on the care of these guys and some of the fun facts that we have learned about them. It is no way intended to be comprehensive, but will definitely give you things to research.

 Following is that document. May you and yours also discover the joys and wonder of our fellow Earthlings! :)

Tarantulas and Scorpions

Care and Feeding

 

Environment

Tarantulas and Scorpions are somewhat shy, despite their appearances, and in order for them to thrive they require the ability to seclude themselves when desired. Therefore, their environment should always include some form of shelter in which they can hide when desired. Also, since life in a small plastic cube can become monotonous, always try and incorporate items to keep their lives ‘interesting’; rocks, wood, and other natural items for them to explore and manipulate.

 

Never allow direct sunlight to shine into their cages, as the temperature will rise to the point of threatening their lives. Myself, I have always kept them near a window that gets indirect sunlight, but have never used a heat lamp or let the sun hit them directly.

Observation

One can’t help but be intrigued by these creatures, and rightly so; but in order to learn the most from the time spent observing, it is imperative that they not be disturbed when doing so (no forcing them out of their shelters, making them move around, etc.). The goal should be for them to not be aware of your presence when they are being observed.

 

Tarantulas and scorpions have eyes, but their site is not very good at all. Therefore they connect to the world mostly via the sense of touch. They are extremely sensitive to even the smallest vibrations (sound is a vibration), and that is how they hunt and find their prey. Therefore, it is very important that when observing these animals that their cages (and the table or shelf they sit on) are not touched, bumped, or disturbed. An excellent rule of thumb to drill into everyone’s mind is “FOR YOUR EYES ONLY”.

 

Observation is also GREATLY benefited by the use of a hand held magnifying glass. Many of the more intriguing features can be better seen when magnified, such as their eyes, their spinnerets, the pads on their feet, and their fangs!

Feeding

What

Tarantulas and Scorpions eat only live food. They’re not very discriminatory and will probably eat “anything that moves”, but for the maintenance of their health it is best to only feed them live crickets purchased from a pet store.

When

Like us, each individual has their own appetite. One of them will eat once every two weeks while others will eat as often and as much as given to them. A good rule of thumb is to ensure that they have fresh, living food in their cage at least twice a week. If feeding day rolls around and the tarantula or scorpion still has food, do not add more.

How Much

As I mentioned, some tarantulas are gluttons and will literally cram their fangs as full as possible, while others will take one cricket and be completely satisfied. I recommend putting no more than two crickets into the cage at a time. For those gluttonous, it will limit their gluttony; for those who are picky eaters, it will improve their odds of finding and catching at least one.

 

Watering

Tarantulas and scorpions require constant access to fresh water, so do check this daily. Due to the nature of the tarantula’s “mouth”, they require that a sponge be placed in their water dish so that they can suck water out of it as needed. The scorpion’s mouth on the other hand is such that it does not require a sponge but only a shallow dish of water present. It will dip its head into the water to drink when it is thirsty.

Crickets

Crickets are people too (even if they are food)! Since the arachnid may not eat all of the food given it in one sitting, it is important to ensure that the crickets in the cage with the tarantulas, and those extras in their own cage, are provided with the basic necessities so that they can survive until they are eaten. Make sure that they have access to water (in the arachnid’s cage they can use the water dish there, in their own cage put a shallow dish with water and a sponge) and food (I put in a few pieces of dry cat food that they can nibble on).

 

Fun Facts and Research Items

My family and I have learned a LOT about tarantulas and scorpions, and I wanted to pass along some of the more interesting items for the students to learn and/or research.

 

Tarantulas are not true spiders!

There are several differences between true, modern spiders and tarantulas...things that make them interesting and unique among arachnids. What are those differences? The way they breathe, the number of eyes they have...what else?

 

How do tarantulas eat? How do scorpions eat?

We tend to think that tarantulas “suck the blood” from their prey through their fangs; not so! And scorpions...the process is something you have to see to appreciate. The scorpion doesn’t tend to eat all that often, and prefers to do so hidden away, but careful and frequent observation may result in an opportunity to observe the process! Oh, and pay particular attention to the tarantulas right after they catch a cricket. I like to call it “the happy dance”, for they will turn in circles while dipping their spinnerets to the ground, forming a “blanket” on which they will lay the digesting cricket. They will then place more web over the cricket and pick it up again to finish eating it. Very interesting!

 

Shedding of Skin

Tarantulas shed their skin in order to grow. These particular tarantulas are fairly old, and so will probably only shed once or twice a year, max. When they get ready to shed, however, they do an odd thing that at first glance will cause you to think that they have died! So, if you come in one morning and find the tarantula lying flat on his back with his legs in the air, do not disturb it; it is in the very delicate process of taking off its old skin. Once it is finished, it will lie in that position until its new skin is dry and hard, then very often it will take the time to move its old skin to another part of the cage. You may use tongs or some other instrument to gently and slowly remove the old skin from the cage for closer observation.

 

The scorpion doesn’t shed its skin, at least at this stage in its life. It has a body that is more elastic and expandable.

 

Urticating Hairs

One of the tarantulas (the one with the long, red hair) is the type that protects itself with urticating hairs. You should have your students research this to find out more about it. When provoked or frightened, this tarantula will turn itself around and, using its back legs, kick or throw the hairs from its abdomen at its attacker. So, if you see one tarantula that appears to be going bald on its abdomen, it probably just threw some hairs.

 

Reproduction

Tarantulas and Scorpions are very mysterious in this regard, and even being able to discern a male from a female takes a trained eye. All of the tarantulas in this collection are female, but the only way to truly tell is to examine their shed skin under a magnifying glass. Also, male tarantulas when mature will develop a special set of hooks at the knee of their front legs which they use during mating to pin the female’s fangs with so she can’t bite him.

 

Other than the very brief time when they mate, tarantulas must be kept separated because otherwise they simply see each other as a potential meal. Scorpions, on the other hand, are very much at ease with one another and can be kept in groups.

 

Colors

Observe the tarantulas under different kinds of light and at different angles. One will glow a beautiful purple in certain light and angles; the other appears a nice shade of pink and grey. Scorpions, under a black light, glow.

Posted by dougboude at 8:36 AM | PRINT THIS POST! | Link | 0 comments