December 1, 2010

Job Position Available

Filed under: Random Business Thoughts, Team Dynamics — Stacy @ 1:42 pm

We just posted a position to our website today for a writing position at Thunder Data.  We are seeking an intern to write articles and blog posts for ThunderTix, a ticketing software to help manage events for small venues to sell tickets. We are looking for a talented writer to create both educational articles and short blog posts. We’ll provide the topics and concepts for each writing assignment.

This is a paid position with flexible hours for a college intern or graduate interested in part-time work.  All work can be performed outside the office, so close proximity to our office is not necessary.  Our preferred candidate would be working towards a degree in either English or Journalism.  Experience in marketing or public relations is a plus, but is not required. 

If you are interested in this position or know someone who might be, please send resumes and/or a portfolio to careers_info@thunderdata.com.

Come and join our team!

October 13, 2010

Using PHP’s preg_match() to Parse Credit Card Data from Magnetic Swipe Readers

Filed under: TDS Developer's Corner — Dawn @ 12:24 pm

Magnetic swipe card readers via USB can facilitate faster credit card processing. This is particularly important for our ThunderTix clients whose box offices must move patrons past the ticket window as quickly as possible. This week, a client from one of our legacy PHP apps asked us if we could implement a swipe reader for his own administrative checkout page. I thought it would be a good time to create a tutorial for parsing the credit card string using PHP and regular expressions.

Our client wanted the ability to enter either all form fields during the checkout process by manual entry or only some of the required fields when swiping the card during a purchase. We create a form for the checkout:

<span style='required'>*</span> - Indicates required field.
<div class='fields'>First Name</div>
  <input type=text name='first_name'><span style='required'>*</span>
</div>
<div class='fields'>Last Name</div>
  <input type=text name='last_name'><span style='required'>*</span>
</div>
<div class='fields'>Expiration</div>
  <input type=text size=8 name='expiration'><span style='required'>*</span>(MMYY)
</div>
<div class='fields'>CVV Code</div>
  <input type=text size=8 name='cvv'><span style='required'>*</span>
</div>
<div class='fields'>Credit Card Number</div>
  <input type=text name='card'><span style='required'>*</span>
</div>

Now, how do we bypass the validation for the required fields? Using regular expressions, we can both assess whether the credit card post variable is from the magnetic stripe data and parse the string itself when a match exists.

The string generated from a magnetic stripe reader looks like this :

    %B4444490633714269^DOE/JANE B^12101010000000379000000?    

The data from the string is divided by the start sentinel (%B), the end sentinel (?), and field separators (^ and /). So, we need to create a regular expression to parse the pieces out. We’ll create the following regex pattern and save it to the $swipe variable.

$swipe = "/^(%B)([0-9]{16})(^)([a-zA-Z-s]*)(/)([a-zA-Z-s]*)(^)([0-9]{2})([0-9]{2})(.)*?$/";

Let’s break down our regular expression:

  /^(%B)  = Start Sentinel and format Code (alpha only)
([0-9]{16}) = Primary account number is 16 digits long, accepts only numeric characters
(^) = Field Separator
([a-zA-Z-]*) = Last Name contains only alpha characters, a space, or a dash. Note the - and sare escaped
(/) = Separator between first and last name (escaped forward slash)
([a-zA-Z-]*) = First Name accepts only alpha, spaces, or dashes
(^) = Field Separator
([0-9]{2}) = Expiration year is two digits, accepts only numeric characters
([0-9]{2}) = Expiration month is two digits, accepts only numeric characters
(.)*?$/ = The "(.)*" sequence is any other characters that follow through to the end sentinel for the mag data represented by a the Question Mark is reMagnetic Data 'end sentinenel'

So, now we’ll move onto the form’s action. We’ll use preg_match to compare our $_POST['card'] string to our regex contained in $swipe. The result is held in the $matches array. If a match exists, it is held in $matches[0], and $matches[1] contains the first parenthesized value, $matches[2] contains the next (of actual card number), and so on. We’ll assign these to the $_POST variables, so our form can be processed as if the data had been entered into the fields individually. If there is no match, standard validation processing occurs.

