require 'nokogiri'
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class BridgePayGateway < Gateway
self.display_name = 'BridgePay'
self.homepage_url = 'http://www.bridgepaynetwork.com/'
self.test_url = 'https://gatewaystage.itstgate.com/SmartPayments/transact3.asmx'
self.live_url = 'https://gateway.itstgate.com/SmartPayments/transact3.asmx'
self.supported_countries = ['CA', 'US']
self.default_currency = 'USD'
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
def initialize(options={})
requires!(options, :user_name, :password)
super
end
def purchase(amount, payment_method, options={})
post = initialize_required_fields('Sale')
# Allow the same amount in multiple transactions.
post[:ExtData] = 'T'
add_invoice(post, amount, options)
add_payment_method(post, payment_method)
add_customer_data(post, options)
commit(post)
end
def authorize(amount, payment_method, options={})
post = initialize_required_fields('Auth')
add_invoice(post, amount, options)
add_payment_method(post, payment_method)
add_customer_data(post, options)
commit(post)
end
def capture(amount, authorization, options={})
post = initialize_required_fields('Force')
add_invoice(post, amount, options)
add_reference(post, authorization)
add_customer_data(post, options)
commit(post)
end
def refund(amount, authorization, options={})
post = initialize_required_fields('Return')
add_invoice(post, amount, options)
add_reference(post, authorization)
commit(post)
end
def void(authorization, options={})
post = initialize_required_fields('Void')
add_reference(post, authorization)
commit(post)
end
def verify(creditcard, options = {})
MultiResponse.run(:use_first_response) do |r|
r.process { authorize(100, creditcard, options) }
r.process(:ignore_result) { void(r.authorization, options) }
end
end
def store(creditcard, options={})
post = initialize_required_fields('')
post[:transaction] = 'Create'
post[:CardNumber] = creditcard.number
post[:CustomerPaymentInfoKey] = ''
post[:token] = ''
add_payment_method(post, creditcard)
commit(post)
end
def supports_scrubbing?
true
end
def scrub(transcript)
transcript.
gsub(%r((&?CardNum=)[^&]*)i, '\1[FILTERED]').
gsub(%r((&?CVNum=)[^&]*)i, '\1[FILTERED]').
gsub(%r((&?Password=)[^&]*)i, '\1[FILTERED]').
gsub(%r((&?TransitNum=)[^&]*)i, '\1[FILTERED]').
gsub(%r((&?AccountNum=)[^&]*)i, '\1[FILTERED]')
end
private
def add_payment_method(post, payment_method)
if payment_method.respond_to? :brand
post[:NameOnCard] = payment_method.name if payment_method.name
post[:ExpDate] = expdate(payment_method)
post[:CardNum] = payment_method.number
post[:CVNum] = payment_method.verification_value
elsif payment_method.is_a?(String)
add_token(post, payment_method)
else
post[:CheckNum] = payment_method.number
post[:TransitNum] = payment_method.routing_number
post[:AccountNum] = payment_method.account_number
post[:NameOnCheck] = payment_method.name
post[:ExtData] = "#{payment_method.account_type.capitalize}" if payment_method.account_type
end
end
def add_token(post, payment_method)
payment_method = payment_method.split('|')
post[:ExtData] = "TRead#{payment_method[1]}#{payment_method[0]}#{payment_method[2]}"
end
def initialize_required_fields(transaction_type)
post = {}
post[:TransType] = transaction_type
post[:Amount] = ''
post[:PNRef] = ''
post[:InvNum] = ''
post[:CardNum] = ''
post[:ExpDate] = ''
post[:MagData] = ''
post[:NameOnCard] = ''
post[:Zip] = ''
post[:Street] = ''
post[:CVNum] = ''
post[:MagData] = ''
post[:ExtData] = ''
post[:MICR] = ''
post[:DL] = ''
post[:SS] = ''
post[:DOB] = ''
post[:StateCode] = ''
post[:CheckType] = ''
post
end
def add_customer_data(post, options)
if(billing_address = (options[:billing_address] || options[:address]))
post[:Street] = billing_address[:address1]
post[:Zip] = billing_address[:zip]
end
end
def add_invoice(post, amount, options)
post[:Amount] = amount(amount)
post[:InvNum] = options[:order_id]
end
def expdate(creditcard)
"#{format(creditcard.month, :two_digits)}#{format(creditcard.year, :two_digits)}"
end
def parse(xml)
response = {}
doc = Nokogiri::XML(xml)
doc.root&.xpath('*')&.each do |node|
if node.elements.size == 0
response[node.name.downcase.to_sym] = node.text
else
node.elements.each do |childnode|
name = "#{node.name.downcase}_#{childnode.name.downcase}"
response[name.to_sym] = childnode.text
end
end
end
response
end
def commit(parameters)
data = post_data(parameters)
raw = parse(ssl_post(url(parameters), data))
Response.new(
success_from(raw),
message_from(raw),
raw,
authorization: authorization_from(raw),
test: test?
)
end
def url(params)
if params[:transaction]
"#{base_url}/ManageCardVault"
else
action = params[:TransitNum] ? 'ProcessCheck' : 'ProcessCreditCard'
"#{base_url}/#{action}"
end
end
def base_url
test? ? test_url : live_url
end
def success_from(response)
response[:result] == '0'
end
def message_from(response)
response[:respmsg] || response[:message]
end
def authorization_from(response)
if response[:token]
[response[:token], response[:customerpaymentinfokey], response[:expdate]].join('|')
else
[response[:authcode], response[:pnref]].join('|')
end
end
def split_authorization(authorization)
authcode, pnref = authorization.split('|')
[authcode, pnref]
end
def add_reference(post, authorization)
authcode, pnref = split_authorization(authorization)
post[:AuthCode] = authcode
post[:PNRef] = pnref
end
def post_data(post)
{
:UserName => @options[:user_name],
:Password => @options[:password]
}.merge(post).collect { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
end
end
end
end