lib/active_merchant/billing/gateways/visanet_peru.rb
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class VisanetPeruGateway < Gateway
include Empty
self.display_name = 'VisaNet Peru Gateway'
self.homepage_url = 'http://www.visanet.com.pe'
self.test_url = 'https://devapi.vnforapps.com/api.tokenization/api/v2/merchant'
self.live_url = 'https://api.vnforapps.com/api.tokenization/api/v2/merchant'
self.supported_countries = %w[US PE]
self.default_currency = 'PEN'
self.money_format = :dollars
self.supported_cardtypes = %i[visa master american_express discover]
def initialize(options = {})
requires!(options, :access_key_id, :secret_access_key, :merchant_id)
super
end
def purchase(amount, payment_method, options = {})
MultiResponse.run() do |r|
r.process { authorize(amount, payment_method, options) }
r.process { capture(amount, r.authorization, options) }
end
end
def authorize(amount, payment_method, options = {})
params = {}
add_invoice(params, amount, options)
add_payment_method(params, payment_method)
add_antifraud_data(params, options)
params[:email] = options[:email] || 'unknown@email.com'
params[:createAlias] = false
commit('authorize', params, options)
end
def capture(amount, authorization, options = {})
params = {}
options[:id_unico] = split_authorization(authorization)[1]
add_auth_order_id(params, authorization, options)
commit('deposit', params, options)
end
def void(authorization, options = {})
params = {}
add_auth_order_id(params, authorization, options)
commit('void', params, options)
end
def refund(amount, authorization, options = {})
params = {}
params[:amount] = amount(amount) if amount
add_auth_order_id(params, authorization, options)
response = commit('cancelDeposit', params, options)
return response if response.success? || split_authorization(authorization).length == 1 || !options[:force_full_refund_if_unsettled]
# Attempt RefundSingleTransaction if unsettled (and stash the original
# response message so it will be included it in the follow-up response
# message)
options[:error_message] = response.message
prepare_refund_data(params, authorization, options)
commit('refund', params, options)
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((Authorization: Basic )\w+), '\1[FILTERED]').
gsub(%r((\"cardNumber\\\":\\\")\d+), '\1[FILTERED]').
gsub(%r((\"cvv2Code\\\":\\\")\d+), '\1[FILTERED]')
end
private
CURRENCY_CODES = Hash.new { |_h, k| raise ArgumentError.new("Unsupported currency: #{k}") }
CURRENCY_CODES['USD'] = 840
CURRENCY_CODES['PEN'] = 604
def add_invoice(params, money, options)
# Visanet Peru expects a 12-digit alphanumeric purchaseNumber
params[:purchaseNumber] = generate_purchase_number_stamp
params[:externalTransactionId] = options[:order_id]
params[:amount] = amount(money)
params[:currencyId] = CURRENCY_CODES[options[:currency] || currency(money)]
end
def add_auth_order_id(params, authorization, options)
purchase_number, = split_authorization(authorization)
params[:purchaseNumber] = purchase_number
params[:externalTransactionId] = options[:order_id]
end
def add_payment_method(params, payment_method)
params[:firstName] = payment_method.first_name
params[:lastName] = payment_method.last_name
params[:cardNumber] = payment_method.number
params[:cvv2Code] = payment_method.verification_value
params[:expirationYear] = format(payment_method.year, :four_digits)
params[:expirationMonth] = format(payment_method.month, :two_digits)
end
def add_antifraud_data(params, options)
antifraud = {}
if billing_address = options[:billing_address] || options[:address]
antifraud[:billTo_street1] = billing_address[:address1]
antifraud[:billTo_city] = billing_address[:city]
antifraud[:billTo_state] = billing_address[:state]
antifraud[:billTo_country] = billing_address[:country]
antifraud[:billTo_postalCode] = billing_address[:zip]
end
antifraud[:deviceFingerprintId] = options[:device_fingerprint_id] || SecureRandom.hex(16)
antifraud[:merchantDefineData] = options[:merchant_define_data] if options[:merchant_define_data]
params[:antifraud] = antifraud
end
def prepare_refund_data(params, authorization, options)
params.delete(:purchaseNumber)
params[:externalReferenceId] = params.delete(:externalTransactionId)
_, transaction_id = split_authorization(authorization)
options.update(transaction_id: transaction_id)
params[:ruc] = options[:ruc]
end
def split_authorization(authorization)
authorization.split('|')
end
def generate_purchase_number_stamp
rand(('9' * 12).to_i).to_s.center(12, rand(9).to_s)
end
def commit(action, params, options = {})
raw_response = ssl_request(method(action), url(action, params, options), params.to_json, headers)
response = parse(raw_response).merge('purchaseNumber' => params[:purchaseNumber])
rescue ResponseError => e
raw_response = e.response.body
response_error(raw_response, options, action)
rescue JSON::ParserError
unparsable_response(raw_response)
else
Response.new(
success_from(response),
message_from(response, options, action),
response,
test: test?,
authorization: authorization_from(params, response, options),
error_code: response['errorCode']
)
end
def headers
{
'Authorization' => 'Basic ' + Base64.strict_encode64("#{@options[:access_key_id]}:#{@options[:secret_access_key]}").strip,
'Content-Type' => 'application/json'
}
end
def url(action, params, options = {})
if action == 'authorize'
"#{base_url}/#{@options[:merchant_id]}"
elsif action == 'refund'
"#{base_url}/#{@options[:merchant_id]}/#{action}/#{options[:transaction_id]}"
else
"#{base_url}/#{@options[:merchant_id]}/#{action}/#{params[:purchaseNumber]}"
end
end
def method(action)
%w(authorize refund).include?(action) ? :post : :put
end
def authorization_from(params, response, options)
id_unico = response['data']['ID_UNICO'] || options[:id_unico]
"#{params[:purchaseNumber]}|#{id_unico}"
end
def base_url
test? ? test_url : live_url
end
def parse(body)
JSON.parse(body)
end
def success_from(response)
response['errorCode'] == 0
end
def message_from(response, options, action)
message_from_messages(
response['errorMessage'],
action_code_description(response),
options[:error_message]
)
end
def message_from_messages(*args)
args.reject { |m| error_message_empty?(m) }.join(' | ')
end
def action_code_description(response)
return nil unless response['data']
response['data']['DSC_COD_ACCION']
end
def error_message_empty?(error_message)
empty?(error_message) || error_message == '[ ]'
end
def response_error(raw_response, options, action)
response = parse(raw_response)
rescue JSON::ParserError
unparsable_response(raw_response)
else
return Response.new(
false,
message_from(response, options, action),
response,
test: test?,
authorization: response['transactionUUID'],
error_code: response['errorCode']
)
end
def unparsable_response(raw_response)
message = 'Invalid JSON response received from VisanetPeruGateway. Please contact VisanetPeruGateway if you continue to receive this message.'
message += " (The raw response returned by the API was #{raw_response.inspect})"
return Response.new(false, message)
end
end
end
end