if($_POST['action'] == 'pay') {
  $swipe = "/^(%B)([0-9]{16})(^)([a-zA-Z-s]*)(/)([a-zA-Z-s]*)(^)([0-9]{2})([0-9]{2})(.)*?$/";
  
  if (preg_match($swipe, $_POST['x_card_num'], $matches)) {
    $_POST['card'] =  $matches[2];
    $_POST['first_name'] = $matches[6];
    $_POST['last_name'] =  $matches[4];
    $_POST['expiration'] = $matches[9]."".$matches[8];
  }  else {
    perform_validation_procedures ...
}

A couple of final notes: if using Authorize.net as your gateway, make sure you have the Address Verification Service (AVS) matching turned off. This will prevent swipes from passing and result in a declined transaction since no address is being passed. Likewise, you may also want to turn off the CVV requirement to prevent a rejection. Otherwise, you can enter the CVV number in the CVV form field prior to swiping the card. Also, make sure you properly order the expiration month and year and the first and last names, or your transaction will fail.

Happy parsing!

July 19, 2010

10 Steps to Capistrano and Plesk Subdomains Deployment

Filed under: Designer Showcase, TDS Developer's Corner — Dawn @ 12:56 pm

We’ve adopted a standard convention for deploying Rails apps that places the files in a subdomain of our clients’ domains. Since we use Plesk, there are some steps we have to take to get Capistrano deploys working. Since we deploy a lot of apps, I decided to write this guide as much for the office as for anyone who may find it useful. Let’s go through the steps:

  1. Create your subdomain in Plesk (ie. myrailsapp)
  2. Navigate to the /domain.com/subdomains/myrailsapp/conf directory
  3. Create a vhost.conf file. Again, we use a convention that creates a new directory within /myrailsapp that holds our rails code. Fittingly, we name the directory /rails. The DocumentRoot points to the public directory of our application. Enter the following into the vhost.conf file:
  4. DocumentRoot /var/www/vhosts/domain.com/subdomains/myrailsapp/rails/current/public 
    RailsEnv production
    
    
  5. If you haven’t already installed the capistrano and capistrano-ext (for multistage environments such as testing and production deploys), you’ll need to get them. The Capistrano tutorials will walk you through that process.
  6. With the gems installed, now we want to capify our code. In your root rails app directory, enter the following command. (You may have to sudo the command depending on your directory permissions.)
  7. capify .
    

    This will create two files: Capfile and deploy.rb in your /config directory.

  8. Open up your deploy.rb file and enter your configuration variables. Again, you can refer to the Capistrano tutorials for help.
  9. If you are only planning to deploy to a single environment, you’re work is done. If you plan a multi-stage environment, you’ll want to set the stages in your deploy.rb file and require the capistrano-ext gem like this:
    set :stages, %w(testing, production)
    set :default_stage, "testing"
    require "capistrano/ext/multistage"
    
    
  10. Within the /config directory, create a new /deploy directory and two new files, testing.rb and production.rb. These files will provide the deploy details for each environment.
  11. Open up your new .rb files and enter the following for each environment:
    
    #############################################################
    #   Application
    #############################################################
    
    set :application, "myrailsapp"
    set :deploy_to, "/var/www/vhosts/domain.com/subdomains/myrailsapp/rails"
    
    #############################################################
    #   Settings
    #############################################################
    
    default_run_options[:pty] = true
    ssh_options[:forward_agent] = true
    set :use_sudo, true
    set :scm_verbose, true
    set :rails_env, "testing" 
    
    #############################################################
    #   Servers
    #############################################################
    
    set :user, "username"
    set :password, "user_password"
    set :domain, "myrailsapp.domain.com"
    server domain, :app, :web
    role :db, domain, :primary => true
    
    #############################################################
    #   Passenger
    #############################################################
    
    namespace :deploy do
      desc "Create the database yaml file"
      task :after_update_code do
        db_config = <<-EOF
        production:    
          adapter: mysql
          encoding: utf8
          username: database_username
          password: database_password
          database: database
          host: localhost
        EOF
        
        put db_config, "#{release_path}/config/database.yml"
    
      end
        
      # Restart passenger on deploy
      desc "Restarting mod_rails with restart.txt"
      task :restart, :roles => :app, :except => { :no_release => true } do
        run "touch #{current_path}/tmp/restart.txt"
      end
      
      [:start, :stop].each do |t|
        desc "#{t} task is a no-op with mod_rails"
        task t, :roles => :app do ; end
      end
      
    end
    
    
    Then add the details of your repository for either subversion or git.
    
    
    #############################################################
    #   GIT
    #############################################################
    
    set :scm, :git
    set :branch, "master"
    set :repository, "git@github.com:your_github_path/MYRAILSAPP.git"
    set :deploy_via, :remote_cache
    ssh_options[:forward_agent] = true
    
    #############################################################
    #   SVN
    #############################################################
    
    set :scm, :svn
    set :branch, "trunk"
    set :scm_user, 'username'
    set :scm_password, "user_password"
    set :repository, Proc.new { "--username #{scm_user} --password '#{scm_password}' --no-auth-cache http://doman.com/path/to/your/subversion/repository/#{application}/trunk" } 
    
    set :deploy_via, :remote_cache
    ssh_options[:forward_agent] = true
    
    
  12. That’s it; you’re ready to deploy. Back at your terminal window, you’ll type the respective environment to which you’re deploy:
cap testing deploy

A final note: make sure the user specified in your deploy files has permission to create directories on the server, otherwise your /releases directories will not be created.

Feel free to ask any questions if you’re having trouble with your Plesk subdomain and Capistrano deployment. Good luck!

January 7, 2010

Rails validation error not displaying - validates_presence_of, error_message_for

Filed under: Newbie Rails Trip-ups, TDS Developer's Corner — Dawn @ 4:26 pm

I hit a wall recently as I tried to track down the reason validates_presence_of was not displaying errors. Rails validations normally go off without a hitch, so I was a bit stumped when my attempts to save blank fields were not triggering the appropriate messages. After ensuring my model and view were correct, I fired up the console to make sure my validations were working in the first place. After loading an object and attempting a save, I checked to see if the errors object returned false. Sure enough, all of the validations were enforced.


>> @contact = Contact.find_by_id(9)
=> #
>> @contact.save
=> false
>> @contact.errors.empty?
=> false
>> @contact.errors.count
=> 6
>> @contact.errors.each_full { |msg| puts msg }
   City No blanks
   Zip No blanks
   First name No blanks
   Address No blanks
   Last name No blanks
   State No blanks

After more head banging, I was ready to give up with a simple notification workaround on the update method.


if !@contact.errors.empty?
  flash[:error] = "You are missing required fields."
  redirect_to edit_contact_url(@contact.user)
else
  flash[:notice] = "Successfully saved."
  redirect_to contact_url(@contact.user)
end

That works, but it doesn’t take advantage of the Rails’ error_messages_for helper to display helpful messages and highlight problem fields. Then I realized the initial error. During redirects, the object’s data isn’t passed, so while the validations were enforced (the form data would not save upon submission), no errors were being output to the screen. My new code rendered the page along with the error messages:


if !@contact.errors.empty?
  render :action => 'edit'
else
  flash[:notice] = "Successfully saved."
  redirect_to contact_url(@contact.user)
end