Skip to content
93 changes: 15 additions & 78 deletions app/jobs/resolution_proofing_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@ class ResolutionProofingJob < ApplicationJob
keyword_init: true,
)

def perform(result_id:, encrypted_arguments:, trace_id:, should_proof_state_id:,
dob_year_only:, user_id: nil, threatmetrix_session_id: nil,
request_ip: nil)
def perform(
result_id:,
encrypted_arguments:,
trace_id:,
should_proof_state_id:,
user_id: nil,
threatmetrix_session_id: nil,
request_ip: nil,
dob_year_only: nil # rubocop:disable Lint:UnusedMethodArgument
)
timer = JobHelpers::Timer.new

raise_stale_job! if stale_job?(enqueued_at)
Expand All @@ -35,19 +42,11 @@ def perform(result_id:, encrypted_arguments:, trace_id:, should_proof_state_id:,
request_ip: request_ip,
)

callback_log_data = if dob_year_only && should_proof_state_id
proof_aamva_then_lexisnexis_dob_only(
timer: timer,
applicant_pii: applicant_pii,
dob_year_only: dob_year_only,
)
else
proof_lexisnexis_then_aamva(
timer: timer,
applicant_pii: applicant_pii,
should_proof_state_id: should_proof_state_id,
)
end
callback_log_data = proof_lexisnexis_then_aamva(
timer: timer,
applicant_pii: applicant_pii,
should_proof_state_id: should_proof_state_id,
)

