Skip to content

Commit

Permalink
Elavon: Update Stored Credential behavior
Browse files Browse the repository at this point in the history
To satisfy new Elavon API requirements, including recurring_flag,
approval_code for non-tokenized PMs, installment_count and _number, and
situational par_value and association_token_data fields.

This also now allows Authorize transactions with a token/stored card.

Remote (Same 5 Failures on Master):
39 tests, 162 assertions, 5 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
87.1795% passed

Unit:
53 tests, 298 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
  • Loading branch information
curiousepic committed Jan 31, 2024
1 parent f2b39c7 commit f1730e3
Show file tree
Hide file tree
Showing 3 changed files with 442 additions and 112 deletions.
67 changes: 44 additions & 23 deletions lib/active_merchant/billing/gateways/elavon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def purchase(money, payment_method, options = {})
if payment_method.is_a?(String)
add_token(xml, payment_method)
else
add_creditcard(xml, payment_method)
add_creditcard(xml, payment_method) unless options[:ssl_token]
end

add_invoice(xml, options)
Expand All @@ -56,27 +56,32 @@ def purchase(money, payment_method, options = {})
add_customer_email(xml, options)
add_test_mode(xml, options)
add_ip(xml, options)
add_auth_purchase_params(xml, options)
add_auth_purchase_params(xml, payment_method, options)
add_level_3_fields(xml, options) if options[:level_3_data]
end
commit(request)
end

def authorize(money, creditcard, options = {})
def authorize(money, payment_method, options = {})
request = build_xml_request do |xml|
xml.ssl_vendor_id @options[:ssl_vendor_id] || options[:ssl_vendor_id]
xml.ssl_transaction_type self.actions[:authorize]
xml.ssl_amount amount(money)

add_salestax(xml, options)
if payment_method.is_a?(String)
add_token(xml, payment_method)
else
add_creditcard(xml, payment_method) unless options[:ssl_token]
end

add_invoice(xml, options)
add_creditcard(xml, creditcard)
add_salestax(xml, options)
add_currency(xml, money, options)
add_address(xml, options)
add_customer_email(xml, options)
add_test_mode(xml, options)
add_ip(xml, options)
add_auth_purchase_params(xml, options)
add_auth_purchase_params(xml, payment_method, options)
add_level_3_fields(xml, options) if options[:level_3_data]
end
commit(request)
Expand Down Expand Up @@ -294,16 +299,17 @@ def add_ip(xml, options)
end

# add_recurring_token is a field that can be sent in to obtain a token from Elavon for use with their tokenization program
def add_auth_purchase_params(xml, options)
def add_auth_purchase_params(xml, payment_method, options)
xml.ssl_dynamic_dba options[:dba] if options.has_key?(:dba)
xml.ssl_merchant_initiated_unscheduled merchant_initiated_unscheduled(options) if merchant_initiated_unscheduled(options)
xml.ssl_add_token options[:add_recurring_token] if options.has_key?(:add_recurring_token)
xml.ssl_token options[:ssl_token] if options[:ssl_token]
xml.ssl_customer_code options[:customer] if options.has_key?(:customer)
xml.ssl_customer_number options[:customer_number] if options.has_key?(:customer_number)
xml.ssl_entry_mode entry_mode(options) if entry_mode(options)
xml.ssl_entry_mode add_entry_mode(payment_method, options) if add_entry_mode(payment_method, options)
add_custom_fields(xml, options) if options[:custom_fields]
add_stored_credential(xml, options) if options[:stored_credential]
add_stored_credential(xml, payment_method, options) if options[:stored_credential]
add_installment_fields(xml, options) if options.dig(:stored_credential, :reason_type) == 'installment'
end

def add_custom_fields(xml, options)
Expand Down Expand Up @@ -352,30 +358,45 @@ def add_line_items(xml, level_3_data)
}
end

def add_stored_credential(xml, options)
def add_stored_credential(xml, payment_method, options)
return unless options[:stored_credential]

network_transaction_id = options.dig(:stored_credential, :network_transaction_id)
case
when network_transaction_id.nil?
return
when network_transaction_id.to_s.include?('|')
if network_transaction_id.to_s.include?('|')
oar_data, ps2000_data = options[:stored_credential][:network_transaction_id].split('|')
xml.ssl_oar_data oar_data unless oar_data.nil? || oar_data.empty?
xml.ssl_ps2000_data ps2000_data unless ps2000_data.nil? || ps2000_data.empty?
when network_transaction_id.to_s.length > 22
xml.ssl_oar_data options.dig(:stored_credential, :network_transaction_id)
else
xml.ssl_ps2000_data options.dig(:stored_credential, :network_transaction_id)
xml.ssl_oar_data oar_data unless oar_data.blank? || payment_method.is_a?(String)
xml.ssl_ps2000_data ps2000_data unless ps2000_data.blank? || payment_method.is_a?(String)
elsif network_transaction_id.to_s.length > 22
xml.ssl_oar_data network_transaction_id
elsif network_transaction_id.present?
xml.ssl_ps2000_data network_transaction_id
end
xml.ssl_recurring_flag recurring_flag(options) if recurring_flag(options)
xml.ssl_approval_code options[:approval_code] if options[:approval_code] && !payment_method.is_a?(String) && !options[:ssl_token]
xml.ssl_par_value options[:par_value] if options[:par_value]
xml.ssl_association_token_data options[:association_token_data] if options[:association_token_data]
end

def recurring_flag(options)
return unless reason = options.dig(:stored_credential, :reason_type)
return 1 if reason == 'recurring'
return 2 if reason == 'installment'
end

def merchant_initiated_unscheduled(options)
return options[:merchant_initiated_unscheduled] if options[:merchant_initiated_unscheduled]
return 'Y' if options.dig(:stored_credential, :initiator) == 'merchant' && options.dig(:stored_credential, :reason_type) == 'unscheduled' || options.dig(:stored_credential, :reason_type) == 'recurring'
return 'Y' if options.dig(:stored_credential, :initiator) == 'merchant' && options.dig(:stored_credential, :reason_type) == 'unscheduled'
end

def add_installment_fields(xml, options)
xml.ssl_payment_number options[:payment_number]
xml.ssl_payment_count options[:installments]
end

def entry_mode(options)
def add_entry_mode(payment_method, options)
return options[:entry_mode] if options[:entry_mode]
return 12 if options[:stored_credential]
return if payment_method.is_a?(String) || options[:ssl_token]
return 12 if options.dig(:stored_credential, :reason_type) == 'unscheduled'
end

def build_xml_request
Expand Down
Loading

0 comments on commit f1730e3

Please sign in to comment.