require 'nokogiri' module ActiveMerchant #:nodoc: module Billing #:nodoc: class BamboraApacGateway < Gateway self.live_url = 'https://www.bambora.co.nz/interface/api' self.test_url = 'https://demo.bambora.co.nz/interface/api' self.supported_countries = %w[AU NZ] self.supported_cardtypes = %i[visa master american_express diners_club jcb] self.homepage_url = 'http://www.bambora.com/' self.display_name = 'Bambora Asia-Pacific' self.money_format = :cents STANDARD_ERROR_CODE_MAPPING = { '05' => STANDARD_ERROR_CODE[:card_declined], '06' => STANDARD_ERROR_CODE[:processing_error], '14' => STANDARD_ERROR_CODE[:invalid_number], '54' => STANDARD_ERROR_CODE[:expired_card] } def initialize(options = {}) requires!(options, :username, :password) super end def purchase(money, payment, options = {}) commit('SubmitSinglePayment') do |xml| xml.Transaction do xml.CustRef options[:order_id] xml.Amount amount(money) xml.TrnType '1' add_payment(xml, payment) add_credentials(xml, options) xml.TrnSource options[:ip] end end end def authorize(money, payment, options = {}) commit('SubmitSinglePayment') do |xml| xml.Transaction do xml.CustRef options[:order_id] xml.Amount amount(money) xml.TrnType '2' add_payment(xml, payment) add_credentials(xml, options) xml.TrnSource options[:ip] end end end def capture(money, authorization, options = {}) commit('SubmitSingleCapture') do |xml| xml.Capture do xml.Receipt authorization xml.Amount amount(money) add_credentials(xml, options) end end end def refund(money, authorization, options = {}) commit('SubmitSingleRefund') do |xml| xml.Refund do xml.Receipt authorization xml.Amount amount(money) add_credentials(xml, options) end end end def void(authorization, options = {}) commit('SubmitSingleVoid') do |xml| xml.Void do xml.Receipt authorization xml.Amount amount(options[:amount]) add_credentials(xml, options) end end end def store(payment, options = {}) commit('TokeniseCreditCard') do |xml| xml.TokeniseCreditCard do xml.CardNumber payment.number xml.ExpM format(payment.month, :two_digits) xml.ExpY format(payment.year, :four_digits) xml.TokeniseAlgorithmID options[:tokenise_algorithm_id] || 2 xml.UserName @options[:username] xml.Password @options[:password] end 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 add_credentials(xml, options) xml.AccountNumber options[:account_number] if options[:account_number] xml.Security do xml.UserName @options[:username] xml.Password @options[:password] end end def add_payment(xml, payment) if payment.is_a?(String) add_token(xml, payment) else add_credit_card(xml, payment) end end def add_token(xml, payment) xml.CreditCard do xml.TokeniseAlgorithmID options[:tokenise_algorithm_id] || 2 xml.CardNumber payment end end def add_credit_card(xml, payment) xml.CreditCard Registered: 'False' do xml.CardNumber payment.number xml.ExpM format(payment.month, :two_digits) xml.ExpY format(payment.year, :four_digits) xml.CVN payment.verification_value xml.CardHolderName payment.name end end def parse(body) element = Nokogiri::XML(body).root.first_element_child.first_element_child response = {} doc = Nokogiri::XML(element) doc.root.elements.each do |e| response[e.name.underscore.to_sym] = e.inner_text end response end def commit(action, &block) headers = { 'Content-Type' => 'text/xml; charset=utf-8', 'SOAPAction' => "http://www.ippayments.com.au/interface/api/#{endpoint(action)}/#{action}" } response = parse(ssl_post("#{commit_url}/#{endpoint(action)}.asmx", new_submit_xml(action, &block), headers)) Response.new( success_from(response), message_from(response), response, authorization: authorization_from(response), error_code: error_code_from(response), test: test? ) end def new_submit_xml(action) xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! xml.soap :Envelope, 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/' do xml.soap :Body do xml.__send__(action, 'xmlns' => "http://www.ippayments.com.au/interface/api/#{endpoint(action)}") do if action == 'TokeniseCreditCard' xml.tokeniseCreditCardXML do inner_xml = Builder::XmlMarkup.new(indent: 2) yield(inner_xml) xml.cdata!(inner_xml.target!) end else xml.trnXML do inner_xml = Builder::XmlMarkup.new(indent: 2) yield(inner_xml) xml.cdata!(inner_xml.target!) end end end end end xml.target! end def endpoint(action) action == 'TokeniseCreditCard' ? 'sipp' : 'dts' end def commit_url test? ? test_url : live_url end def success_from(response) response[:response_code] == '0' || response[:return_value] == '0' end def error_code_from(response) STANDARD_ERROR_CODE_MAPPING[response[:declined_code]] end def message_from(response) success_from(response) ? 'Succeeded' : response[:declined_message] end def authorization_from(response) response[:receipt] || response[:token] end end end end