-
Notifications
You must be signed in to change notification settings - Fork 166
LG-7012 LG-7010 Add/Integrate new DDP Proofer #6684
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7ab60b3
723a1f9
9e1eefa
f9fdca8
cecfb90
603eb50
d69f5de
8844ccf
7c6db18
6be75d8
2909f7a
d641b54
ad6b150
e49e029
2251142
ccd9aa3
0057afe
b084e2d
23e2d3a
1b3e44c
dc94760
50caa6e
bd71ed6
66a05d9
cc76729
8c1a77a
fa60003
0564dbd
ffa5095
13c3037
ed933ed
e8a6a19
e6ab5ca
1c84082
a311bd3
528673f
2bc12ba
02cccb8
4fc272d
97f2e5b
e9a8a62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,7 @@ class ResolutionProofingJob < ApplicationJob | |
| ) | ||
|
|
||
| def perform(result_id:, encrypted_arguments:, trace_id:, should_proof_state_id:, | ||
| dob_year_only:) | ||
| dob_year_only:, user_id: nil, threatmetrix_session_id: nil) | ||
| timer = JobHelpers::Timer.new | ||
|
|
||
| raise_stale_job! if stale_job?(enqueued_at) | ||
|
|
@@ -25,6 +25,15 @@ def perform(result_id:, encrypted_arguments:, trace_id:, should_proof_state_id:, | |
|
|
||
| applicant_pii = decrypted_args[:applicant_pii] | ||
|
|
||
| threatmetrix_result = nil | ||
| if use_lexisnexis_ddp_threatmetrix_before_rdp_instant_verify? | ||
| user = User.find_by(id: user_id) | ||
| threatmetrix_result = proof_lexisnexis_ddp_with_threatmetrix( | ||
| applicant_pii, user, threatmetrix_session_id | ||
| ) | ||
| log_threatmetrix_info(threatmetrix_result, user) | ||
| end | ||
|
|
||
| callback_log_data = if dob_year_only && should_proof_state_id | ||
| proof_aamva_then_lexisnexis_dob_only( | ||
| timer: timer, | ||
|
|
@@ -39,22 +48,48 @@ def perform(result_id:, encrypted_arguments:, trace_id:, should_proof_state_id:, | |
| ) | ||
| end | ||
|
|
||
| add_threatmetrix_result_to_callback_result(callback_log_data.result, threatmetrix_result) | ||
|
|
||
| document_capture_session = DocumentCaptureSession.new(result_id: result_id) | ||
| document_capture_session.store_proofing_result(callback_log_data.result) | ||
| ensure | ||
| logger.info( | ||
| { | ||
| name: 'ProofResolution', | ||
| trace_id: trace_id, | ||
| resolution_success: callback_log_data&.resolution_success, | ||
| state_id_success: callback_log_data&.state_id_success, | ||
| timing: timer.results, | ||
| }.to_json, | ||
| logger_info_hash( | ||
| name: 'ProofResolution', | ||
| trace_id: trace_id, | ||
| resolution_success: callback_log_data&.resolution_success, | ||
| state_id_success: callback_log_data&.state_id_success, | ||
| timing: timer.results, | ||
| ) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def log_threatmetrix_info(threatmetrix_result, user) | ||
| logger_info_hash( | ||
| name: 'ThreatMetrix', | ||
| user_id: user&.uuid, | ||
| threatmetrix_request_id: threatmetrix_result.transaction_id, | ||
| threatmetrix_success: threatmetrix_result.success?, | ||
| ) | ||
| end | ||
|
|
||
| def logger_info_hash(hash) | ||
| logger.info(hash.to_json) | ||
| end | ||
|
Comment on lines
+76
to
+78
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not convinced this helper method is worth it?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was difficult to test multiple logger calls especially because old instant verify code was doing a partial check. this made it much more simple to test
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's also a tad bit DRYer |
||
|
|
||
| def add_threatmetrix_result_to_callback_result(callback_log_data_result, threatmetrix_result) | ||
| callback_log_data_result[:threatmetrix_success] = threatmetrix_result.success? | ||
| callback_log_data_result[:threatmetrix_request_id] = threatmetrix_result.transaction_id | ||
| end | ||
|
|
||
| def proof_lexisnexis_ddp_with_threatmetrix(applicant_pii, user, threatmetrix_session_id) | ||
| return unless applicant_pii | ||
| ddp_pii = applicant_pii.dup | ||
| ddp_pii[:threatmetrix_session_id] = threatmetrix_session_id | ||
| ddp_pii[:email] = user&.confirmed_email_addresses&.first&.email | ||
| lexisnexis_ddp_proofer.proof(ddp_pii) | ||
| end | ||
|
|
||
| # @return [CallbackLogData] | ||
| def proof_lexisnexis_then_aamva(timer:, applicant_pii:, should_proof_state_id:) | ||
| proofer_result = timer.time('resolution') do | ||
|
|
@@ -202,6 +237,19 @@ def resolution_proofer | |
| end | ||
| end | ||
|
|
||
| def lexisnexis_ddp_proofer | ||
| @lexisnexis_ddp_proofer ||= | ||
| if IdentityConfig.store.proofer_mock_fallback | ||
| Proofing::Mock::DdpMockClient.new | ||
| else | ||
| Proofing::LexisNexis::Ddp::Proofer.new( | ||
| api_key: IdentityConfig.store.lexisnexis_threatmetrix_api_key, | ||
| org_id: IdentityConfig.store.lexisnexis_threatmetrix_org_id, | ||
| base_url: IdentityConfig.store.lexisnexis_threatmetrix_base_url, | ||
| ) | ||
| end | ||
| end | ||
|
|
||
| def state_id_proofer | ||
| @state_id_proofer ||= | ||
| if IdentityConfig.store.proofer_mock_fallback | ||
|
|
@@ -218,4 +266,8 @@ def state_id_proofer | |
| ) | ||
| end | ||
| end | ||
|
|
||
| def use_lexisnexis_ddp_threatmetrix_before_rdp_instant_verify? | ||
| IdentityConfig.store.lexisnexis_threatmetrix_enabled | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| module Proofing | ||
| module LexisNexis | ||
| module Ddp | ||
| class Proofer < LexisNexis::Proofer | ||
| vendor_name 'lexisnexis:ddp' | ||
|
|
||
| required_attributes :state_id_number, | ||
| :threatmetrix_session_id, | ||
| :state_id_number, | ||
| :first_name, | ||
| :last_name, | ||
| :dob, | ||
| :ssn, | ||
| :address1, | ||
| :city, | ||
| :state, | ||
| :zipcode | ||
|
|
||
| optional_attributes :address2, :phone, :email | ||
|
|
||
| stage :resolution | ||
|
|
||
| proof do |applicant, result| | ||
| proof_applicant(applicant, result) | ||
| end | ||
|
|
||
| def send_verification_request(applicant) | ||
| VerificationRequest.new(config: config, applicant: applicant).send | ||
| end | ||
|
|
||
| def proof_applicant(applicant, result) | ||
| response = send_verification_request(applicant) | ||
| process_response(response, result) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def process_response(response, result) | ||
| body = response.response_body | ||
| result.transaction_id = body['request_id'] | ||
| request_result = body['request_result'] | ||
| review_status = body['review_status'] | ||
| result.add_error(:request_result, request_result) unless request_result == 'success' | ||
| result.add_error(:review_status, review_status) unless review_status == 'pass' | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| module Proofing | ||
| module LexisNexis | ||
| module Ddp | ||
| class VerificationRequest < Request | ||
| private | ||
|
|
||
| def build_request_body | ||
| { | ||
| api_key: config.api_key, | ||
| org_id: config.org_id, | ||
| account_address_street1: applicant[:address1], | ||
| account_address_street2: applicant[:address2] || '', | ||
| account_address_city: applicant[:city], | ||
| account_address_state: applicant[:state], | ||
| account_address_country: 'US', | ||
| account_address_zip: applicant[:zipcode], | ||
| account_date_of_birth: applicant[:dob] ? | ||
| Date.parse(applicant[:dob]).strftime('%Y%m%d') : '', | ||
| account_email: applicant[:email], | ||
| account_first_name: applicant[:first_name], | ||
| account_last_name: applicant[:last_name], | ||
| account_telephone: '', # applicant[:phone], decision was made not to send phone | ||
| drivers_license_number_hash: applicant[:state_id_number] ? | ||
| OpenSSL::Digest::SHA256.hexdigest(applicant[:state_id_number].gsub(/\W/, '')) : '', | ||
|
Comment on lines
+23
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd consider extracting methods for these
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree. This particular code is covered but it's quick and dirty. |
||
| event_type: 'ACCOUNT_CREATION', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a list of other event types? this is more like an account upgrade typically than an account create
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems to be the best option. ie create a verified profile |
||
| service_type: 'all', | ||
| session_id: applicant[:threatmetrix_session_id], | ||
| ssn_hash: OpenSSL::Digest::SHA256.hexdigest(applicant[:ssn].gsub(/\D/, '')), | ||
| }.to_json | ||
| end | ||
|
|
||
| def metric_name | ||
| 'lexis_nexis_ddp' | ||
| end | ||
|
|
||
| def url_request_path | ||
| '/api/session-query' | ||
| end | ||
|
|
||
| def timeout | ||
| IdentityConfig.store.lexisnexis_threatmetrix_timeout | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| module Proofing | ||
| module Mock | ||
| class DdpMockClient < Proofing::Base | ||
| vendor_name 'DdpMock' | ||
|
|
||
| required_attributes :threatmetrix_session_id, | ||
| :state_id_number, | ||
| :first_name, | ||
| :last_name, | ||
| :dob, | ||
| :ssn, | ||
| :address1, | ||
| :city, | ||
| :state, | ||
| :zipcode | ||
|
|
||
| optional_attributes :address2, :phone, :email | ||
|
|
||
| stage :resolution | ||
|
|
||
| TRANSACTION_ID = 'ddp-mock-transaction-id-123' | ||
|
|
||
| proof do |applicant, result| | ||
| result.transaction_id = TRANSACTION_ID | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "error_detail": "service_type", | ||
| "request_id":"1234-abcd", | ||
| "request_result":"fail_invalid_parameter" | ||
| } |

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so I see that we're passing
user_id, but I think that ActiveJob will correctly serialize/deserialize ActiveRecord models, we should just passuserif we wanted to I thinkThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I modeled it after AddressProofingJob which takes user_id