module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class IatsPaymentsGateway < Gateway
class_attribute :live_na_url, :live_uk_url
self.live_na_url = 'https://www.iatspayments.com/NetGate'
self.live_uk_url = 'https://www.uk.iatspayments.com/NetGate'
self.supported_countries = %w(AU BR CA CH DE DK ES FI FR GR HK IE IT NL NO PT SE SG TR GB US TH ID PH BE)
self.default_currency = 'USD'
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
self.homepage_url = 'http://home.iatspayments.com/'
self.display_name = 'iATS Payments'
ACTIONS = {
purchase: 'ProcessCreditCard',
purchase_check: 'ProcessACHEFT',
purchase_customer_code: 'ProcessCreditCardWithCustomerCode',
refund: 'ProcessCreditCardRefundWithTransactionId',
refund_check: 'ProcessACHEFTRefundWithTransactionId',
store: 'CreateCreditCardCustomerCode',
unstore: 'DeleteCustomerCode'
}
def initialize(options={})
if options[:login]
ActiveMerchant.deprecated("The 'login' option is deprecated in favor of 'agent_code' and will be removed in a future version.")
options[:agent_code] = options[:login]
end
options[:region] = 'na' unless options[:region]
requires!(options, :agent_code, :password, :region)
super
end
def purchase(money, payment, options={})
post = {}
add_invoice(post, money, options)
add_payment(post, payment)
add_address(post, options)
add_ip(post, options)
add_description(post, options)
commit(determine_purchase_type(payment), post)
end
def refund(money, authorization, options={})
post = {}
transaction_id, payment_type = split_authorization(authorization)
post[:transaction_id] = transaction_id
add_invoice(post, -money, options)
add_ip(post, options)
add_description(post, options)
commit((payment_type == 'check' ? :refund_check : :refund), post)
end
def store(credit_card, options = {})
post = {}
add_payment(post, credit_card)
add_address(post, options)
add_ip(post, options)
add_description(post, options)
add_store_defaults(post)
commit(:store, post)
end
def unstore(authorization, options = {})
post = {}
post[:customer_code] = authorization
add_ip(post, options)
commit(:unstore, post)
end
def supports_scrubbing?
true
end
def scrub(transcript)
transcript.
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2')
end
private
def determine_purchase_type(payment)
if payment.is_a?(String)
:purchase_customer_code
elsif payment.is_a?(Check)
:purchase_check
else
:purchase
end
end
def add_ip(post, options)
post[:customer_ip_address] = options[:ip] if options.has_key?(:ip)
end
def add_address(post, options)
billing_address = options[:billing_address] || options[:address]
if billing_address
post[:address] = billing_address[:address1]
post[:city] = billing_address[:city]
post[:state] = billing_address[:state]
post[:zip_code] = billing_address[:zip]
post[:phone] = billing_address[:phone] if billing_address[:phone]
post[:email] = billing_address[:email] if billing_address[:email]
post[:country] = billing_address[:country] if billing_address[:country]
end
end
def add_invoice(post, money, options)
post[:invoice_num] = options[:order_id] if options[:order_id]
post[:total] = amount(money)
end
def add_description(post, options)
post[:comment] = options[:description] if options[:description]
end
def add_payment(post, payment)
if payment.is_a?(String)
post[:customer_code] = payment
elsif payment.is_a?(Check)
add_check(post, payment)
else
add_credit_card(post, payment)
end
end
def add_credit_card(post, payment)
post[:first_name] = payment.first_name
post[:last_name] = payment.last_name
post[:credit_card_num] = payment.number
post[:credit_card_expiry] = expdate(payment)
post[:cvv2] = payment.verification_value if payment.verification_value?
post[:mop] = creditcard_brand(payment.brand)
end
def add_check(post, payment)
post[:first_name] = payment.first_name
post[:last_name] = payment.last_name
post[:account_num] = "#{payment.routing_number}#{payment.account_number}"
post[:account_type] = payment.account_type.upcase
end
def add_store_defaults(post)
post[:recurring] = false
post[:begin_date] = Time.now.xmlschema
post[:end_date] = Time.now.xmlschema
post[:amount] = 0
end
def expdate(creditcard)
year = sprintf('%.4i', creditcard.year)
month = sprintf('%.2i', creditcard.month)
"#{month}/#{year[-2..-1]}"
end
def creditcard_brand(brand)
case brand
when 'visa' then 'VISA'
when 'master' then 'MC'
when 'discover' then 'DSC'
when 'american_express' then 'AMX'
when 'maestro' then 'MAESTR'
else
raise "Unhandled credit card brand #{brand}"
end
end
def commit(action, parameters)
response = parse(ssl_post(url(action), post_data(action, parameters),
{ 'Content-Type' => 'application/soap+xml; charset=utf-8'}))
Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(action, response),
test: test?
)
end
def endpoints
{
purchase: 'ProcessLinkv3.asmx',
purchase_check: 'ProcessLinkv3.asmx',
purchase_customer_code: 'ProcessLinkv3.asmx',
refund: 'ProcessLinkv3.asmx',
refund_check: 'ProcessLinkv3.asmx',
store: 'CustomerLinkv3.asmx',
unstore: 'CustomerLinkv3.asmx'
}
end
def url(action)
base_url = @options[:region] == 'uk' ? live_uk_url : live_na_url
"#{base_url}/#{endpoints[action]}?op=#{ACTIONS[action]}"
end
def parse(body)
response = {}
hashify_xml!(body, response)
response
end
def dexmlize_param_name(name)
names = {
'AUTHORIZATIONRESULT' => :authorization_result,
'SETTLEMENTBATCHDATE' => :settlement_batch_date,
'SETTLEMENTDATE' => :settlement_date,
'TRANSACTIONID' => :transaction_id
}
names[name] || name.to_s.downcase.intern
end
def hashify_xml!(xml, response)
xml = REXML::Document.new(xml)
xml.elements.each('//IATSRESPONSE/*') do |node|
recursively_parse_element(node, response)
end
end
def recursively_parse_element(node, response)
if node.has_elements?
node.elements.each { |n| recursively_parse_element(n, response) }
else
response[dexmlize_param_name(node.name)] = (node.text ? node.text.strip : nil)
end
end
def successful_result_message?(response)
response[:authorization_result] ? response[:authorization_result].start_with?('OK') : false
end
def success_from(response)
response[:status] == 'Success' && successful_result_message?(response)
end
def message_from(response)
if !successful_result_message?(response) && response[:authorization_result]
return response[:authorization_result].strip
elsif response[:status] == 'Failure'
return response[:errors]
else
response[:status]
end
end
def authorization_from(action, response)
if [:store, :unstore].include?(action)
response[:customercode]
elsif [:purchase_check].include?(action)
response[:transaction_id] ? "#{response[:transaction_id]}|check" : nil
else
response[:transaction_id]
end
end
def split_authorization(authorization)
authorization.split('|')
end
def envelope_namespaces
{
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
'xmlns:soap12' => 'http://www.w3.org/2003/05/soap-envelope'
}
end
def post_data(action, parameters = {})
xml = Builder::XmlMarkup.new
xml.instruct!(:xml, version: '1.0', encoding: 'utf-8')
xml.tag! 'soap12:Envelope', envelope_namespaces do
xml.tag! 'soap12:Body' do
xml.tag! ACTIONS[action], { 'xmlns' => 'https://www.iatspayments.com/NetGate/' } do
xml.tag!('agentCode', @options[:agent_code])
xml.tag!('password', @options[:password])
parameters.each do |name, value|
xml.tag!(xmlize_param_name(name), value)
end
end
end
end
xml.target!
end
def xmlize_param_name(name)
names = { customer_ip_address: 'customerIPAddress' }
names[name] || name.to_s.camelcase(:lower)
end
end
end
end