module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class LitleGateway < Gateway
# Specific to Litle options:
# * :merchant_id - Merchant Id assigned by Litle
# * :user - Username assigned by Litle
# * :password - Password assigned by Litle
# * :version - The version of the api you are using (eg, '8.10')
# * :proxy_addr - Proxy address - nil if not needed
# * :proxy_port - Proxy port - nil if not needed
# * :url - URL assigned by Litle (for testing, use the sandbox)
#
# Standard Active Merchant options
# * :order_id - The order number
# * :ip - The IP address of the customer making the purchase
# * :customer - The name, customer number, or other information that identifies the customer
# * :invoice - The invoice number
# * :merchant - The name or description of the merchant offering the product
# * :description - A description of the transaction
# * :email - The email address of the customer
# * :currency - The currency of the transaction. Only important when you are using a currency that is not the default with a gateway that supports multiple currencies.
# * :billing_address - A hash containing the billing address of the customer.
# * :shipping_address - A hash containing the shipping address of the customer.
#
# The :billing_address, and :shipping_address hashes can have the following keys:
#
# * :name - The full name of the customer.
# * :company - The company name of the customer.
# * :address1 - The primary street address of the customer.
# * :address2 - Additional line of address information.
# * :city - The city of the customer.
# * :state - The state of the customer. The 2 digit code for US and Canadian addresses. The full name of the state or province for foreign addresses.
# * :country - The [ISO 3166-1-alpha-2 code](http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm) for the customer.
# * :zip - The zip or postal code of the customer.
# * :phone - The phone number of the customer.
self.test_url = 'https://www.testlitle.com/sandbox/communicator/online'
self.live_url = 'https://payments.litle.com/vap/communicator/online'
LITLE_SCHEMA_VERSION = '8.10'
# The countries the gateway supports merchants from as 2 digit ISO country codes
self.supported_countries = ['US']
# The card types supported by the payment gateway
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
# The homepage URL of the gateway
self.homepage_url = 'http://www.litle.com/'
# The name of the gateway
self.display_name = 'Litle & Co.'
self.default_currency = 'USD'
def initialize(options = {})
begin
require 'LitleOnline'
rescue LoadError
raise "Could not load the LitleOnline gem (>= 08.13.2). Use `gem install LitleOnline` to install it."
end
@litle = LitleOnline::LitleOnlineRequest.new
options[:version] ||= LITLE_SCHEMA_VERSION
options[:merchant] ||= 'Default Report Group'
options[:user] ||= options[:login]
requires!(options, :merchant_id, :user, :password, :merchant, :version)
super
end
def authorize(money, creditcard, options = {})
to_pass = create_credit_card_hash(money, creditcard, options)
build_response(:authorization, @litle.authorization(to_pass))
end
def purchase(money, creditcard, options = {})
to_pass = create_credit_card_hash(money, creditcard, options)
build_response(:sale, @litle.sale(to_pass))
end
def capture(money, authorization, options = {})
to_pass = create_capture_hash(money, authorization, options)
build_response(:capture, @litle.capture(to_pass))
end
def void(identification, options = {})
to_pass = create_void_hash(identification, options)
build_response(:void, @litle.void(to_pass))
end
def credit(money, identification, options = {})
to_pass = create_credit_hash(money, identification, options)
build_response(:credit, @litle.credit(to_pass))
end
def store(creditcard, options = {})
to_pass = create_token_hash(creditcard, options)
build_response(:registerToken, @litle.register_token_request(to_pass), %w(801 802))
end
private
CARD_TYPE = {
'visa' => 'VI',
'master' => 'MC',
'american_express' => 'AX',
'discover' => 'DI',
'jcb' => 'DI',
'diners_club' => 'DI'
}
AVS_RESPONSE_CODE = {
'00' => 'Y',
'01' => 'X',
'02' => 'D',
'10' => 'Z',
'11' => 'W',
'12' => 'A',
'13' => 'A',
'14' => 'P',
'20' => 'N',
'30' => 'S',
'31' => 'R',
'32' => 'U',
'33' => 'R',
'34' => 'I',
'40' => 'E'
}
def url
return @options[:url] if @options[:url].present?
test? ? self.test_url : self.live_url
end
def build_response(kind, litle_response, valid_responses=%w(000))
response = Hash.from_xml(litle_response.raw_xml.to_s)['litleOnlineResponse']
if response['response'] == "0"
detail = response["#{kind}Response"]
fraud = fraud_result(detail)
Response.new(
valid_responses.include?(detail['response']),
detail['message'],
{:litleOnlineResponse => response},
:authorization => detail['litleTxnId'],
:avs_result => {:code => fraud['avs']},
:cvv_result => fraud['cvv'],
:test => test?
)
else
Response.new(false, response['message'], :litleOnlineResponse => response, :test => test?)
end
end
def create_credit_card_hash(money, creditcard, options)
cc_type = CARD_TYPE[creditcard.brand]
exp_date_yr = creditcard.year.to_s()[2..3]
if( creditcard.month.to_s().length == 1 )
exp_date_mo = '0' + creditcard.month.to_s()
else
exp_date_mo = creditcard.month.to_s()
end
exp_date = exp_date_mo + exp_date_yr
card_info = {
'type' => cc_type,
'number' => creditcard.number,
'expDate' => exp_date,
'cardValidationNum' => creditcard.verification_value
}
hash = create_hash(money, options)
hash['card'] = card_info
hash
end
def create_capture_hash(money, authorization, options)
hash = create_hash(money, options)
hash['litleTxnId'] = authorization
hash
end
def create_credit_hash(money, identification, options)
hash = create_hash(money, options)
hash['litleTxnId'] = identification
hash['orderSource'] = nil
hash['orderId'] = nil
hash
end
def create_token_hash(creditcard, options)
hash = create_hash(0, options)
hash['accountNumber'] = creditcard.number
hash
end
def create_void_hash(identification, options)
hash = create_hash(nil, options)
hash['litleTxnId'] = identification
hash
end
def create_hash(money, options)
fraud_check_type = {}
if options[:ip]
fraud_check_type['customerIpAddress'] = options[:ip]
end
enhanced_data = {}
if options[:invoice]
enhanced_data['invoiceReferenceNumber'] = options[:invoice]
end
if options[:description]
enhanced_data['customerReference'] = options[:description]
end
if options[:billing_address]
bill_to_address = {
'name' => options[:billing_address][:name],
'companyName' => options[:billing_address][:company],
'addressLine1' => options[:billing_address][:address1],
'addressLine2' => options[:billing_address][:address2],
'city' => options[:billing_address][:city],
'state' => options[:billing_address][:state],
'zip' => options[:billing_address][:zip],
'country' => options[:billing_address][:country],
'email' => options[:email],
'phone' => options[:billing_address][:phone]
}
end
if options[:shipping_address]
ship_to_address = {
'name' => options[:shipping_address][:name],
'companyName' => options[:shipping_address][:company],
'addressLine1' => options[:shipping_address][:address1],
'addressLine2' => options[:shipping_address][:address2],
'city' => options[:shipping_address][:city],
'state' => options[:shipping_address][:state],
'zip' => options[:shipping_address][:zip],
'country' => options[:shipping_address][:country],
'email' => options[:email],
'phone' => options[:shipping_address][:phone]
}
end
hash = {
'billToAddress' => bill_to_address,
'shipToAddress' => ship_to_address,
'orderId' => (options[:order_id] || @options[:order_id]),
'customerId' => options[:customer],
'reportGroup' => (options[:merchant] || @options[:merchant]),
'merchantId' => (options[:merchant_id] || @options[:merchant_id]),
'orderSource' => 'ecommerce',
'enhancedData' => enhanced_data,
'fraudCheckType' => fraud_check_type,
'user' => (options[:user] || @options[:user]),
'password' => (options[:password] || @options[:password]),
'version' => (options[:version] || @options[:version]),
'url' => (options[:url] || url),
'proxy_addr' => (options[:proxy_addr] || @options[:proxy_addr]),
'proxy_port' => (options[:proxy_port] || @options[:proxy_port]),
'id' => (options[:id] || options[:order_id] || @options[:order_id])
}
if( !money.nil? && money.to_s.length > 0 )
hash.merge!({'amount' => money})
end
hash
end
def fraud_result(authorization_response)
if result = authorization_response['fraudResult']
if result.key?('cardValidationResult')
cvv_to_pass = result['cardValidationResult'].blank? ? "P" : result['cardValidationResult']
end
avs_to_pass = AVS_RESPONSE_CODE[result['avsResult']] unless result['avsResult'].blank?
end
{'cvv'=>cvv_to_pass, 'avs'=>avs_to_pass}
end
end
end
end