if optional_threatmetrix_result.present?
add_threatmetrix_result_to_callback_result(
Expand Down Expand Up @@ -140,7 +139,6 @@ def proof_lexisnexis_then_aamva(timer:, applicant_pii:, should_proof_state_id:)
result[:exception] = exception

result[:context] = {
dob_year_only: false,
should_proof_state_id: should_proof_state_id,
stages: {
resolution: {
Expand Down Expand Up @@ -170,67 +168,6 @@ def proof_lexisnexis_then_aamva(timer:, applicant_pii:, should_proof_state_id:)
)
end

# @return [CallbackLogData]
def proof_aamva_then_lexisnexis_dob_only(timer:, applicant_pii:, dob_year_only:)
proofer_result = timer.time('state_id') do
state_id_proofer.proof(applicant_pii)
end

result = proofer_result.to_h
state_id_success = proofer_result.success?
resolution_success = nil
exception = proofer_result.exception.inspect if proofer_result.exception

result[:context] = {
dob_year_only: dob_year_only,
should_proof_state_id: true,
stages: {
state_id: {
client: state_id_proofer.class.vendor_name,
errors: proofer_result.errors,
exception: exception,
success: state_id_success,
timed_out: proofer_result.timed_out?,
transaction_id: proofer_result.transaction_id,
},
},
}

if state_id_success
lexisnexis_result = timer.time('resolution') do
resolution_proofer.proof(applicant_pii.merge(dob_year_only: dob_year_only))
end

resolution_success = lexisnexis_result.success?
exception = lexisnexis_result.exception.inspect if lexisnexis_result.exception

result.merge!(lexisnexis_result.to_h) do |key, orig, current|
key == :messages ? orig + current : current
end

result[:context][:stages][:resolution] = {
client: resolution_proofer.class.vendor_name,
errors: lexisnexis_result.errors,
exception: exception,
success: lexisnexis_result.success?,
timed_out: lexisnexis_result.timed_out?,
transaction_id: lexisnexis_result.transaction_id,
reference: lexisnexis_result.reference,
}

result[:transaction_id] = lexisnexis_result.transaction_id
result[:reference] = lexisnexis_result.reference
result[:timed_out] = lexisnexis_result.timed_out?
result[:exception] = lexisnexis_result.exception.inspect if lexisnexis_result.exception
end

CallbackLogData.new(
result: result,
resolution_success: resolution_success,
state_id_success: state_id_success,
)
end

def proof_state_id(applicant_pii:, result:)
proofer_result = state_id_proofer.proof(applicant_pii)

Expand Down
1 change: 0 additions & 1 deletion app/services/idv/agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def proof_resolution(
job_arguments = {
encrypted_arguments: encrypted_arguments,
should_proof_state_id: should_proof_state_id,
dob_year_only: IdentityConfig.store.proofing_send_partial_dob,
trace_id: trace_id,
result_id: document_capture_session.result_id,
user_id: user_id,
Expand Down
8 changes: 2 additions & 6 deletions app/services/proofing/lexis_nexis/instant_verify/proofer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Proofer < LexisNexis::Proofer
:state,
:zipcode

optional_attributes :address2, :uuid_prefix, :dob_year_only
optional_attributes :address2, :uuid_prefix

stage :resolution

Expand All @@ -24,11 +24,7 @@ class Proofer < LexisNexis::Proofer
end

def send_verification_request(applicant)
VerificationRequest.new(config: config, applicant: applicant).send(
response_options: {
dob_year_only: applicant[:dob_year_only],
},
)
VerificationRequest.new(config: config, applicant: applicant).send
end
end
end
Expand Down
6 changes: 1 addition & 5 deletions app/services/proofing/lexis_nexis/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ def initialize(config:, applicant:)
@url = build_request_url
end

# @see Response#initialize
# @param [Hash<Symbol, Object>] response_options a hash of options for the Response class
# * +:dob_year_only+
def send(response_options: {})
def send
conn = Faraday.new do |f|
f.request :instrumentation, name: 'request_metric.faraday'
f.request :basic_auth, config.username, config.password
Expand All @@ -30,7 +27,6 @@ def send(response_options: {})
conn.post(url, body, headers) do |req|
req.options.context = { service_name: metric_name }
end,
**response_options,
)
rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e
# NOTE: This is only for when Faraday is using NET::HTTP if the adapter is changed
Expand Down
14 changes: 2 additions & 12 deletions app/services/proofing/lexis_nexis/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,11 @@ class UnexpectedHTTPStatusCodeError < StandardError; end

attr_reader :response

# @param [Boolean] dob_year_only
# @see VerificationErrorParser#initialize
def initialize(response, dob_year_only: false)
def initialize(response)
@response = response
@dob_year_only = dob_year_only
handle_unexpected_http_status_code_error
end

def dob_year_only?
@dob_year_only
end

def verification_errors
return {} if verification_status == 'passed'

Expand All @@ -43,10 +36,7 @@ def response_body
private

def verification_error_parser
@verification_error_parser ||= VerificationErrorParser.new(
response_body,
dob_year_only: dob_year_only?,
)
@verification_error_parser ||= VerificationErrorParser.new(response_body)
end

def handle_unexpected_http_status_code_error
Expand Down
52 changes: 3 additions & 49 deletions app/services/proofing/lexis_nexis/verification_error_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,18 @@ module LexisNexis
class VerificationErrorParser
attr_reader :body

# @param [Boolean] dob_year_only when true, only enforce that the year
# from the date of birth must match
def initialize(response_body, dob_year_only: false)
def initialize(response_body)
@body = response_body
@dob_year_only = dob_year_only
@product_error_messages = parse_product_error_messages
@base_error_message = parse_base_error_message
end

def dob_year_only?
@dob_year_only
end

def parsed_errors
{ base: base_error_message }.merge(product_error_messages)
end

def verification_status
@verification_status ||=
begin
status = body.dig('Status', 'TransactionStatus')

if status == 'failed' && dob_year_only? && product_error_messages.empty?
'passed'
else
status
end
end
@verification_status ||= body.dig('Status', 'TransactionStatus')
end

private
Expand Down Expand Up @@ -64,19 +48,7 @@ def parse_product_error_messages

products.each_with_object({}) do |product, error_messages|
if product['ProductType'] == 'InstantVerify'
original_passed = (product['ProductStatus'] == 'pass')
passed_partial_dob = instant_verify_dob_year_only_pass?(product['Items'])

Rails.logger.info(
{
name: 'lexisnexis_partial_dob',
original_passed: original_passed,
passed_partial_dob: passed_partial_dob,
partial_dob_override_enabled: dob_year_only?,
}.to_json,
)

next if original_passed || (dob_year_only? && passed_partial_dob)
next if product['ProductStatus'] == 'pass'
elsif product['ProductStatus'] == 'pass'
next
end
Expand All @@ -85,24 +57,6 @@ def parse_product_error_messages
error_messages[key] = product
end
end

# if DOBYearVerified passes, but DOBFullVerified fails, we can still allow a pass
def instant_verify_dob_year_only_pass?(items)
items ||= []
dob_full_verified = items.find { |item| item['ItemName'] == 'DOBFullVerified' }
dob_year_verified = items.find { |item| item['ItemName'] == 'DOBYearVerified' }
other_checks = items.reject do |item|
%w[DOBYearVerified DOBFullVerified].include?(item['ItemName'])
end

dob_full_verified.present? &&
item_passed?(dob_year_verified) &&
other_checks.all? { |item| item_passed?(item) }
end

def item_passed?(item)
item && item['ItemStatus'] == 'pass'
end
end
end
end
2 changes: 1 addition & 1 deletion app/services/proofing/mock/resolution_mock_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ResolutionMockClient < Proofing::Base
:state,
:zipcode

optional_attributes :address2, :uuid_prefix, :dob_year_only
optional_attributes :address2, :uuid_prefix

stage :resolution

Expand Down
1 change: 0 additions & 1 deletion config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ poll_rate_for_verify_in_seconds: 3
proofer_mock_fallback: true
proofing_device_profiling_collecting_enabled: false
proofing_device_profiling_decisioning_enabled: false
proofing_send_partial_dob: false
proof_address_max_attempts: 5
proof_address_max_attempt_window_in_minutes: 360
proof_ssn_max_attempts: 10
Expand Down
1 change: 0 additions & 1 deletion lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ def self.build_store(config_map)
config.add(:proofer_mock_fallback, type: :boolean)
config.add(:proofing_device_profiling_collecting_enabled, type: :boolean)
config.add(:proofing_device_profiling_decisioning_enabled, type: :boolean)
config.add(:proofing_send_partial_dob, type: :boolean)
config.add(:proof_address_max_attempts, type: :integer)
config.add(:proof_address_max_attempt_window_in_minutes, type: :integer)
config.add(:proof_ssn_max_attempts, type: :integer)
Expand Down
4 changes: 2 additions & 2 deletions spec/features/idv/analytics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
'IdV: doc auth verify visited' => { flow_path: 'standard', step: 'verify', step_count: 1 },
'IdV: doc auth verify submitted' => { success: true, errors: {}, flow_path: 'standard', step: 'verify', step_count: 1 },
'IdV: doc auth verify_wait visited' => { flow_path: 'standard', step: 'verify_wait', step_count: 1 },
'IdV: doc auth optional verify_wait submitted' => { success: true, errors: {}, address_edited: false, proofing_results: { messages: [], exception: nil, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc', timed_out: false, context: { dob_year_only: false, should_proof_state_id: true, stages: { resolution: { client: 'ResolutionMock', errors: {}, exception: nil, success: true, timed_out: false, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc' }, state_id: { client: 'StateIdMock', errors: {}, success: true, timed_out: false, exception: nil, transaction_id: 'state-id-mock-transaction-id-456', state: 'MT', state_id_jurisdiction: 'ND' } } } }, ssn_is_unique: true, step: 'verify_wait_step_show' },
'IdV: doc auth optional verify_wait submitted' => { success: true, errors: {}, address_edited: false, proofing_results: { messages: [], exception: nil, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc', timed_out: false, context: { should_proof_state_id: true, stages: { resolution: { client: 'ResolutionMock', errors: {}, exception: nil, success: true, timed_out: false, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc' }, state_id: { client: 'StateIdMock', errors: {}, success: true, timed_out: false, exception: nil, transaction_id: 'state-id-mock-transaction-id-456', state: 'MT', state_id_jurisdiction: 'ND' } } } }, ssn_is_unique: true, step: 'verify_wait_step_show' },
'IdV: phone of record visited' => {},
'IdV: phone confirmation form' => { success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202' },
'IdV: phone confirmation vendor' => { success: true, errors: {}, vendor: { messages: [], exception: nil, context: { stages: [{ address: 'AddressMock' }] }, transaction_id: 'address-mock-transaction-id-123', timed_out: false }, new_phone_added: false },
Expand Down Expand Up @@ -70,7 +70,7 @@
'IdV: doc auth verify visited' => { flow_path: 'standard', step: 'verify', step_count: 1 },
'IdV: doc auth verify submitted' => { success: true, errors: {}, flow_path: 'standard', step: 'verify', step_count: 1 },
'IdV: doc auth verify_wait visited' => { flow_path: 'standard', step: 'verify_wait', step_count: 1 },
'IdV: doc auth optional verify_wait submitted' => { success: true, errors: {}, address_edited: false, proofing_results: { messages: [], exception: nil, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc', timed_out: false, context: { dob_year_only: false, should_proof_state_id: true, stages: { resolution: { client: 'ResolutionMock', errors: {}, exception: nil, success: true, timed_out: false, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc' }, state_id: { client: 'StateIdMock', errors: {}, success: true, timed_out: false, exception: nil, transaction_id: 'state-id-mock-transaction-id-456', state: 'MT', state_id_jurisdiction: 'ND' } } } }, ssn_is_unique: true, step: 'verify_wait_step_show' },
'IdV: doc auth optional verify_wait submitted' => { success: true, errors: {}, address_edited: false, proofing_results: { messages: [], exception: nil, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc', timed_out: false, context: { should_proof_state_id: true, stages: { resolution: { client: 'ResolutionMock', errors: {}, exception: nil, success: true, timed_out: false, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc' }, state_id: { client: 'StateIdMock', errors: {}, success: true, timed_out: false, exception: nil, transaction_id: 'state-id-mock-transaction-id-456', state: 'MT', state_id_jurisdiction: 'ND' } } } }, ssn_is_unique: true, step: 'verify_wait_step_show' },
'IdV: phone of record visited' => {},
'IdV: USPS address letter requested' => { enqueued_at: Time.zone.now },
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@
"ItemName": "DOBYearVerified",
"ItemStatus": "pass"
},
{
"ItemName": "SSNLowIssuance",
"ItemStatus": "pass"
},
{
"ItemName": "LexIDDeathMatch",
"ItemStatus": "pass"
Expand Down
Loading