Skip to content

Commit

Permalink
Add support for Sage Payment Solutions gateway
Browse files Browse the repository at this point in the history
  • Loading branch information
Cody Fauser committed May 18, 2008
1 parent 035a24b commit 709aaa1
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
@@ -1,5 +1,6 @@
= ActiveMerchant CHANGELOG

* Add support for Sage Payment Solutions gateway [cody]
* Add test mode detection to Beanstream [cody]
* Add support for Beanstream payment gateway [xiaobozz]
* Add support for PayPal NV Pair API. Will be used to replace the usage of the PayPal SOAP API in ActiveMerchant in the future [Greg Furmanek, cody]
Expand Down
168 changes: 168 additions & 0 deletions lib/active_merchant/billing/gateways/sage.rb
@@ -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

4 changes: 4 additions & 0 deletions test/fixtures.yml
Expand Up @@ -241,6 +241,10 @@ realex_mastercard_coms_error:
year: '2020'
verification_value: '123'

sage:
login: login
password: password

secure_pay:
login: LOGIN
password: PASSWORD
Expand Down
54 changes: 54 additions & 0 deletions test/remote/gateways/remote_sage_test.rb
@@ -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
85 changes: 85 additions & 0 deletions test/unit/gateways/sage_test.rb
@@ -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

0 comments on commit 709aaa1

Please sign in to comment.