module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class CardprocessGateway < Gateway
self.test_url = 'https://test.vr-pay-ecommerce.de/v1/payments'
self.live_url = 'https://vr-pay-ecommerce.de/v1/payments'
self.supported_countries = %w[ BE BG CZ DK DE EE IE ES FR HR IT CY LV LT LU
MT HU NL AT PL PT RO SI SK FI SE GB IS LI NO
CH ME MK AL RS TR BA ]
self.default_currency = 'EUR'
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]
self.homepage_url = 'https://vr-pay-ecommerce.docs.oppwa.com/'
self.display_name = 'CardProcess VR-Pay'
self.money_format = :dollars
STANDARD_ERROR_CODE_MAPPING = {}
# Creates a new CardProcess Gateway
#
# The gateway requires a valid login, password, and entity ID
# to be passed in the +options+ hash.
#
# === Options
#
# * :user_id -- The CardProcess user ID
# * :password -- The CardProcess password
# * :entity_id -- The CardProcess channel or entity ID for any transactions
def initialize(options={})
requires!(options, :user_id, :password, :entity_id)
super
# This variable exists purely to allow remote tests to force error codes;
# the lack of a setter despite its usage is intentional.
@test_options = {}
end
def purchase(money, payment, options = {})
post = {}
add_invoice(post, money, options)
add_payment(post, payment)
add_address(post, payment, options)
add_customer_data(post, options)
commit('DB', post)
end
def authorize(money, payment, options = {})
post = {}
add_invoice(post, money, options)
add_payment(post, payment)
add_address(post, payment, options)
add_customer_data(post, options)
commit('PA', post)
end
def capture(money, authorization, options = {})
post = {
id: authorization
}
add_invoice(post, money, options)
commit('CP', post)
end
def refund(money, authorization, options = {})
post = {
id: authorization
}
add_invoice(post, money, options)
commit('RF', post)
end
def credit(money, payment, options = {})
post = {}
add_invoice(post, money, options)
add_payment(post, payment)
add_address(post, payment, options)
add_customer_data(post, options)
commit('CD', post)
end
def void(authorization, _options = {})
post = {
id: authorization
}
commit('RV', post)
end
def verify(credit_card, options = {})
MultiResponse.run do |r|
r.process { authorize(100, credit_card, options) }
r.process { void(r.authorization, options) }
end
end
def supports_scrubbing?
true
end
def scrub(transcript)
transcript
.gsub(%r{(authentication\.[^=]+=)[^&]+}, '\1[FILTERED]')
.gsub(%r{(card\.number=)\d+}, '\1[FILTERED]')
.gsub(%r{(cvv=)\d{3,4}}, '\1[FILTERED]\2')
end
private
def add_customer_data(post, options)
post['customer.ip'] = options[:ip] if options[:ip]
end
def add_address(post, _card, options)
if (address = options[:billing_address] || options[:address])
post[:billing] = hashify_address(address)
end
if (shipping = options[:shipping_address])
post[:shipping] = hashify_address(shipping)
end
end
def add_invoice(post, money, options)
return if money.nil?
post[:amount] = amount(money)
post[:currency] = (options[:currency] || currency(money))
post[:merchantInvoiceId] = options[:merchant_invoice_id] if options[:merchant_invoice_id]
post[:merchantTransactionId] = options[:merchant_transaction_id] if options[:merchant_transaction_id]
post[:transactionCategory] = options[:transaction_category] if options[:transaction_category]
end
def add_payment(post, payment)
return if payment.is_a?(String)
post[:paymentBrand] = payment.brand.upcase if payment.brand
post[:card] ||= {}
post[:card][:number] = payment.number
post[:card][:holder] = payment.name
post[:card][:expiryMonth] = sprintf('%02d', payment.month)
post[:card][:expiryYear] = sprintf('%02d', payment.year)
post[:card][:cvv] = payment.verification_value
end
def parse(body)
JSON.parse(body)
end
def commit(action, parameters)
url = (test? ? test_url : live_url)
if (id = parameters.delete(:id))
url += "/#{id}"
end
begin
raw_response = ssl_post(url, post_data(action, parameters.merge(@test_options)))
rescue ResponseError => e
raw_response = e.response.body
end
response = parse(raw_response)
Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(response),
avs_result: AVSResult.new(code: response['result']['avsResponse']),
cvv_result: CVVResult.new(response['result']['cvvResponse']),
test: test?,
error_code: error_code_from(response)
)
end
def success_from(response)
!(response['result']['code'] =~ /^(000\.000\.|000\.100\.1|000\.[36])/).nil?
end
def message_from(response)
response['result']['description']
end
def authorization_from(response)
response['id']
end
def post_data(action, parameters = {})
post = parameters.clone
post[:authentication] ||= {}
post[:authentication][:userId] = @options[:user_id]
post[:authentication][:password] = @options[:password]
post[:authentication][:entityId] = @options[:entity_id]
post[:paymentType] = action
dot_flatten_hash(post).map {|key, value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
end
def error_code_from(response)
unless success_from(response)
case response['result']['code']
when '100.100.101'
STANDARD_ERROR_CODE[:incorrect_number]
when '100.100.303'
STANDARD_ERROR_CODE[:expired_card]
when /100\.100\.(201|301|305)/
STANDARD_ERROR_CODE[:invalid_expiry_date]
when /100.100.60[01]/
STANDARD_ERROR_CODE[:invalid_cvc]
when '800.100.151'
STANDARD_ERROR_CODE[:invalid_number]
when '800.100.153'
STANDARD_ERROR_CODE[:incorrect_cvc]
when /800.800.(102|302)/
STANDARD_ERROR_CODE[:incorrect_address]
when '800.800.202'
STANDARD_ERROR_CODE[:invalid_zip]
when '800.100.166'
STANDARD_ERROR_CODE[:incorrect_pin]
when '800.100.171'
STANDARD_ERROR_CODE[:pickup_card]
when /^(200|700)\./
STANDARD_ERROR_CODE[:config_error]
when /^(800\.[17]00|800\.800\.[123])/
STANDARD_ERROR_CODE[:card_declined]
when /^(900\.[1234]00)/
STANDARD_ERROR_CODE[:processing_error]
else
STANDARD_ERROR_CODE[:processing_error]
end
end
end
def hashify_address(address)
hash = {}
hash[:street1] = address[:address1] if address[:address1]
hash[:street2] = address[:address2] if address[:address2]
hash[:city] = address[:city] if address[:city]
hash[:state] = address[:state] if address[:state]
hash[:postcode] = address[:zip] if address[:zip]
hash[:country] = address[:country] if address[:country]
hash
end
def dot_flatten_hash(hash, prefix = '')
h = {}
hash.each_pair do |k, v|
if v.is_a?(Hash)
h.merge!(dot_flatten_hash(v, prefix + k.to_s + '.'))
else
h[prefix + k.to_s] = v
end
end
h
end
end
end
end