Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Sage Payment Solutions gateway
- Loading branch information
Cody Fauser
committed
May 18, 2008
1 parent
035a24b
commit 709aaa1
Showing
5 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
module ActiveMerchant #:nodoc: | ||
module Billing #:nodoc: | ||
class SageGateway < Gateway | ||
URL = "https://www.sagepayments.net/cgi-bin/eftBankcard.dll?transaction" | ||
|
||
self.supported_countries = ['US'] | ||
self.supported_cardtypes = [:visa, :master, :american_express, :discover] | ||
self.homepage_url = 'http://www.sagepayments.com' | ||
self.display_name = 'Sage Payment Solutions' | ||
|
||
# Transactions types: | ||
# <tt>01</tt> - Sale | ||
# <tt>02</tt> - AuthOnly | ||
# <tt>03</tt> - Force/PriorAuthSale | ||
# <tt>04</tt> - Void | ||
# <tt>06</tt> - Credit | ||
# <tt>11</tt> - PriorAuthSale by Reference* | ||
TRANSACTIONS = { | ||
:purchase => '01', | ||
:authorization => '02', | ||
:capture => '03', | ||
:void => '04', | ||
:credit => '06', | ||
:reference_purchase => '11' | ||
} | ||
|
||
def initialize(options = {}) | ||
requires!(options, :login, :password) | ||
@options = options | ||
super | ||
end | ||
|
||
def authorize(money, credit_card, options = {}) | ||
post = {} | ||
add_sale_data(post, money, credit_card, options) | ||
commit(:authorization, post) | ||
end | ||
|
||
def purchase(money, credit_card, options = {}) | ||
post = {} | ||
add_sale_data(post, money, credit_card, options) | ||
commit(:purchase, post) | ||
end | ||
|
||
def capture(money, reference, options = {}) | ||
post = {} | ||
add_amount(post, money) | ||
add_reference(post, reference) | ||
commit(:capture, post) | ||
end | ||
|
||
def void(reference, options = {}) | ||
post = {} | ||
add_reference(post, reference) | ||
commit(:void, post) | ||
end | ||
|
||
def credit(money, reference, options = {}) | ||
post = {} | ||
add_amount(post, money) | ||
add_reference(post, reference) | ||
commit(:credit, post) | ||
end | ||
|
||
private | ||
def exp_date(credit_card) | ||
year = sprintf("%.4i", credit_card.year) | ||
month = sprintf("%.2i", credit_card.month) | ||
|
||
"#{month}#{year[-2..-1]}" | ||
end | ||
|
||
def add_invoice(post, options) | ||
post[:T_ordernum] = options[:order_id].slice(0, 20) | ||
post[:T_tax] = amount(options[:tax]) unless options[:tax].blank? | ||
post[:T_shipping] = amount(options[:tax]) unless options[:tax].blank? | ||
end | ||
|
||
def add_reference(post, reference) | ||
post[:T_reference] = reference | ||
end | ||
|
||
def add_amount(post, money) | ||
post[:T_amt] = amount(money) | ||
end | ||
|
||
def add_customer_data(post, options) | ||
post[:T_customer_number] = options[:customer] | ||
end | ||
|
||
def add_addresses(post, options) | ||
billing_address = options[:billing_address] || options[:address] || {} | ||
|
||
post[:C_address] = billing_address[:address1] | ||
post[:C_city] = billing_address[:city] | ||
post[:C_state] = billing_address[:state] | ||
post[:C_zip] = billing_address[:zip] | ||
post[:C_country] = billing_address[:country] | ||
post[:C_email] = options[:email] | ||
|
||
if shipping_address = options[:shipping_address] | ||
post[:C_ship_name] = shipping_address[:name] | ||
post[:C_ship_address] = shipping_address[:address1] | ||
post[:C_ship_city] = shipping_address[:city] | ||
post[:C_ship_state] = shipping_address[:state] | ||
post[:C_ship_zip] = shipping_address[:zip] | ||
post[:C_ship_country] = shipping_address[:country] | ||
post[:C_telephone] = shipping_address[:phone] | ||
post[:C_fax] = shipping_address[:fax] | ||
end | ||
end | ||
|
||
def add_credit_card(post, credit_card) | ||
post[:C_name] = credit_card.name | ||
post[:C_cardnumber] = credit_card.number | ||
post[:C_exp] = exp_date(credit_card) | ||
post[:C_cvv] = credit_card.verification_value if credit_card.verification_value? | ||
end | ||
|
||
def add_sale_data(post, money, credit_card, options) | ||
add_amount(post, money) | ||
add_invoice(post, options) | ||
add_credit_card(post, credit_card) | ||
add_addresses(post, options) | ||
add_customer_data(post, options) | ||
end | ||
|
||
def parse(data) | ||
response = {} | ||
response[:success] = data[1,1] | ||
response[:code] = data[2,6] | ||
response[:message] = data[8,32].strip | ||
response[:front_end] = data[40, 2] | ||
response[:cvv_result] = data[42, 1] | ||
response[:avs_result] = data[43, 1].strip | ||
response[:risk] = data[44, 2] | ||
response[:reference] = data[46, 10] | ||
|
||
response[:order_number], response[:recurring] = data[57...-1].split("\034") | ||
response | ||
end | ||
|
||
def commit(action, params) | ||
response = parse(ssl_post(URL, post_data(action, params))) | ||
|
||
Response.new(success?(response), response[:message], response, | ||
:test => test?, | ||
:authorization => response[:reference], | ||
:avs_result => { :code => response[:avs_result] }, | ||
:cvv_result => response[:cvv_result] | ||
) | ||
end | ||
|
||
def success?(response) | ||
response[:success] == 'A' | ||
end | ||
|
||
def post_data(action, params = {}) | ||
params[:M_id] = @options[:login] | ||
params[:M_key] = @options[:password] | ||
params[:T_code] = TRANSACTIONS[:action] | ||
|
||
params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&") | ||
end | ||
end | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
require File.dirname(__FILE__) + '/../../test_helper' | ||
|
||
class RemoteSageTest < Test::Unit::TestCase | ||
|
||
def setup | ||
@gateway = SageGateway.new(fixtures(:sage)) | ||
|
||
@amount = 100 | ||
@credit_card = credit_card('4000100011112224') | ||
@declined_card = credit_card('4000300011112220') | ||
|
||
@options = { | ||
:order_id => generate_unique_id, | ||
:billing_address => address, | ||
:description => 'Store Purchase' | ||
} | ||
end | ||
|
||
def test_successful_purchase | ||
assert response = @gateway.purchase(@amount, @credit_card, @options) | ||
assert_success response | ||
assert_equal '', response.message | ||
end | ||
|
||
def test_unsuccessful_purchase | ||
assert response = @gateway.purchase(@amount, @declined_card, @options) | ||
assert_failure response | ||
assert_equal '', response.message | ||
end | ||
|
||
def test_authorize_and_capture | ||
assert auth = @gateway.authorize(@amount, @credit_card, @options) | ||
assert_success auth | ||
|
||
assert capture = @gateway.capture(@amount, auth.authorization) | ||
assert_success capture | ||
end | ||
|
||
def test_failed_capture | ||
assert response = @gateway.capture(@amount, '') | ||
assert_failure response | ||
assert_equal '', response.message | ||
end | ||
|
||
def test_invalid_login | ||
gateway = SageGateway.new( | ||
:login => '', | ||
:password => '' | ||
) | ||
assert response = gateway.purchase(@amount, @credit_card, @options) | ||
assert_failure response | ||
assert_equal 'SECURITY VIOLATION', response.message | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
require File.dirname(__FILE__) + '/../../test_helper' | ||
|
||
class SageTest < Test::Unit::TestCase | ||
def setup | ||
@gateway = SageGateway.new( | ||
:login => 'login', | ||
:password => 'password' | ||
) | ||
|
||
@credit_card = credit_card | ||
@amount = 100 | ||
|
||
@options = { | ||
:order_id => '1', | ||
:billing_address => address, | ||
:description => 'Store Purchase' | ||
} | ||
end | ||
|
||
def test_successful_purchase | ||
@gateway.expects(:ssl_post).returns(successful_purchase_response) | ||
|
||
assert response = @gateway.purchase(@amount, @credit_card, @options) | ||
assert_instance_of Response, response | ||
assert_success response | ||
|
||
assert_equal "APPROVED", response.message | ||
assert_equal "1234567890", response.authorization | ||
|
||
assert_equal "A", response.params["success"] | ||
assert_equal "911911", response.params["code"] | ||
assert_equal "APPROVED", response.params["message"] | ||
assert_equal "00", response.params["front_end"] | ||
assert_equal "M", response.params["cvv_result"] | ||
assert_equal "X", response.params["avs_result"] | ||
assert_equal "00", response.params["risk"] | ||
assert_equal "1234567890", response.params["reference"] | ||
assert_equal "1000", response.params["order_number"] | ||
assert_equal "0", response.params["recurring"] | ||
end | ||
|
||
def test_unsuccessful_purchase | ||
@gateway.expects(:ssl_post).returns(failed_purchase_response) | ||
|
||
assert response = @gateway.purchase(@amount, @credit_card, @options) | ||
assert_failure response | ||
assert response.test? | ||
assert_equal "SECURITY VIOLATION", response.message | ||
assert_equal "0000000000", response.authorization | ||
|
||
assert_equal "X", response.params["success"] | ||
assert_equal "911911", response.params["code"] | ||
assert_equal "SECURITY VIOLATION", response.params["message"] | ||
assert_equal "00", response.params["front_end"] | ||
assert_equal "P", response.params["cvv_result"] | ||
assert_equal "", response.params["avs_result"] | ||
assert_equal "00", response.params["risk"] | ||
assert_equal "0000000000", response.params["reference"] | ||
assert_equal "", response.params["order_number"] | ||
assert_equal "0", response.params["recurring"] | ||
end | ||
|
||
def test_avs_result | ||
@gateway.expects(:ssl_post).returns(successful_purchase_response) | ||
|
||
response = @gateway.purchase(@amount, @credit_card, @options) | ||
assert_equal 'X', response.avs_result['code'] | ||
end | ||
|
||
def test_cvv_result | ||
@gateway.expects(:ssl_post).returns(successful_purchase_response) | ||
|
||
response = @gateway.purchase(@amount, @credit_card, @options) | ||
assert_equal 'M', response.cvv_result['code'] | ||
end | ||
|
||
private | ||
def successful_purchase_response | ||
"\002A911911APPROVED 00MX001234567890\0341000\0340\034\003" | ||
end | ||
|
||
def failed_purchase_response | ||
"\002X911911SECURITY VIOLATION 00P 000000000000\034\0340\034\003" | ||
end | ||
end |