ActiveMerchant and Authorize.Net
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| %>
<p>Card number: <%= f.text_field "card_number" %></p>
... 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!

