require 'nokogiri' module ActiveMerchant #:nodoc: module Billing #:nodoc: class WorldpayUsGateway < Gateway class_attribute :backup_url self.display_name = 'Worldpay US' self.homepage_url = 'http://www.worldpay.com/us' # No sandbox, just use test cards. self.live_url = 'https://trans.worldpay.us/cgi-bin/process.cgi' self.backup_url = 'https://trans.gwtx01.com/cgi-bin/process.cgi' self.supported_countries = ['US'] self.default_currency = 'USD' self.money_format = :dollars self.supported_cardtypes = %i[visa master american_express discover jcb] def initialize(options={}) requires!(options, :acctid, :subid, :merchantpin) super end def purchase(money, payment_method, options={}) post = {} add_invoice(post, money, options) add_payment_method(post, payment_method) add_customer_data(post, options) commit('purchase', options, post) end def authorize(money, payment, options={}) post = {} add_invoice(post, money, options) add_credit_card(post, payment) add_customer_data(post, options) commit('authorize', options, post) end def capture(amount, authorization, options={}) post = {} add_invoice(post, amount, options) add_reference(post, authorization) add_customer_data(post, options) commit('capture', options, post) end def refund(amount, authorization, options={}) post = {} add_invoice(post, amount, options) add_reference(post, authorization) add_customer_data(post, options) commit('refund', options, post) end def void(authorization, options={}) post = {} add_reference(post, authorization) commit('void', options, post) 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((&?merchantpin=)[^&]*)i, '\1[FILTERED]'). gsub(%r((&?ccnum=)[^&]*)i, '\1[FILTERED]'). gsub(%r((&?ckacct=)[^&]*)i, '\1[FILTERED]'). gsub(%r((&?cvv2=)[^&]*)i, '\1[FILTERED]') end private def url(options) options[:use_backup_url].to_s == 'true' ? self.backup_url : self.live_url end def add_customer_data(post, options) if (billing_address = (options[:billing_address] || options[:address])) post[:ci_companyname] = billing_address[:company] post[:ci_billaddr1] = billing_address[:address1] post[:ci_billaddr2] = billing_address[:address2] post[:ci_billcity] = billing_address[:city] post[:ci_billstate] = billing_address[:state] post[:ci_billzip] = billing_address[:zip] post[:ci_billcountry] = billing_address[:country] post[:ci_phone] = billing_address[:phone] post[:ci_email] = billing_address[:email] post[:ci_ipaddress] = billing_address[:ip] end if (shipping_address = options[:shipping_address]) post[:ci_shipaddr1] = shipping_address[:address1] post[:ci_shipaddr2] = shipping_address[:address2] post[:ci_shipcity] = shipping_address[:city] post[:ci_shipstate] = shipping_address[:state] post[:ci_shipzip] = shipping_address[:zip] post[:ci_shipcountry] = shipping_address[:country] end end def add_invoice(post, money, options) post[:amount] = amount(money) post[:currencycode] = (options[:currency] || currency(money)) post[:merchantordernumber] = options[:order_id] if options[:order_id] end def add_payment_method(post, payment_method) if card_brand(payment_method) == 'check' add_check(post, payment_method) else add_credit_card(post, payment_method) end end def add_credit_card(post, payment_method) post[:ccname] = payment_method.name post[:ccnum] = payment_method.number post[:cvv2] = payment_method.verification_value post[:expyear] = format(payment_method.year, :four_digits) post[:expmon] = format(payment_method.month, :two_digits) end ACCOUNT_TYPES = { 'checking' => '1', 'savings' => '2' } def add_check(post, payment_method) post[:action] = 'ns_quicksale_check' post[:ckacct] = payment_method.account_number post[:ckaba] = payment_method.routing_number post[:ckno] = payment_method.number post[:ckaccttype] = ACCOUNT_TYPES[payment_method.account_type] if ACCOUNT_TYPES[payment_method.account_type] end def split_authorization(authorization) historyid, orderid = authorization.split('|') [historyid, orderid] end def add_reference(post, authorization) historyid, orderid = split_authorization(authorization) post[:postonly] = historyid post[:historykeyid] = historyid post[:orderkeyid] = orderid end def parse(xml) response = {} doc = Nokogiri::XML(xml) message = doc.xpath('//plaintext') message.text.split(/\r?\n/).each do |line| key, value = line.split(%r{=}) response[key] = value if key end response end ACTIONS = { 'purchase' => 'ns_quicksale_cc', 'refund' => 'ns_credit', 'authorize' => 'ns_quicksale_cc', 'capture' => 'ns_quicksale_cc', 'void' => 'ns_void' } def commit(action, options, post) post[:action] = ACTIONS[action] unless post[:action] post[:acctid] = @options[:acctid] post[:subid] = @options[:subid] post[:merchantpin] = @options[:merchantpin] post[:authonly] = '1' if action == 'authorize' raw = parse(ssl_post(url(options), post.to_query)) succeeded = success_from(raw['result']) Response.new( succeeded, message_from(succeeded, raw), raw, authorization: authorization_from(raw), test: test? ) end def success_from(result) result == '1' end def message_from(succeeded, response) if succeeded 'Succeeded' else (response['transresult'] || response['Reason'] || 'Unable to read error message') end end def authorization_from(response) [response['historyid'], response['orderid']].join('|') end end end end