Adobe Solution Partner

October 17, 2007

Using PayPal subscriptions with ColdFusion

Filed under: ColdFusion — Tags: , , — Shannon Hicks @ 1:02 pm

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:

<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<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:

<!--- validate the paypal IPN --->
<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.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • LinkedIn
  • StumbleUpon
  • Technorati
  • TwitThis

3 Comments »

  1. 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

  2. That’s what this code is for:

    <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#">

    The “on” and “os” values get passed back to your notify_url.

    Comment by Shannon Hicks — April 4, 2009 @ 4:06 pm

  3. Thanks Shannon for your code. Quick question, have you done much work with the Unsubscribe button? What code would you use to allow the user to unsubscribe and then update the database?

    Comment by Ed — July 17, 2010 @ 7:11 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

 

Server Down?

Maximize Web application uptime by drawing upon Webapper's years of experience tuning and stabilizing many of the world's largest ColdFusion Web applications. Contact us today!