require 'nokogiri'
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class CreditcallGateway < Gateway
include Empty
self.test_url = 'https://test.cardeasexml.com/generic.cex'
self.live_url = 'https://live.cardeasexml.com/generic.cex'
self.supported_countries = ['US']
self.default_currency = 'USD'
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
self.homepage_url = 'https://www.creditcall.com'
self.display_name = 'Creditcall'
CVV_CODE = {
'matched' => 'M',
'notmatched' => 'N',
'notchecked' => 'P',
'partialmatch' => 'N'
}
AVS_CODE = {
'matched;matched' => 'D',
'matched;notchecked' =>'B',
'matched;notmatched' => 'A',
'matched;partialmatch' => 'A',
'notchecked;matched' => 'P',
'notchecked;notchecked' =>'I',
'notchecked;notmatched' => 'I',
'notchecked;partialmatch' => 'I',
'notmatched;matched' => 'W',
'notmatched;notchecked' =>'C',
'notmatched;notmatched' => 'C',
'notmatched;partialmatch' => 'C',
'partialmatched;matched' => 'W',
'partialmatched;notchecked' =>'C',
'partialmatched;notmatched' => 'C',
'partialmatched;partialmatch' => 'C'
}
def initialize(options={})
requires!(options, :terminal_id, :transaction_key)
super
end
def purchase(money, payment_method, options={})
multi_response = MultiResponse.run do |r|
r.process { authorize(money, payment_method, options) }
r.process { capture(money, r.authorization, options) }
end
merged_params = multi_response.responses.map(&:params).reduce({}, :merge)
Response.new(
multi_response.primary_response.success?,
multi_response.primary_response.message,
merged_params,
authorization: multi_response.responses.first.authorization,
avs_result: AVSResult.new(code: avs_result_code_from(merged_params)),
cvv_result: CVVResult.new(cvv_result_code_from(merged_params)),
error_code: error_result_code_from(merged_params),
test: test?
)
end
def authorize(money, payment_method, options={})
request = build_xml_request do |xml|
add_transaction_details(xml, money, nil, 'Auth', options)
add_terminal_details(xml, options)
add_card_details(xml, payment_method, options)
end
commit(request)
end
def capture(money, authorization, options={})
request = build_xml_request do |xml|
add_transaction_details(xml, money, authorization, 'Conf', options)
add_terminal_details(xml, options)
end
commit(request)
end
def refund(money, authorization, options={})
request = build_xml_request do |xml|
add_transaction_details(xml, money, authorization, 'Refund', options)
add_terminal_details(xml, options)
end
commit(request)
end
def void(authorization, options={})
request = build_xml_request do |xml|
add_transaction_details(xml, nil, authorization, 'Void', options)
add_terminal_details(xml, options)
end
commit(request)
end
def verify(credit_card, options={})
MultiResponse.run(:use_first_response) do |r|
r.process { authorize(100, credit_card, options) }
r.process(:ignore_result) { void(r.authorization, options) }
end
end
def supports_scrubbing?
true
end
def scrub(transcript)
transcript.
gsub(%r(().+?())i, '\1[FILTERED]\2').
gsub(%r(().+?())i, '\1[FILTERED]\2').
gsub(%r(().+?())i, '\1[FILTERED]\2')
end
private
def avs_result_code_from(params)
AVS_CODE["#{params['Address']};#{params['Zip']}"]
end
def cvv_result_code_from(params)
CVV_CODE[params['CSC']]
end
def error_result_code_from(params)
params['ErrorCode']
end
def build_xml_request
builder = Nokogiri::XML::Builder.new do |xml|
xml.Request(type: 'CardEaseXML', version: '1.0.0') do
yield(xml)
end
end
builder.to_xml
end
def add_transaction_details(xml, amount, authorization, type, options={})
xml.TransactionDetails do
xml.MessageType type
xml.Amount(unit: 'Minor'){ xml.text(amount) } if amount
xml.CardEaseReference authorization if authorization
xml.VoidReason '01' if type == 'Void'
end
end
def add_terminal_details(xml, options={})
xml.TerminalDetails do
xml.TerminalID @options[:terminal_id]
xml.TransactionKey @options[:transaction_key]
xml.Software(version: 'SoftwareVersion'){ xml.text('SoftwareName') }
end
end
def add_card_details(xml, payment_method, options={})
xml.CardDetails do
xml.Manual(type: manual_type(options)) do
xml.PAN payment_method.number
xml.ExpiryDate exp_date(payment_method)
xml.CSC payment_method.verification_value unless empty?(payment_method.verification_value)
end
add_additional_verification(xml, options)
end
end
def add_additional_verification(xml, options)
return unless (options[:verify_zip].to_s == 'true') || (options[:verify_address].to_s == 'true')
if address = options[:billing_address]
xml.AdditionalVerification do
xml.Zip address[:zip] if options[:verify_zip].to_s == 'true'
xml.Address address[:address1] if options[:verify_address].to_s == 'true'
end
end
end
def exp_date(payment_method)
"#{format(payment_method.year, :two_digits)}#{format(payment_method.month, :two_digits)}"
end
def parse(body)
response = {}
xml = Nokogiri::XML(body)
node = xml.xpath('//Response/TransactionDetails')
node.children.each do |childnode|
response[childnode.name] = childnode.text
end
node = xml.xpath('//Response/Result')
node.children.each do |childnode|
if childnode.elements.empty?
response[childnode.name] = childnode.text
else
childnode_to_response(response, childnode)
end
end
node = xml.xpath('//Response/CardDetails')
node.children.each do |childnode|
if childnode.elements.empty?
response[childnode.name] = childnode.text
else
childnode_to_response(response, childnode)
end
end
response
end
def childnode_to_response(response, childnode)
childnode.elements.each do |element|
if element.name == 'Error'
response['ErrorCode'] = element.attr('code')
response['ErrorMessage'] = element.text
else
response[element.name] = element.text
end
end
end
def commit(parameters)
response = parse(ssl_post(url, parameters))
Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(response),
avs_result: AVSResult.new(code: avs_result_code_from(response)),
cvv_result: CVVResult.new(cvv_result_code_from(response)),
error_code: error_result_code_from(response),
test: test?
)
end
def url
test? ? test_url : live_url
end
def success_from(response)
response['LocalResult'] == '0' || response['LocalResult'] == '00'
end
def message_from(response)
if success_from(response)
'Succeeded'
else
response['ErrorMessage']
end
end
def authorization_from(response)
response['CardEaseReference']
end
def manual_type(options)
options[:manual_type] || 'ecommerce'
end
end
end
end