require 'json'
require 'openssl'
require 'digest'
require 'base64'
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class MitGateway < Gateway
self.live_url = 'https://wpy.mitec.com.mx/ModuloUtilWS/activeCDP.htm'
self.supported_countries = ['MX']
self.default_currency = 'MXN'
self.supported_cardtypes = %i[visa master]
self.homepage_url = 'http://www.centrodepagos.com.mx/'
self.display_name = 'MIT Centro de pagos'
self.money_format = :dollars
def initialize(options = {})
requires!(options, :commerce_id, :user, :api_key, :key_session)
super
end
def purchase(money, payment, options = {})
MultiResponse.run do |r|
r.process { authorize(money, payment, options) }
r.process { capture(money, r.authorization, options) }
end
end
def cipher_key
@options[:key_session]
end
def decrypt(val, keyinhex)
# Splits the first 16 bytes (the IV bytes) in array format
unpacked = val.unpack('m')
iv_base64 = unpacked[0].bytes.slice(0, 16)
# Splits the second bytes (the encrypted text bytes) these would be the
# original message
full_data = unpacked[0].bytes.slice(16, unpacked[0].bytes.length)
# Creates the engine
engine = OpenSSL::Cipher::AES128.new(:CBC)
# Set engine as decrypt mode
engine.decrypt
# Converts the key from hex to bytes
engine.key = [keyinhex].pack('H*')
# Converts the ivBase64 array into bytes
engine.iv = iv_base64.pack('c*')
# Decrypts the texts and returns the original string
engine.update(full_data.pack('c*')) + engine.final
end
def encrypt(val, keyinhex)
# Creates the engine motor
engine = OpenSSL::Cipher::AES128.new(:CBC)
# Set engine as encrypt mode
engine.encrypt
# Converts the key from hex to bytes
engine.key = [keyinhex].pack('H*')
# Generates a random iv with this settings
iv_rand = engine.random_iv
# Packs IV as a Base64 string
iv_base64 = [iv_rand].pack('m')
# Converts the packed key into bytes
unpacked = iv_base64.unpack('m')
iv = unpacked[0]
# Sets the IV into the engine
engine.iv = iv
# Encrypts the texts and stores the bytes
encrypted_bytes = engine.update(val) + engine.final
# Concatenates the (a) IV bytes and (b) the encrypted bytes then returns a
# base64 representation
[iv << encrypted_bytes].pack('m')
end
def authorize(money, payment, options = {})
post = {
operation: 'Authorize',
commerce_id: @options[:commerce_id],
user: @options[:user],
apikey: @options[:api_key],
testMode: (test? ? 'YES' : 'NO')
}
add_invoice(post, money, options)
# Payments contains the card information
add_payment(post, payment)
add_customer_data(post, options)
post[:key_session] = @options[:key_session]
post_to_json = post.to_json
post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
final_post = '' + post_to_json_encrypt + '' + @options[:user] + ''
json_post = final_post
commit('sale', json_post)
end
def capture(money, authorization, options = {})
post = {
operation: 'Capture',
commerce_id: @options[:commerce_id],
user: @options[:user],
apikey: @options[:api_key],
testMode: (test? ? 'YES' : 'NO'),
transaction_id: authorization,
amount: amount(money)
}
post[:key_session] = @options[:key_session]
post_to_json = post.to_json
post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
final_post = '' + post_to_json_encrypt + '' + @options[:user] + ''
json_post = final_post
commit('capture', json_post)
end
def refund(money, authorization, options = {})
post = {
operation: 'Refund',
commerce_id: @options[:commerce_id],
user: @options[:user],
apikey: @options[:api_key],
testMode: (test? ? 'YES' : 'NO'),
transaction_id: authorization,
auth: authorization,
amount: amount(money)
}
post[:key_session] = @options[:key_session]
post_to_json = post.to_json
post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
final_post = '' + post_to_json_encrypt + '' + @options[:user] + ''
json_post = final_post
commit('refund', json_post)
end
def supports_scrubbing?
true
end
def extract_mit_responses_from_transcript(transcript)
groups = transcript.scan(/reading \d+ bytes(.*?)read \d+ bytes/m)
groups.map do |group|
group.first.scan(/-> "(.*?)"/).flatten.map(&:strip).join('')
end
end
def scrub(transcript)
ret_transcript = transcript
auth_origin = ret_transcript[/(.*?)<\/authorization>/, 1]
unless auth_origin.nil?
auth_origin = auth_origin.gsub('\n', '')
auth_decrypted = decrypt(auth_origin, @options[:key_session])
auth_json = JSON.parse(auth_decrypted)
auth_json['card'] = '[FILTERED]'
auth_json['cvv'] = '[FILTERED]'
auth_json['apikey'] = '[FILTERED]'
auth_json['key_session'] = '[FILTERED]'
auth_to_json = auth_json.to_json
auth_tagged = '' + auth_to_json + ''
ret_transcript = ret_transcript.gsub(/(.*?)<\/authorization>/, auth_tagged)
end
cap_origin = ret_transcript[/(.*?)<\/capture>/, 1]
unless cap_origin.nil?
cap_origin = cap_origin.gsub('\n', '')
cap_decrypted = decrypt(cap_origin, @options[:key_session])
cap_json = JSON.parse(cap_decrypted)
cap_json['apikey'] = '[FILTERED]'
cap_json['key_session'] = '[FILTERED]'
cap_to_json = cap_json.to_json
cap_tagged = '' + cap_to_json + ''
ret_transcript = ret_transcript.gsub(/(.*?)<\/capture>/, cap_tagged)
end
ref_origin = ret_transcript[/(.*?)<\/refund>/, 1]
unless ref_origin.nil?
ref_origin = ref_origin.gsub('\n', '')
ref_decrypted = decrypt(ref_origin, @options[:key_session])
ref_json = JSON.parse(ref_decrypted)
ref_json['apikey'] = '[FILTERED]'
ref_json['key_session'] = '[FILTERED]'
ref_to_json = ref_json.to_json
ref_tagged = '' + ref_to_json + ''
ret_transcript = ret_transcript.gsub(/(.*?)<\/refund>/, ref_tagged)
end
groups = extract_mit_responses_from_transcript(transcript)
groups.each do |group|
group_decrypted = decrypt(group, @options[:key_session])
ret_transcript = ret_transcript.gsub('Conn close', "\n" + group_decrypted + "\nConn close")
end
ret_transcript
end
private
def add_customer_data(post, options)
post[:email] = options[:email] || 'nadie@mit.test'
end
def add_invoice(post, money, options)
post[:amount] = amount(money)
post[:currency] = (options[:currency] || currency(money))
post[:reference] = options[:order_id]
post[:transaction_id] = options[:order_id]
end
def add_payment(post, payment)
post[:installments] = 1
post[:card] = payment.number
post[:expmonth] = payment.month
post[:expyear] = payment.year
post[:cvv] = payment.verification_value
post[:name_client] = [payment.first_name, payment.last_name].join(' ')
end
def commit(action, parameters)
raw_response = ssl_post(live_url, parameters, { 'Content-type' => 'text/plain' })
response = JSON.parse(decrypt(raw_response, @options[:key_session]))
Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(response),
avs_result: AVSResult.new(code: response['some_avs_response_key']),
cvv_result: CVVResult.new(response['some_cvv_response_key']),
test: test?,
error_code: error_code_from(response)
)
end
def success_from(response)
response['response'] == 'approved'
end
def message_from(response)
response['response']
end
def authorization_from(response)
response['reference']
end
def post_data(action, parameters = {})
parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
end
def error_code_from(response)
response['message'].split(' -- ', 2)[0] unless success_from(response)
end
end
end
end