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