lib/active_merchant/billing/gateways/epay.rb
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class EpayGateway < Gateway
self.live_url = 'https://ssl.ditonlinebetalingssystem.dk/'
self.default_currency = 'DKK'
self.money_format = :cents
self.supported_countries = %w[DK SE NO]
self.supported_cardtypes = %i[dankort forbrugsforeningen visa master
american_express diners_club jcb maestro]
self.homepage_url = 'http://epay.dk/'
self.display_name = 'ePay'
CURRENCY_CODES = {
ADP: '020', AED: '784', AFA: '004', ALL: '008', AMD: '051',
ANG: '532', AOA: '973', ARS: '032', AUD: '036', AWG: '533',
AZM: '031', BAM: '977', BBD: '052', BDT: '050', BGL: '100',
BGN: '975', BHD: '048', BIF: '108', BMD: '060', BND: '096',
BOB: '068', BOV: '984', BRL: '986', BSD: '044', BTN: '064',
BWP: '072', BYR: '974', BZD: '084', CAD: '124', CDF: '976',
CHF: '756', CLF: '990', CLP: '152', CNY: '156', COP: '170',
CRC: '188', CUP: '192', CVE: '132', CYP: '196', CZK: '203',
DJF: '262', DKK: '208', DOP: '214', DZD: '012', ECS: '218',
ECV: '983', EEK: '233', EGP: '818', ERN: '232', ETB: '230',
EUR: '978', FJD: '242', FKP: '238', GBP: '826', GEL: '981',
GHC: '288', GIP: '292', GMD: '270', GNF: '324', GTQ: '320',
GWP: '624', GYD: '328', HKD: '344', HNL: '340', HRK: '191',
HTG: '332', HUF: '348', IDR: '360', ILS: '376', INR: '356',
IQD: '368', IRR: '364', ISK: '352', JMD: '388', JOD: '400',
JPY: '392', KES: '404', KGS: '417', KHR: '116', KMF: '174',
KPW: '408', KRW: '410', KWD: '414', KYD: '136', KZT: '398',
LAK: '418', LBP: '422', LKR: '144', LRD: '430', LSL: '426',
LTL: '440', LVL: '428', LYD: '434', MAD: '504', MDL: '498',
MGF: '450', MKD: '807', MMK: '104', MNT: '496', MOP: '446',
MRO: '478', MTL: '470', MUR: '480', MVR: '462', MWK: '454',
MXN: '484', MXV: '979', MYR: '458', MZM: '508', NAD: '516',
NGN: '566', NIO: '558', NOK: '578', NPR: '524', NZD: '554',
OMR: '512', PAB: '590', PEN: '604', PGK: '598', PHP: '608',
PKR: '586', PLN: '985', PYG: '600', QAR: '634', ROL: '642',
RUB: '643', RUR: '810', RWF: '646', SAR: '682', SBD: '090',
SCR: '690', SDD: '736', SEK: '752', SGD: '702', SHP: '654',
SIT: '705', SKK: '703', SLL: '694', SOS: '706', SRG: '740',
STD: '678', SVC: '222', SYP: '760', SZL: '748', THB: '764',
TJS: '972', TMM: '795', TND: '788', TOP: '776', TPE: '626',
TRL: '792', TRY: '949', TTD: '780', TWD: '901', TZS: '834',
UAH: '980', UGX: '800', USD: '840', UYU: '858', UZS: '860',
VEB: '862', VND: '704', VUV: '548', XAF: '950', XCD: '951',
XOF: '952', XPF: '953', YER: '886', YUM: '891', ZAR: '710',
ZMK: '894', ZWD: '716'
}
# login: merchant number
# password: referrer url (for authorize authentication)
def initialize(options = {})
requires!(options, :login)
super
end
def authorize(money, credit_card_or_reference, options = {})
post = {}
add_amount(post, money, options)
add_invoice(post, options)
add_creditcard_or_reference(post, credit_card_or_reference)
add_instant_capture(post, false)
add_3ds_auth(post, options)
commit(:authorize, post)
end
def purchase(money, credit_card_or_reference, options = {})
post = {}
add_amount(post, money, options)
add_creditcard_or_reference(post, credit_card_or_reference)
add_invoice(post, options)
add_instant_capture(post, true)
add_3ds_auth(post, options)
commit(:authorize, post)
end
def capture(money, authorization, options = {})
post = {}
add_reference(post, authorization)
add_amount_without_currency(post, money)
commit(:capture, post)
end
def void(identification, options = {})
post = {}
add_reference(post, identification)
commit(:void, post)
end
def refund(money, identification, options = {})
post = {}
add_amount_without_currency(post, money)
add_reference(post, identification)
commit(:credit, post)
end
def credit(money, identification, options = {})
ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
refund(money, identification, options)
end
def supports_scrubbing
true
end
def scrub(transcript)
transcript.
gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
gsub(%r(((?:\?|&)cardno=)\d*(&?)), '\1[FILTERED]\2').
gsub(%r((&?cvc=)\d*(&?)), '\1[FILTERED]\2')
end
private
def add_amount(post, money, options)
post[:amount] = amount(money)
post[:currency] = CURRENCY_CODES[(options[:currency] || currency(money)).to_sym]
end
def add_amount_without_currency(post, money)
post[:amount] = amount(money)
end
def add_reference(post, identification)
post[:transaction] = identification
end
def add_invoice(post, options)
post[:orderid] = format_order_number(options[:order_id])
end
def add_creditcard(post, credit_card)
post[:cardno] = credit_card.number
post[:cvc] = credit_card.verification_value
post[:expmonth] = credit_card.month
post[:expyear] = credit_card.year
end
def add_creditcard_or_reference(post, credit_card_or_reference)
if credit_card_or_reference.respond_to?(:number)
add_creditcard(post, credit_card_or_reference)
else
add_reference(post, credit_card_or_reference.to_s)
end
end
def add_instant_capture(post, option)
post[:instantcapture] = option ? 1 : 0
end
def add_3ds_auth(post, options)
if options[:three_d_secure]
post[:eci] = options.dig(:three_d_secure, :eci)
post[:xid] = options.dig(:three_d_secure, :xid)
post[:cavv] = options.dig(:three_d_secure, :cavv)
post[:threeds_version] = options.dig(:three_d_secure, :version)
post[:ds_transaction_id] = options.dig(:three_d_secure, :ds_transaction_id)
end
end
def commit(action, params)
response = send("do_#{action}", params)
if action == :authorize
Response.new(
response['accept'].to_i == 1,
response['errortext'],
response,
test: test?,
authorization: response['tid']
)
else
Response.new(
response['result'] == 'true',
messages(response['epay'], response['pbs']),
response,
test: test?,
authorization: params[:transaction]
)
end
end
def messages(epay, pbs = nil)
response = "ePay: #{epay}"
response << " PBS: #{pbs}" if pbs
return response
end
def soap_post(method, params)
data = xml_builder(params, method)
headers = make_headers(data, method)
REXML::Document.new(ssl_post(live_url + 'remote/payment.asmx', data, headers))
end
def do_authorize(params)
headers = {}
headers['Referer'] = (options[:password] || 'activemerchant.org')
response = raw_ssl_request(:post, live_url + 'auth/default.aspx', authorize_post_data(params), headers)
# Authorize gives the response back by redirecting with the values in
# the URL query
if location = response['Location']
query = CGI::parse(URI.parse(location.gsub(' ', '%20').gsub('<', '%3C').gsub('>', '%3E')).query)
else
return {
'accept' => '0',
'errortext' => 'ePay did not respond as expected. Please try again.',
'response_code' => response.code,
'response_message' => response.message
}
end
result = {}
query.each_pair do |k, v|
result[k] = v.is_a?(Array) && v.size == 1 ? v[0] : v # make values like ['v'] into 'v'
end
result
end
def do_capture(params)
response = soap_post('capture', params)
{
'result' => response.elements['//captureResponse/captureResult'].text,
'pbs' => response.elements['//captureResponse/pbsResponse'].text,
'epay' => response.elements['//captureResponse/epayresponse'].text
}
end
def do_credit(params)
response = soap_post('credit', params)
{
'result' => response.elements['//creditResponse/creditResult'].text,
'pbs' => response.elements['//creditResponse/pbsresponse'].text,
'epay' => response.elements['//creditResponse/epayresponse'].text
}
end
def do_void(params)
response = soap_post('delete', params)
{
'result' => response.elements['//deleteResponse/deleteResult'].text,
'epay' => response.elements['//deleteResponse/epayresponse'].text
}
end
def make_headers(data, soap_call)
{
'Content-Type' => 'text/xml; charset=utf-8',
'Host' => 'ssl.ditonlinebetalingssystem.dk',
'Content-Length' => data.size.to_s,
'SOAPAction' => self.live_url + 'remote/payment/' + soap_call
}
end
def xml_builder(params, soap_call)
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
xml.tag! 'soap:Envelope', { 'xmlns:xsi' => 'http://schemas.xmlsoap.org/soap/envelope/',
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/' } do
xml.tag! 'soap:Body' do
xml.tag! soap_call, { 'xmlns' => "#{self.live_url}remote/payment" } do
xml.tag! 'merchantnumber', @options[:login]
xml.tag! 'transactionid', params[:transaction]
xml.tag! 'amount', params[:amount].to_s if soap_call != 'delete'
end
end
end
xml.target!
end
def authorize_post_data(params = {})
params[:language] = '2'
params[:cms] = 'activemerchant_3ds'
params[:accepturl] = live_url + 'auth/default.aspx?accept=1'
params[:declineurl] = live_url + 'auth/default.aspx?decline=1'
params[:merchantnumber] = @options[:login]
params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
end
# Limited to 20 digits max
def format_order_number(number)
number.to_s.gsub(/[^\w]/, '').rjust(4, '0')[0...20]
end
end
end
end