class ActiveMerchant::Billing::PayeezyGateway

Constants

CREDIT_CARD_BRAND

Public Class Methods

new(options = {}) click to toggle source
Calls superclass method ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/payeezy.rb, line 28
def initialize(options = {})
  requires!(options, :apikey, :apisecret, :token)
  super
end

Public Instance Methods

authorize(amount, payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 51
def authorize(amount, payment_method, options = {})
  params = { transaction_type: 'authorize' }

  add_invoice(params, options)
  add_reversal_id(params, options)
  add_customer_ref(params, options)
  add_reference_3(params, options)
  add_payment_method(params, payment_method, options)
  add_address(params, options)
  add_amount(params, amount, options)
  add_soft_descriptors(params, options)
  add_level2_data(params, options)
  add_stored_credentials(params, options)
  add_external_three_ds(params, payment_method, options)

  commit(params, options)
end
capture(amount, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 69
def capture(amount, authorization, options = {})
  params = { transaction_type: 'capture' }

  add_authorization_info(params, authorization)
  add_amount(params, amount, options)
  add_soft_descriptors(params, options)

  commit(params, options)
end
credit(amount, payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 90
def credit(amount, payment_method, options = {})
  params = { transaction_type: 'refund' }

  add_amount(params, amount, options)
  add_payment_method(params, payment_method, options)
  add_soft_descriptors(params, options)
  add_invoice(params, options)
  commit(params, options)
end
purchase(amount, payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 33
def purchase(amount, payment_method, options = {})
  params = payment_method.is_a?(String) ? { transaction_type: 'recurring' } : { transaction_type: 'purchase' }

  add_invoice(params, options)
  add_reversal_id(params, options)
  add_customer_ref(params, options)
  add_reference_3(params, options)
  add_payment_method(params, payment_method, options)
  add_address(params, options)
  add_amount(params, amount, options)
  add_soft_descriptors(params, options)
  add_level2_data(params, options)
  add_stored_credentials(params, options)
  add_external_three_ds(params, payment_method, options)

  commit(params, options)
end
refund(amount, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 79
def refund(amount, authorization, options = {})
  params = { transaction_type: 'refund' }

  add_authorization_info(params, authorization)
  add_amount(params, (amount || amount_from_authorization(authorization)), options)
  add_soft_descriptors(params, options)
  add_invoice(params, options)

  commit(params, options)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 128
def scrub(transcript)
  transcript.
    gsub(%r((Token: )(\w|-)+), '\1[FILTERED]').
    gsub(%r((Apikey: )(\w|-)+), '\1[FILTERED]').
    gsub(%r((\\?"card_number\\?":\\?")\d+), '\1[FILTERED]').
    gsub(%r((\\?"cvv\\?":\\?")\d+), '\1[FILTERED]').
    gsub(%r((\\?"cvv\\?":\\?)\d+), '\1[FILTERED]').
    gsub(%r((\\?"account_number\\?":\\?")\d+), '\1[FILTERED]').
    gsub(%r((\\?"routing_number\\?":\\?")\d+), '\1[FILTERED]').
    gsub(%r((\\?card_number=)\d+(&?)), '\1[FILTERED]').
    gsub(%r((\\?cvv=)\d+(&?)), '\1[FILTERED]').
    gsub(%r((\\?apikey=)\w+(&?)), '\1[FILTERED]').
    gsub(%r{(\\?"credit_card\.card_number\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
    gsub(%r{(\\?"credit_card\.cvv\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
    gsub(%r{(\\?"apikey\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
    gsub(%r{(\\?"cavv\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]').
    gsub(%r{(\\?"xid\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]')
end
store(payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 100
def store(payment_method, options = {})
  params = { transaction_type: 'store' }

  add_creditcard_for_tokenization(params, payment_method, options)

  commit(params, options)
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 124
def supports_scrubbing?
  true
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 117
def verify(credit_card, options = {})
  MultiResponse.run(:use_first_response) do |r|
    r.process { authorize(0, credit_card, options) }
    r.process(:ignore_result) { void(r.authorization, options) }
  end
end
void(authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 108
def void(authorization, options = {})
  params = { transaction_type: 'void' }

  add_authorization_info(params, authorization, options)
  add_amount(params, amount_from_authorization(authorization), options)

  commit(params, options)
end

Private Instance Methods

add_address(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 308
def add_address(params, options)
  address = options[:billing_address]
  return unless address

  billing_address = {}
  billing_address[:street] = address[:address1] if address[:address1]
  billing_address[:city] = address[:city] if address[:city]
  billing_address[:state_province] = address[:state] if address[:state]
  billing_address[:zip_postal_code] = address[:zip] if address[:zip]
  billing_address[:country] = address[:country] if address[:country]

  params[:billing_address] = billing_address
end
add_amount(params, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 322
def add_amount(params, money, options)
  params[:currency_code] = (options[:currency] || default_currency).upcase
  params[:amount] = amount(money)
end
add_authorization_info(params, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 189
def add_authorization_info(params, authorization, options = {})
  transaction_id, transaction_tag, method, = authorization.split('|')
  params[:method] = method == 'token' ? 'credit_card' : method
  # If the previous transaction `method` value was 3DS, it needs to be set to `credit_card` on follow up transactions
  params[:method] = 'credit_card' if method == '3DS'

  if options[:reversal_id]
    params[:reversal_id] = options[:reversal_id]
  else
    params[:transaction_id] = transaction_id
    params[:transaction_tag] = transaction_tag
  end
end
add_card_data(payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 267
def add_card_data(payment_method, options = {})
  card = {}
  card[:type] = CREDIT_CARD_BRAND[payment_method.brand]
  card[:cardholder_name] = name_from_payment_method(payment_method) || name_from_address(options)
  card[:card_number] = payment_method.number
  card[:exp_date] = format_exp_date(payment_method.month, payment_method.year)
  card[:cvv] = payment_method.verification_value if payment_method.verification_value?
  card
end
add_creditcard(params, creditcard, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 260
def add_creditcard(params, creditcard, options)
  credit_card = add_card_data(creditcard, options)

  params[:method] = 'credit_card'
  params[:credit_card] = credit_card
end
add_creditcard_for_tokenization(params, payment_method, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 203
def add_creditcard_for_tokenization(params, payment_method, options)
  params[:apikey] = @options[:apikey]
  params[:ta_token] = options[:ta_token]
  params[:type] = 'FDToken'
  params[:credit_card] = add_card_data(payment_method, options)
  params[:auth] = 'false'
end
add_customer_ref(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 177
def add_customer_ref(params, options)
  params[:customer_ref] = options[:customer_ref] if options[:customer_ref]
end
add_echeck(params, echeck, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 227
def add_echeck(params, echeck, options)
  tele_check = {}

  tele_check[:check_number] = echeck.number || '001'
  tele_check[:check_type] = 'P'
  tele_check[:routing_number] = echeck.routing_number
  tele_check[:account_number] = echeck.account_number
  tele_check[:accountholder_name] = name_from_payment_method(echeck)
  tele_check[:customer_id_type] = options[:customer_id_type] if options[:customer_id_type]
  tele_check[:customer_id_number] = options[:customer_id_number] if options[:customer_id_number]
  tele_check[:client_email] = options[:client_email] if options[:client_email]

  params[:method] = 'tele_check'
  params[:tele_check] = tele_check
end
add_external_three_ds(params, payment_method, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 149
def add_external_three_ds(params, payment_method, options)
  return unless three_ds = options[:three_d_secure]

  params[:'3DS'] = {
    program_protocol: three_ds[:version][0],
    directory_server_transaction_id: three_ds[:ds_transaction_id],
    cardholder_name: payment_method.name,
    card_number: payment_method.number,
    exp_date: format_exp_date(payment_method.month, payment_method.year),
    cvv: payment_method.verification_value,
    xid: three_ds[:acs_transaction_id],
    cavv: three_ds[:cavv],
    wallet_provider_id: 'NO_WALLET',
    type: 'D'
  }.compact

  params[:eci_indicator] = options[:three_d_secure][:eci]
  params[:method] = '3DS'
end
add_invoice(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 169
def add_invoice(params, options)
  params[:merchant_ref] = options[:order_id]
end
add_level2_data(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 331
def add_level2_data(params, options)
  return unless level2_data = options[:level2]

  params[:level2] = {}
  params[:level2][:customer_ref] = level2_data[:customer_ref]
end
add_network_tokenization(params, payment_method, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 277
def add_network_tokenization(params, payment_method, options)
  nt_card = {}
  nt_card[:type] = 'D'
  nt_card[:cardholder_name] = name_from_payment_method(payment_method) || name_from_address(options)
  nt_card[:card_number] = payment_method.number
  nt_card[:exp_date] = format_exp_date(payment_method.month, payment_method.year)
  nt_card[:cvv] = payment_method.verification_value
  nt_card[:xid] = payment_method.payment_cryptogram unless payment_method.payment_cryptogram.empty? || payment_method.brand.include?('american_express')
  nt_card[:cavv] = payment_method.payment_cryptogram unless payment_method.payment_cryptogram.empty?
  nt_card[:wallet_provider_id] = 'APPLE_PAY'

  params['3DS'] = nt_card
  params[:method] = '3DS'
  params[:eci_indicator] = payment_method.eci.nil? ? '5' : payment_method.eci
end
add_payment_method(params, payment_method, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 215
def add_payment_method(params, payment_method, options)
  if payment_method.is_a? Check
    add_echeck(params, payment_method, options)
  elsif payment_method.is_a? String
    add_token(params, payment_method, options)
  elsif payment_method.is_a? NetworkTokenizationCreditCard
    add_network_tokenization(params, payment_method, options)
  else
    add_creditcard(params, payment_method, options)
  end
end
add_reference_3(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 181
def add_reference_3(params, options)
  params[:reference_3] = options[:reference_3] if options[:reference_3]
end
add_reversal_id(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 173
def add_reversal_id(params, options)
  params[:reversal_id] = options[:reversal_id] if options[:reversal_id]
end
add_soft_descriptors(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 327
def add_soft_descriptors(params, options)
  params[:soft_descriptors] = options[:soft_descriptors] if options[:soft_descriptors]
end
add_stored_credentials(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 338
def add_stored_credentials(params, options)
  if options[:sequence] || options[:stored_credential]
    params[:stored_credentials] = {}
    params[:stored_credentials][:cardbrand_original_transaction_id] = original_transaction_id(options) if original_transaction_id(options)
    params[:stored_credentials][:initiator] = initiator(options) if initiator(options)
    params[:stored_credentials][:sequence] = options[:sequence] || sequence(options[:stored_credential][:initial_transaction])
    params[:stored_credentials][:is_scheduled] = options[:is_scheduled] || is_scheduled(options[:stored_credential][:reason_type])
    params[:stored_credentials][:auth_type_override] = options[:auth_type_override] if options[:auth_type_override]
  end
end
add_token(params, payment_method, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 243
def add_token(params, payment_method, options)
  token = {}
  token[:token_type] = 'FDToken'

  type, cardholder_name, exp_date, card_number = payment_method.split('|')

  token[:token_data] = {}
  token[:token_data][:type] = type
  token[:token_data][:cardholder_name] = cardholder_name
  token[:token_data][:value] = card_number
  token[:token_data][:exp_date] = exp_date
  token[:token_data][:cvv] = options[:cvv] if options[:cvv]

  params[:method] = 'token'
  params[:token] = token
end
amount_from_authorization(authorization) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 185
def amount_from_authorization(authorization)
  authorization.split('|').last.to_i
end
api_request(url, params) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 408
def api_request(url, params)
  body = params.to_json
  parse(ssl_post(url, body, headers(body)))
end
authorization_from(params, response) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 487
def authorization_from(params, response)
  if store_action?(params)
    if success_from(response)
      [
        response['token']['type'],
        response['token']['cardholder_name'],
        response['token']['exp_date'],
        response['token']['value']
      ].join('|')
    else
      nil
    end
  else
    [
      response['transaction_id'],
      response['transaction_tag'],
      params[:method],
      response['amount']&.to_i
    ].join('|')
  end
end
base_url(options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 394
def base_url(options)
  if options[:integration]
    integration_url
  elsif test?
    test_url
  else
    live_url
  end
end
commit(params, options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 366
def commit(params, options)
  url = base_url(options) + endpoint(params)

  if transaction_id = params.delete(:transaction_id)
    url = "#{url}/#{transaction_id}"
  end

  begin
    response = api_request(url, params)
  rescue ResponseError => e
    response = response_error(e.response.body)
  rescue JSON::ParserError
    response = json_error(e.response.body)
  end

  success = success_from(response)
  Response.new(
    success,
    handle_message(response, success),
    response,
    test: test?,
    authorization: authorization_from(params, response),
    avs_result: { code: response['avs'] },
    cvv_result: response['cvv2'],
    error_code: success ? nil : error_code_from(response)
  )
end
endpoint(params) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 404
def endpoint(params)
  store_action?(params) ? '/transactions/tokens' : '/transactions'
end
error_code_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 443
def error_code_from(response)
  error_code = nil
  if response['bank_resp_code'] == '100'
    return
  elsif response['bank_resp_code']
    error_code = response['bank_resp_code']
  elsif error_code = response['Error'].to_h['messages'].to_a.map { |e| e['code'] }.join(', ')
  end

  error_code
end
format_exp_date(month, year) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 293
def format_exp_date(month, year)
  "#{format(month, :two_digits)}#{format(year, :two_digits)}"
end
generate_hmac(nonce, current_timestamp, payload) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 419
def generate_hmac(nonce, current_timestamp, payload)
  message = [
    @options[:apikey],
    nonce.to_s,
    current_timestamp.to_s,
    @options[:token],
    payload
  ].join('')
  Base64.strict_encode64(OpenSSL::HMAC.hexdigest('sha256', @options[:apisecret], message))
end
handle_message(response, success) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 467
def handle_message(response, success)
  if success && response['status'].present?
    'Token successfully created.'
  elsif success
    "#{response['gateway_message']} - #{response['bank_message']}"
  elsif %w(401 403).include?(response['code'])
    response['message']
  elsif response.key?('Error')
    response['Error']['messages'].first['description']
  elsif response.key?('results')
    response['results']['Error']['messages'].first['description']
  elsif response.key?('error')
    response['error']
  elsif response.key?('fault')
    response['fault'].to_h['faultstring']
  else
    response['bank_message'] || response['gateway_message'] || 'Failure to successfully create token.'
  end
end
headers(payload) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 430
def headers(payload)
  nonce = (SecureRandom.random_number * 10_000_000_000)
  current_timestamp = (Time.now.to_f * 1000).to_i
  {
    'Content-Type' => 'application/json',
    'apikey' => options[:apikey],
    'token' => options[:token],
    'nonce' => nonce.to_s,
    'timestamp' => current_timestamp.to_s,
    'Authorization' => generate_hmac(nonce, current_timestamp, payload)
  }
end
initiator(options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 353
def initiator(options)
  return options[:initiator] if options[:initiator]
  return options[:stored_credential][:initiator].upcase if options.dig(:stored_credential, :initiator)
end
is_scheduled(reason_type) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 362
def is_scheduled(reason_type)
  reason_type == 'recurring' ? 'true' : 'false'
end
json_error(raw_response) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 519
def json_error(raw_response)
  { 'error' => "Unable to parse response: #{raw_response.inspect}" }
end
name_from_address(options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 297
def name_from_address(options)
  return unless address = options[:billing_address]
  return address[:name] if address[:name]
end
name_from_payment_method(payment_method) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 302
def name_from_payment_method(payment_method)
  return unless payment_method.first_name && payment_method.last_name

  return "#{payment_method.first_name} #{payment_method.last_name}"
end
original_transaction_id(options) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 349
def original_transaction_id(options)
  return options[:cardbrand_original_transaction_id] || options.dig(:stored_credential, :network_transaction_id)
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 509
def parse(body)
  JSON.parse(body)
end
post_data(params) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 413
def post_data(params)
  return nil unless params

  params.reject { |_k, v| v.blank? }.collect { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
end
response_error(raw_response) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 513
def response_error(raw_response)
  parse(raw_response)
rescue JSON::ParserError
  json_error(raw_response)
end
sequence(initial_transaction) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 358
def sequence(initial_transaction)
  initial_transaction ? 'FIRST' : 'SUBSEQUENT'
end
store_action?(params) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 211
def store_action?(params)
  params[:transaction_type] == 'store'
end
success_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/payeezy.rb, line 455
def success_from(response)
  if response['transaction_status']
    response['transaction_status'] == 'approved'
  elsif response['results']
    response['results']['status'] == 'success'
  elsif response['status']
    response['status'] == 'success'
  else
    false
  end
end