class ActiveMerchant::Billing::AleloGateway

Public Class Methods

new(options = {}) click to toggle source
Calls superclass method ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/alelo.rb, line 19
def initialize(options = {})
  requires!(options, :client_id, :client_secret)
  super
end

Public Instance Methods

purchase(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 24
def purchase(money, payment, options = {})
  post = {}
  add_order(post, options)
  add_amount(post, money)
  add_payment(post, payment)
  add_geolocation(post, options)
  add_extra_data(post, options)

  commit('capture/transaction', post, options)
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 35
def refund(money, authorization, options = {})
  request_id = authorization.split('#').first
  options[:http] = { method: :put, prevent_encrypt: true }
  commit('capture/transaction/refund', { requestId: request_id }, options, :put)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 45
def scrub(transcript)
  force_utf8(transcript.encode).
    gsub(%r((Authorization: Bearer )[\w -]+), '\1[FILTERED]').
    gsub(%r((client_id=|Client-Id:)[\w -]+), '\1[FILTERED]\2').
    gsub(%r((client_secret=|Client-Secret:)[\w -]+), '\1[FILTERED]\2').
    gsub(%r((access_token\":\")[^\"]*), '\1[FILTERED]').
    gsub(%r((publicKey\":\")[^\"]*), '\1[FILTERED]')
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 41
def supports_scrubbing?
  true
end

Private Instance Methods

add_amount(post, money) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 63
def add_amount(post, money)
  post[:amount] = amount(money).to_f
end
add_extra_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 71
def add_extra_data(post, options)
  post.merge!({
    establishmentCode: options[:establishment_code],
    playerIdentification: options[:player_identification],
    captureType: '3', # send fixed value 3 to ecommerce
    subMerchantCode: options[:sub_merchant_mcc],
    externalTraceNumber: options[:external_trace_number]
  }.compact)
end
add_geolocation(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 81
def add_geolocation(post, options)
  return if options[:geo_latitude].blank? || options[:geo_longitude].blank?

  post.merge!(geolocation: {
    latitude: options[:geo_latitude],
    longitude: options[:geo_longitude]
  })
end
add_order(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 67
def add_order(post, options)
  post[:requestId] = options[:order_id]
end
add_payment(post, payment) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 90
def add_payment(post, payment)
  post.merge!({
    cardNumber: payment.number,
    cardholderName: payment.name,
    expirationMonth: payment.month,
    expirationYear: format(payment.year, :two_digits).to_i,
    securityCode: payment.verification_value
  })
end
authorization_from(response, options) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 253
def authorization_from(response, options)
  [response[:requestId]].join('#')
end
commit(action, body, options, try_again = true) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 191
def commit(action, body, options, try_again = true)
  credentials = ensure_credentials
  payload = encrypt_payload(body, credentials, options)

  if options.dig :http, :method
    payload = body.to_json if options.dig :http, :prevent_encrypt
    response = parse ssl_request(options[:http][:method], url(action), payload, request_headers(credentials[:access_token]))
  else
    response = parse ssl_post(url(action), payload, request_headers(credentials[:access_token]))
  end

  resp = Response.new(
    success_from(action, response),
    message_from(response),
    response,
    authorization: authorization_from(response, options),
    test: test?
  )

  return resp unless credentials[:multiresp].present?

  multiresp = credentials[:multiresp]
  resp.params.merge!({
    'access_token' => credentials[:access_token],
    'encryption_key' => credentials[:key],
    'encryption_uuid' => credentials[:uuid]
  })
  multiresp.process { resp }

  multiresp
rescue ActiveMerchant::OAuthResponseError => e
  raise OAuthResponseError.new(e)
rescue ActiveMerchant::ResponseError => e
  # Retry on a possible expired encryption key
  if retry?(try_again, e, :encryption_key)
    @options.delete(:encryption_key)
    commit(action, body, options, false)
  else
    res = parse(e.response.body)
    Response.new(false, res[:messageUser] || res[:error], res, test: test?)
  end
end
encrypt_payload(body, credentials, options) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 168
def encrypt_payload(body, credentials, options)
  key = OpenSSL::PKey::RSA.new(Base64.decode64(credentials[:key]))
  jwk = JOSE::JWK.from_key(key)
  alg_enc = { 'alg' => 'RSA-OAEP-256', 'enc' => 'A128CBC-HS256' }

  token = JOSE::JWE.block_encrypt(jwk, body.to_json, alg_enc).compact

  encrypted_body = {
    token: token,
    uuid: credentials[:uuid]
  }

  encrypted_body.to_json
end
ensure_credentials(try_again = true) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 132
def ensure_credentials(try_again = true)
  multiresp = MultiResponse.new
  access_token = @options[:access_token]
  key = @options[:encryption_key]
  uuid = @options[:encryption_uuid]

  if access_token.blank?
    multiresp.process { fetch_access_token }
    access_token = multiresp.message
    key = nil
    uuid = nil
  end

  if key.blank?
    multiresp.process { remote_encryption_key(access_token) }
    key = multiresp.message
    uuid = multiresp.params['uuid']
  end

  {
    key: key,
    uuid: uuid,
    access_token: access_token,
    multiresp: multiresp.responses.present? ? multiresp : nil
  }
rescue ActiveMerchant::OAuthResponseError => e
  raise e
rescue ResponseError => e
  # retry to generate a new access_token when the provided one is expired
  raise e unless retry?(try_again, e, :access_token)

  @options.delete(:access_token)
  @options.delete(:encryption_key)
  ensure_credentials false
end
fetch_access_token() click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 100
def fetch_access_token
  params = {
    grant_type: 'client_credentials',
    client_id: @options[:client_id],
    client_secret: @options[:client_secret],
    scope: '/capture'
  }

  headers = {
    'Accept' => 'application/json',
    'Content-Type' => 'application/x-www-form-urlencoded'
  }

  begin
    raw_response = ssl_post(url('captura-oauth-provider/oauth/token'), post_data(params), headers)
  rescue ResponseError => e
    raise OAuthResponseError.new(e)
  else
    response = parse(raw_response)
    if (access_token = response[:access_token])
      Response.new(true, access_token, response)
    else
      raise OAuthResponseError.new(response)
    end
  end
end
force_utf8(string) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 56
def force_utf8(string)
  return nil unless string

  # binary = string.encode('BINARY', invalid: :replace, undef: :replace, replace: '?')
  string.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 249
def message_from(response)
  response[:messages] || response[:messageUser]
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 183
def parse(body)
  JSON.parse(body, symbolize_names: true)
end
post_data(params) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 187
def post_data(params)
  params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
end
remote_encryption_key(access_token) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 127
def remote_encryption_key(access_token)
  response = parse(ssl_get(url('capture/key'), request_headers(access_token)))
  Response.new(true, response[:publicKey], response)
end
request_headers(access_token) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 263
def request_headers(access_token)
  {
    'Accept' => 'application/json',
    'X-IBM-Client-Id' => @options[:client_id],
    'X-IBM-Client-Secret' => @options[:client_secret],
    'Content-Type' => 'application/json',
    'Authorization' => "Bearer #{access_token}"
  }
end
retry?(try_again, error, key) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 234
def retry?(try_again, error, key)
  try_again && %w(401 404).include?(error.response.code) && @options[key].present?
end
success_from(action, response) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 238
def success_from(action, response)
  case action
  when 'capture/transaction/refund'
    response[:status] == 'ESTORNADA'
  when 'capture/transaction'
    response[:status] == 'CONFIRMADA'
  else
    false
  end
end
url(action) click to toggle source
# File lib/active_merchant/billing/gateways/alelo.rb, line 257
def url(action)
  return prelive_url if @options[:url_override] == 'prelive'

  "#{test? ? test_url : live_url}#{action}"
end