Using PayPal subscriptions with ColdFusion
I recently built an application that was based on a yearly subscription model. After thinking about the various payment methods available, I decided to go with PayPal subscriptions. The advantages are obvious… No need for SSL, I don’t have to assume any of the risk of storing payment information, and I didn’t have to worry about other complications like expiring credit cards.
So, the question then became “How do I handle subscription events?”
The answer, it turns out, was pretty simple. Here’s a look at the subscribe button HTML:
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif" border="0" name="submit" alt="Make payments with PayPal - it's fast, free and secure!">
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
<input type="hidden" name="cmd" value="_xclick-subscriptions">
<input type="hidden" name="business" value="[YOUR BUSINESS EMAIL ACCOUNT]">
<input type="hidden" name="item_name" value="My Subscription (1yr)">
<input type="hidden" name="page_style" value="PayPal">
<input type="hidden" name="buyer_credit_promo_code" value="">
<input type="hidden" name="buyer_credit_product_category" value="">
<input type="hidden" name="buyer_credit_shipping_method" value="">
<input type="hidden" name="buyer_credit_user_address_change" value="">
<input type="hidden" name="no_shipping" value="1">
<input type="hidden" name="notify_url" value="http://www.yoursite.com/subscribe/return.cfm">
<input type="hidden" name="return" value="http://www.yoursite.com/subscribe/thanks.cfm">
<input type="hidden" name="cancel_return" value="http://www.yoursite.com/subscribe/cancel.cfm">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="lc" value="US">
<input type="hidden" name="bn" value="PP-SubscriptionsBF">
<input type="hidden" name="a3" value="25.00">
<input type="hidden" name="p3" value="1">
<input type="hidden" name="t3" value="Y">
<input type="hidden" name="src" value="1">
<input type="hidden" name="sra" value="1">
<cfoutput>
<input type="hidden" name="item_number" value="#getUser.userID#">
<input type="hidden" name="on0" value="Username">
<input type="hidden" name="os0" value="#getUser.username#">
<input type="hidden" name="on1" value="SomeOtherInfo">
<input type="hidden" name="os1" value="#getUser.someOtherInfo#">
</cfoutput>
</form>
Now, I’ll skip over some of the standard PayPal inputs, and hit the ones that matter for processing things on the CF side.
- notify_url: The URL that PayPal sends IPN requests to (for successful subscriptions, or expirations and cancellations)
- return: The URL that PayPal redirects users to after they complete payment
- cancel_return: The URL PayPal sends users to after they cancel their subscription.
- item_number: What PayPal suggests you use to distinguish one subscription from another, which in my case was a userID.
My return and cancel_return pages were just plain HTML thanking the user for their respective actions, so I won’t waste space with that code. The interesting code is my notify_url code:
<cfscript>
str="cmd=_notify-validate";
for (i=1;i LTE listLen(FORM.FIELDNAMES);i=i+1) {
theField = listGetAt(FORM.FIELDNAMES,i);
str = str & "&#LCase(theField)#=#URLEncodedFormat(FORM[theField])#";
}
if (isDefined("FORM.payment_date")) {
str = str & "&payment_date=#URLEncodedFormat(Form.payment_date)#";
}
if (isDefined("FORM.subscr_date")) {
str = str & "&subscr_date=#URLEncodedFormat(Form.subscr_date)#";
}
</cfscript>
<cfhttp url="https://www.paypal.com/cgi-bin/webscr?#str#" method="get" resolveurl="false"></cfhttp>
<!--- If the IPN validation fails, stop here --->
<cfif #CFHTTP.FileContent# IS NOT "VERIFIED">
Thanks, hacker.
<cfabort>
</cfif>
<cfswitch expression="#FORM.txn_type#">
<cfcase value="subscr_signup,subscr_payment">
<!--- subscription successful or updated: change the paid date --->
<cfquery name="updateSubscription" datasource="#REQUEST.dsn#">
UPDATE users
SET paidExpires = <cfqueryparam cfsqltype="cf_sql_date" value="#dateAdd('y',1,now())#">,
demoExpires = NULL
WHERE userID = #FORM.ITEM_NUMBER#
</cfquery>
</cfcase>
<cfcase value="subscr_failed,subscr_eot,subscr_cancel">
<!--- subscription failed, expired, or canceled: clear out the date --->
<cfquery name="updateSubscription" datasource="#REQUEST.dsn#">
UPDATE users
SET paidExpires = NULL
WHERE userID = #FORM.ITEM_NUMBER#
</cfquery>
</cfcase>
</cfswitch>
I left my queries in, but you can of course handle flagging people as paid or not however you choose. The important code is in the CFSCRIPT, which actually validates the IPN transaction, to prevent hackers from getting something for nothing. It’s an updated version of the stock PayPal code. After that, there’s a CFSWITCH statement that checks for various txn_type values that PayPal might return:
- subscr_signup: New subscription signup
- subscr_payment: Existing subscription made a payment
- subscr_failed: New subscription failed
- subscr_eot: Subscription lapsed
- subscr_cancel: Subscription canceled
It’s your responsibility to architect your application and database to handle these events, but I’m happy to pass on the research and trial and error I went through.

Hi All,
I am implementing the paypal subscription using coldfusion and now i am able to subcribe the membership using paypal. but i am stuck with getting the user information variables. i am passing userid from my website and it goes to paypal and then payment is done and return to my website i want to get the userid which pass earlier.
Help Need Urgent!!!!
Thanks in Adavance,
Ravi
Comment by ravi — April 1, 2009 @ 2:25 pm
That’s what this code is for:
<input type="hidden" name="os0" value="#getUser.username#">
<input type="hidden" name="on1" value="SomeOtherInfo">
<input type="hidden" name="os1" value="#getUser.someOtherInfo#">
The “on” and “os” values get passed back to your notify_url.
Comment by Shannon Hicks — April 4, 2009 @ 4:06 pm