ActiveMerchant and Authorize.Net

Posted by Brian in Howto, Rails, snacks (June 4th, 2007)

I’ve been working on payment processing with AuthorizeNet, following the documentation for the ActiveMerchant plugin, as well as an excellent book on the subject. However, I find that both sources really lack the true implementation details, so I figured I’d write it up here.

Step 1: Get a developer account.

Go get a developer account from Authorize.Net . They’ll ask you some questions and you’ll get an account in about 24 hours.

Step 2. Get your API login and transaction IDs.

This is really not documented well. ActiveMerchant’s docs all say to create a transaction with

   gateway = AuthorizeNetGateway.new({
       :login => "user",
       :password=>"password})

However, you don’t use the username and password for AuthorizeNet. You use a separate set of credentials..

1. Log into the Merchant Interface
2. Select Settings from the Main Menu
3. Click on API Login ID and Transaction Key in the Security section
4. Type in the answer to the secret question configured on setup
5. Click Submit

Then copy the credentials… you’ll need them later.

Step 3. Install ActiveMerchant

VIsit www.activemerchant.org for installation instructions.

Step 4. Add a configuration file

I suggest creating a file called “config.yml” in your config/ folder. It comes in handy for a lot of things. I’ll store the API login and transaction key in this file.

production:
  auth_net_user: asdfga
  auth_net_pass: 1234412355
development:
  auth_net_user: asdfga
  auth_net_pass: 1234412355
test:
  auth_net_user: asdfga
  auth_net_pass: 1234412355

Remember, that’s a yml file, so make sure you don’t use tabs, and be sure to get your spacing right.

Step 5. Reservation model

My particular application is a reservation system. Regular readers of my blog know I’m really into test-driven development and this is where I really found the docs lacking. Even the book I read, which pushes for TDD the whole way through, never once touched on how to test the Authorize.Net gateway.

I like unit tests a lot, and so rather than use the controller to do the payment processing, I use the model. I want to find a reservation that’s already been created, set the credit card details, and call a method called “process” that will return true if it worked and false if it doesn’t. The process method will set the status to 1 if it was successful.


def test_should_process_order_successfully

    r = Reservation.find 1
    r.status = 0  # want to make sure this has a "pending" status.
    r.step = "checkout"    # need to set this - validations for cc info run only if this is set
    r.customer_ip = '192.168.1.155'
    r.card_type = "Visa"
    r.card_number="4779139500118580"
    r.card_verification_value = "410"
    r.card_expiration_month = "10"
    r.card_expiration_year = "2008"
    r.billing_city = "Springfield"
    r.billing_state = "NT"
    r.billing_zip =" 54703"
    r.billing_address ="123 Fake Street"
    assert r.process
    assert_equal(1, r.status)
end

With that set, I can add the following code to my model.


#  id                     :integer(11)   not null, primary key
#  customer_email         :string(255)
#  confirmation_number    :string(255)
#  payment_transaction_id :string(255)
#  created_at             :datetime
#  confirmed_at           :datetime
#  processed_at           :datetime
#  cancelled_at           :datetime
#  phone_number           :string(255)
#  billing_address        :string(255)
#  billing_city           :string(255)
#  billing_state          :string(255)
#  billing_zip            :string(255)
#  user_id                :integer(11)
#  status                 :integer(11)   default(0)
#  passengers             :integer(11)
#  total_cost             :integer(10)
#
class Reservation < ActiveRecord::Base
include ActiveMerchant::Billing
belongs_to :user
validates_presence_of :billing_address,
    :billing_city,
    :billing_state,
    :billing_zip,
    :customer_ip,
    :card_type,
    :card_verification_value, :card_number, :card_expiration_month, :card_expiration_year,
    :if=> Proc.new{|record| record.step == "checkout"}

  # cc fields as accessors so we don't store anything bad.
  attr_accessor :card_type, :card_expiration_month, :card_expiration_year, :card_number, :card_verification_value, :step

  # Process the payment
  def process
    self.processed_at = Time.now
    begin
      process_payment
    rescue => e
      logger.error("reservation #{self.confirmation_number} failed with error message #{e} ")
      self.error_message = "#{e}"
      self.status = 7  #failed
    end
    save!
    self.status == 1

  end

  protected

    def process_payment

     # this forces the system to use the testing server, which is what all dev accounts use.
     ActiveMerchant::Billing::Base.mode = :test if RAILS_ENV != "production"

     # read api key and  transaction # from config file
    c = YAML::load(File.open("#{RAILS_ROOT}/config/config.yml"))
    user = c[RAILS_ENV]['auth_net_user']
    pass = c[RAILS_ENV]['auth_net_pass']

     creditcard = ActiveMerchant::Billing::CreditCard.new(
       :first_name => self.user.first_name,
       :last_name => self.user.last_name,
       :number=> self.card_number,
       :verification_value =>card_verification_value,
       :type => self.card_type,
       :month => self.card_expiration_month,
       :year => self.card_expiration_year
     )

     if creditcard.valid?
     	  options = {:name => self.user.full_name,
     	     :email => self.user.email,
     	     :phone => self.phone_number,
     	     :ip => self.customer_ip,
 	     :card_code => self.card_verification_value,
             :order_id => self.confirmation_number,
 	     :description => "Conference reservation",
             :billing_address=>{
 				      :address1 => self.billing_address,
 				      :city => self.billing_city,
 				      :state => self.billing_state,
 				      :zip => self.billing_zip,
 				      :country => "US"}
             }

         }

              gateway = AuthorizeNetGateway.new({:login => user, :password=>pass})

           response = gateway.purchase(self.total_cost_in_cents, creditcard, options)

   				if response.success?
   				  self.status = 1
       			self.confirmed_at = Time.now
       			self.error_message = nil
 				  else
 				    self.status = 7
 				    self.error_message = response.message
   				end
 			 else
 			   self.status = 7

         self.error_message = "Invalid credit card."
       end

  end
end

Run the unit test – it should pass without issue.

Now, you just need to make your controller take in the fields from a form. That should be pretty simple. Set up your form fields like

  <% form_for "reservation", @reservation, :url=>{:action=>"process_order"} do |f| %>

Card number: <%= f.text_field "card_number" %>

     ... other fields
    <%=submit_tag "Check out" %>
  <% end %>

 def process_order
   # get  id from session - no passing ids around in the url for me!
   @reservation = Reservation.find(session[:reservation_id]

   @reservation.customer_ip = request.remote_ip
   @reservation.step = "checkout"   # for validation, remember?
        if @reservation.update_attributes(params[:reservation])
          if @reservation.process
            redirect_to :action=>"finished"
          else
            @reservation.errors.add_to_base @reservation.error_message
            render :action=>"checkout"
          end
        else
          render :action=>"checkout"
        end
 end

Step 6. Hooking it all up

You’re using a developer account, so you won’t actually see the transaction on the AuthorizeNet side of things unless you turn testing mode off. This part took me forever to figure out.

1. Log into the Merchant Interface
2. Select Account from the Main Menu
3. Click Test Mode
4. Press Turn OFF test

Now, re-run your unit test and then view the Reports page in the Merchant Interface. You will see a new transaction.

Testing without the Internet

If you’re not really into testing online, you can still get your test to pass. You can use 0 or 1 for the credit card number…. 0 means an invalid card, and 1 means a successful card. This comes in handy when doing a functional test later on.

Taking it Live

Change your details to use your permanent account’s transaction key and id. Then remove the :test mode stuff by running your app in production mode. Put your real gateway in test mode and then run through your app, using some fake credit cards from AuthorizeNet.

Mastercard: 5424000000000015
Visa: 4007000000027
Discover: 6011000000000012
American Express: 370000000000002

Once you’re absolutely sure that everything’s working, turn off the test mode on the production account, and you’re good to go!

Wrapping up

I hope that helps some of you. I spent a lot of time looking over the documentation and various examples out there. The developer account works a bit differently than the real account, so that was a major stumbling block.

Feedback is welcome, as always!

MySQL gem on OSX

Posted by Brian in Howto, Rails, snacks (June 1st, 2007)

UPDATED 12-05-2008: The instructions in this article were written for Tiger. With the release of Rails 2.21 this article’s been getting a lot more traffic and the instructions here needed to be updated. What you see here are instructions that should work for most installations of MySQL on the Mac.

When I got the Macbook Pro in March 2007, I immediately installed Ruby, Rails, Subversion, etc, using this tutorial from Hivelogic. http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx
They’ve done a good job of keeping it up to date, but one problem I noticed is that the MySQL gem instructions don’t work anymore. When the article was first written, it contained steps to remedy a minor installation problem. However, enough users reported that the problem no longer existed so it was removed.

Today, I encountered that problem again, and thought I’d share the solution. If you’re getting an error when you do

First, locate your installation of MySQL using

which mysql_config

You need three libraries:
include/mysql5
lib/mysql5
bin/mysql5_config

These should all be located within your mysql base folder.

For example, if MySQL is installed by Macports, the default location would be

/opt/local/lib/mysql5/

So you’d change the paths accordingly:

 sudo gem install mysql -- --with-mysql-include=/opt/local/include/mysql5 --with-mysql-lib=/opt/local/lib/mysql5 --with-mysql config=/opt/local/lib/mysql5/bin/mysql_config

This solution seems to get things rolling. Thanks much to crookshanks on IRC for the fix. Really, if you’re not participating in the IRC channels for Rails (#rubyonrails on irc.freenode.net) you’re missing out on some good discussions.

The rest of this article is most likely not relevant anymore.,/b>

You’ll need to do the following to fix it.

First, look at where the gem says it’s left its files.

  Gem files will remain installed in /usr/local/lib/ruby/gems/1.8/gems/mysql-2.7 for inspection.

So in my case, I’ll do

  cd /usr/local/lib/ruby/gems/1.8/gems/mysql-2.7

Then I’ll edit the file ‘mysql.c’ in that folder and add one line at the top of the file:

  #define ulong unsigned long

Then, I just run

  sudo make
  sudo make install

The original fix for this comes from
http://jlaine.net/2006/10/3/installing-ruby-mysql-driver-on-os-x but the instructions there are a little backwards. I hope this solution will help others having a similar problem.

Updating Multiple Fields in Rails

Posted by Brian in Rails, snacks (April 23rd, 2007)

Just a quick one today…. I always have to look this one up.

Let’s say you have a guestbook and you want to approve multiple entries quickly. Rails has a built-in method for handling that, although it’s poorly documented.

Assuming your model “Entry” has an “approved” attribute that maps to a boolean field in the database, you might make a form like this:


<% form_tag :action=>"approve" do %>

<% @entries.each do |@entry| %>

Posted at<%=@entry.created_at %> by
<%=@entry.email %>

<%=h(@entry.body) %>

Approved? <%=check_box "@entry[]", "approved" %>

<% end %>

<%= submit_tag "Moderate Entries" %>
<% end %>

The key here is that you’re iterating over the items in the collection and using an instance variable to hold each one (@entry). Normally, a check_box helper looks like this:


<%=check_box "entry", "approved" %>

If you use the check_box helper with an array as the first parameter, you can create an array of entries that can all be sent to the back-end.


<%=check_box "entry[]", "approved" %>

Rails puts the primary key of the entry inside of the square-brackets, thus causing them to group nicely.

Your params hash will look like this:


{
"commit"=>"Moderate Selected Entries",
"action"=>"moderate",
"entry"=>{
"1"=>{"approved"=>"1"},
"2"=>{"approved"=>"1"}
},
"controller"=>"guestbook"
}

That’s the hard part. The action that receives the POST request looks like this


def moderate
Entry.update(params[:entry].keys, params[:entry].values)
flash[:notice] = "Successfully modified the approval flags for all records."
redirect_to :action=>"index"
end

Yeah, you read that right… no looping. The “update” class method on an ActiveRecord object can take either an ID or an array of IDs as the first parameter, and a hash of names and values or an array of hashes containing names and values for the second parameter. Since the form made the record ids as the keys in the hash, calling params[:entry].keys gives us the array of IDs we need for the first parameter. params[:entry].values gives us an array of the hashes we need for the second parameter.

Now, you can use this technique to do some simple mass-update pages if you needed to, but then it gets a little trickier.

I hope someone out there finds this useful.

Unit testing

Posted by Brian in Rails, snacks (April 3rd, 2007)

Rails developers typically do test-driven development, so they’re writing their unit tests before they write their business rules. For example, if I was to write a keyword search method, I’d write a unit test first.

# Should find 2 because I have “red” in the test data in 2 records
assert_equal(2, Document.find_by_keywords(“red”)

# should find 0 because that term isn’t in any of the records
assert_equal(2, Document.find_by_keywords(“blue”)

# should find 2 because the word “ninja” is found on one document
# and ‘pirate’ is found in another.
assert_equal(2, Document.find_by_keywords(“ninja pirate”)

The test that I wrote gives me a clear indication of the requirement I have to implement. When these tests (and more) all pass, I know that I have my search engine finished. I don’t need to continously try various examples manually to see if I am done. It takes a lot of the manual testing away, but it also gives me more confidence that my code does what I think it does.

The hardest part about testing is learning to do it first. It takes much longer to write tests after you already have a working application, and it feels pointless. (It’s really not, but it just feels that way when you’re doing it which can be really demotivating.)

Associations and caching

Posted by Brian in Rails, snacks (April 2nd, 2007)

Sometimes I just do dumb things when I write code. The way I look at it, every mistake I make is a chance to learn something new.

I’ve got a system with workshops, presenters, enrollments, and more. It’s a fairly complex system that has a lot of relationships. If you peek into my User model, you’ll see some things like this:


class User < ActiveRecord::Base
has_many :presenterships
has_many :troubleshooterships
has_many :enrollments
has_many :presentations, :through=>:presenterships, :source=>:workshop
has_many :troubleshoots, :through=>:troubleshooterships, :source=>:workshop
has_many :workshops, :through=>:enrollments
end

....
end

This post Turn finders into associations and get caching for free got me thinking about my code a bit so I decided to take a second look. When I did, I saw this method:


#Returns all of the workshops for which this user is enrolled.
def workshops_enrolled
@workshops = Workshop.find :all, :include=>[:offering, :enrollments],
:conditions=>["enrollments.user_id = ? and enrollments.withdrew = ?", self.id, false]
end

I added this method because I wanted to make sure I only showed associations where the user did not withdraw. (I’m recording withdrawls from a workshop instead of removing the association between the user and the workshop.)

Darn it… that’s just not right. While that works, I lose the ability to cache, and it’s also exactly what an association is for. Refactored, it now looks like this:


has_many :workshops_enrolled, :source=>:workshop, :include=>[:offering, :enrollments],
:through => :enrollments, :conditions=>["enrollments.withdrew = ?", false]

Since it’s a has_many :through, it needs to have the :source option defined so Rails knows what the parent class is.

Fun stuff, this Rails.

Upgrading Ruby to 1.8.6 on OSX and Windows

Posted by Brian in Rails, snacks (March 24th, 2007)

Mac users

Assuming you’ve followed the Hivelogic article that walks you through a basic Ruby on Rails installation, you can use this code snippet to install Ruby 1.8.6.


cd /usr/local/src
curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.gz
tar -zxvf ruby-1.8.6.tar.gz
cd ruby-1.8.6
./configure --prefix=/usr/local --enable-pthread --with-readline-dir=/usr/local
make
sudo make install
sudo make install-doc
cd ..
sudo gem install rails --include-dependencies

Windows users

Windows users only need to snag the latest version of Curt Hibbs’ One Click Ruby Installer which can be downloaded here.

The major advantage of moving to Ruby 1.8.6 and Rails 1.2.3 is that breakpointer works again.

Update: When I did this on my Macbook Pro, I had to recompile a few libraries like Ruby-DBI and Ruby-ODBC before I could connect to Microsoft SQL Server again.

Serializing ActiveRecord objects

Posted by Brian in Rails, snacks (March 19th, 2007)

One neat feature of Rails’ ActiveRecord objects is the #serialize method which will allow you to store an object in the database as YAML. This means, for example, you could have a User and a Profile. The Profile could be a hash containing a bunch of values that you want to manage, but that don’t necessarily need their own database table.


class User < ActiveRecord::Base

serialize :profile
end

u = User.find 1
u.profile = {"url" => "http://www.rubyonrails.com", :nickname =>"Ninja master"}
u.save

u = User.find 1
u.profile.class
=> Hash

The magic of the #serialize method takes the given object, serializes it to YAML, stores the YAML in the database, and then unserializes it when you retrieve the data.

This is all well and good, and I thought I would take advantage of this to help me easily cache some data. I have a system in which I have tasks, and a task belongs to a Service which contains the rate we charge for the task. Now, I really want to be able to store the service name and rate on the task when it’s assigned so that the task won’t be affected when I change my rates in the future.

“Aha!” I thought,”I can just use serialize and store the Service object right on the task!” I created a migration that added a service_data field to my database


./script/generate migration AddServiceDataToTasks

class AddServiceDataToTasks< ActiveRecord::Migration
def self.up
add_column :tasks, :service_data, :text
end

def self.down
remove_column :tasks, :service_data
end
end

I wrote a quick unit test which I knew would come in handy later.


def test_saves_service_when_task_is_created
@service = Service.find_by_name "Rails development"
task = Task.create :name=>"Create user registration site", :esthours => 5, :service => @service
t = Task.find task.id
assert_not_nil t.service_data
assert_kind_of Service, t.service_data

end

Then I modified my Task model


class Task < ActiveRecord::Base
belongs_to :service
serialize :service_data

after_create :sync_service_data!

def sync_service_data!
self.service_data = self.service
self.save!
end
end

That seemed simple enough. However, when I tried it, I got a nasty surprise...

t = Task.find 1
t.service_data
=> nil

No matter what i tried, the service data always came back empty.

Running my unit test proved that something was definitely wrong, as I kept seeing "nil expected to not be nil".

After searching and playing, I decided that #serialize was just not capable of serializing ActiveRecord objects. To get around this, I simply changed my code slightly. I knew that #serialize can handle Hashes so I stored just the attributes hash. Then I redefined #service_data "getter" method to create a new instance of Service from that hash.


class Task < ActiveRecord::Base
belongs_to :service
serialize :service_data

def sync_service_data!
self.service_data = self.service.attributes
self.save!
end

def service_data
Service.new(self.attributes["service_data"]
end
end

A quick run of the tests showed that I was now getting what I wanted.

Shortly after I discovered this solution, Jon Garvin offered a much cleaner solution.... don't use Serialize. He found that Serialize does some strange magical things that often get in the way of our intended results. He proposed that I try

class Task < ActiveRecord::Base
belongs_to :service

after_create :sync_service_data!

def sync_service_data!
self.service_data = self.service
self.save!
end

def service_data
self[:service_data] ? Marshal.load(self[:service_data]) : nil
end

def service_data=(service)
self[:service_data] = Marshal.dump(service)
end

end

This method simply creates a setter that manually marshals the data to the database column, and a getter that retrieves it again. This method works great, and I thank Jon for his quick solution!

Test-driven development – Simple example

Posted by Brian in Rails, snacks (March 9th, 2007)

This is a really simple example of why unit tests are important. I was just working with a project where I am creating workshops. Students enroll in these workshops and I have a method on the Workshop model that handles the enrollment.

(more…)

Connecting to Microsoft SQL Server from Mac OS X

Posted by Brian in Rails, snacks (March 8th, 2007)

Updated on July 2nd, 2008.

As you may know, I develop a lot of new web applications using Ruby on Rails, and to completely buy in to the whole “cult that is Rails”, I purchased a Macbook Pro for development. I am really happy with it, but one of the problems I ran across is the fact that there’s no built-in or prepackaged way to connect to Microsoft SQL Server which I use at work for a lot of my projects.

This guide is written from my notes and should give you the tools you need to get up and running. Some of this information comes from the Ruby on Rails wiki, but some things there are a bit misleading.

This now works on Leopard too!

(more…)

Excel Export, the Stupid Simple Way

Posted by Brian in Rails, snacks (January 24th, 2007)

It occurred to me the other night while helping Mike figure out how to prepare some reports in Excel from a Rails application that it probably wasn’t necessary to use the plugin I created. One of the big drawbacks to the Excel Export plugin is that it doesn’t currently support formatting of cells. (There just really isn’t a good way to do that.) One trick I’ve known about for years is the ability for Excel to read in an HTML table of data and render it as a spreadsheet.

This meant that we could simple create a new view that contained the HTML table

Last Name First Name Birthday Email
<%=user.last_name %> <%=user.first_name %> <%=user.birthday %> <%=user.email %>

Then it’s just a simple matter of making a controller action to display that view.

  def export
    headers[’Content-Type’] = “application/vnd.ms-excel”
    render :layout=> false
  end

That should cause the browser to open Excel, instead of displaying the page. Now, I can’t guarantee that this will work on Linux, OSX, or anywhere where Excel is not installed. However it certainly meets our needs for this one project and so I t hought I would share it with everyone else.

« Previous Page