Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added app/assets/images/email/phone_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 38 additions & 1 deletion app/jobs/get_usps_proofing_results_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,31 @@ def handle_successful_status_update(enrollment, response)
end
end

def handle_passed_with_fraud_review_pending(enrollment, response)
proofed_at = parse_usps_timestamp(response['transactionEndDateTime'])
enrollment_outcomes[:enrollments_passed] += 1
analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_enrollment_updated(
**enrollment_analytics_attributes(enrollment, complete: true),
**response_analytics_attributes(response),
passed: true,
reason: 'Passed with fraud pending',
job_name: self.class.name,
)
enrollment.update(
status: :passed,
proofed_at: proofed_at,
status_check_completed_at: Time.zone.now,
)

# send email
send_please_call_email(enrollment.user, enrollment)
analytics(user: enrollment.user).
idv_in_person_usps_proofing_results_job_please_call_email_initiated(
**email_analytics_attributes(enrollment),
job_name: self.class.name,
)
end

def handle_unsupported_secondary_id(enrollment, response)
proofed_at = parse_usps_timestamp(response['transactionEndDateTime'])
enrollment_outcomes[:enrollments_failed] += 1
Expand Down Expand Up @@ -435,7 +460,9 @@ def process_enrollment_response(enrollment, response)

case response['status']
when IPP_STATUS_PASSED
if passed_with_unsupported_secondary_id_type?(response)
if fraud_result_pending?(enrollment)
handle_passed_with_fraud_review_pending(enrollment, response)
elsif passed_with_unsupported_secondary_id_type?(response)
handle_unsupported_secondary_id(enrollment, response)
elsif SUPPORTED_ID_TYPES.include?(response['primaryIdType'])
handle_successful_status_update(enrollment, response)
Expand Down Expand Up @@ -489,6 +516,16 @@ def send_failed_fraud_email(user, enrollment)
end
end

def send_please_call_email(user, enrollment)
user.confirmed_email_addresses.each do |email_address|
# rubocop:disable IdentityIdp/MailLaterLinter
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is there an easy-ish formatting thing we can do so we're not disabling the linter? I feel like that's a bit of a flag.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a custom rule that enforces the use of deliver_now_or_later vs deliver_later. I'd wonder if we should be using deliver_now_and_later to avoid the lint? There are a few cases where we want to ensure it's delivered exclusively now or later, but I'm not sure that applies here.

https://github.com/18F/identity-idp/blob/main/lib/linters/mail_later_linter.rb

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok interesting. Thanks for that info @aduth. Yeah unfortunately I don't know enough about the motivation for enforcing that we do these calls async. This might be a good thing to bring back to our team and see what folks think.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea with deliver_now_or_later is that we'd typically want to deliver messages asynchronously (to avoid blocking the response), but this is controlled through configuration to allow us the option to deliver synchronously if needed in an environment or if there's issues with asynchronous delivery.

See also:

def deliver_now_or_later(opts = {})
if IdentityConfig.store.deliver_mail_async
deliver_later(opts)
else
deliver_now
end
end

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

digging back through some old prs and it looks like we previously made the decision to use deliver_later for the emails sent in this job, pr where we made the switch

Copy link
Copy Markdown
Contributor

@n1zyy n1zyy Feb 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally don't like the pattern of rubocop:disable for stuff without a really compelling reason. But, as you pointed out, and as is found in some old Slack threads, there's precedent here: every other mailer call in this file is deliver_later.

So, in the end, unless others object, I would support leaving this as-is. I'd rather the file stay consistent, even if it's not strictly necessary in this case.

UserMailer.with(user: user, email_address: email_address).in_person_please_call(
enrollment: enrollment,
).deliver_later(**notification_delivery_params(enrollment))
# rubocop:enable IdentityIdp/MailLaterLinter
end
end

# enqueue sms notification job when it's expired or success
# @param [InPersonEnrollment] enrollment
def send_enrollment_status_sms_notification(enrollment:)
Expand Down
14 changes: 14 additions & 0 deletions app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,20 @@ def in_person_failed_fraud(enrollment:)
end
end

def in_person_please_call(enrollment:)
with_user_locale(user) do
@presenter = Idv::InPerson::VerificationResultsEmailPresenter.new(
enrollment: enrollment,
url_options: url_options,
)
@hide_title = true
mail(
to: email_address.email,
subject: t('user_mailer.in_person_please_call.subject', app_name: APP_NAME),
)
end
end

def in_person_outage_notification(enrollment:)
with_user_locale(user) do
@presenter = Idv::InPerson::VerificationResultsEmailPresenter.new(
Expand Down
10 changes: 10 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2273,6 +2273,16 @@ def idv_in_person_usps_proofing_results_job_exception(
)
end

# Tracks please call emails that are initiated during GetUspsProofingResultsJob
def idv_in_person_usps_proofing_results_job_please_call_email_initiated(
**extra
)
track_event(
:idv_in_person_usps_proofing_results_job_please_call_email_initiated,
**extra,
)
end

# GetUspsProofingResultsJob is beginning. Includes some metadata about what the job will do
# @param [Integer] enrollments_count number of enrollments eligible for status check
# @param [Integer] reprocess_delay_minutes minimum delay since last status check
Expand Down
21 changes: 21 additions & 0 deletions app/views/user_mailer/in_person_please_call.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<%= image_tag(
asset_url('email/phone_icon.png'),
alt: t('image_description.phone_icon'),
width: 88,
height: 88,
) %>
<h1><%= t('user_mailer.in_person_please_call.header') %></h1>
<p>
<%= t(
'user_mailer.in_person_please_call.body.intro_html',
date: I18n.l(14.days.from_now, format: I18n.t('time.formats.event_date')),
) %>
</p>
<p>
<%= t(
'user_mailer.in_person_please_call.body.contact_message_html',
contact_number: IdentityConfig.store.idv_contact_phone_number,
support_code: IdentityConfig.store.lexisnexis_threatmetrix_support_code,
) %>
</p>

1 change: 1 addition & 0 deletions config/locales/image_description/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ en:
laptop: Laptop computer
laptop_and_phone: Laptop and phone
personal_key: Personal key
phone_icon: Image of a phone
post_office: Post office
totp_qrcode: QR code for authenticator app
us_flag: US flag
Expand Down
1 change: 1 addition & 0 deletions config/locales/image_description/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ es:
laptop: Computadora portátil
laptop_and_phone: computadora portátil y celular
personal_key: Clave personal
phone_icon: Imagen de un teléfono
post_office: Oficina de Correos
totp_qrcode: Código QR para la aplicación de autenticación
us_flag: Bandera de estados unidos
Expand Down
1 change: 1 addition & 0 deletions config/locales/image_description/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ fr:
laptop: Ordinateur portable
laptop_and_phone: ordinateur et téléphone portable
personal_key: Clé personnelle
phone_icon: Image d’un téléphone
post_office: Bureau de Poste
totp_qrcode: Code QR pour l’application d’authentification
us_flag: Drapeau américain
Expand Down
8 changes: 8 additions & 0 deletions config/locales/user_mailer/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ en:
identity verification at a Post Office between May 20 and May 29, your
results may not be emailed to you until Tuesday, May 30.</strong>
subject: '%{app_name} In-Person Verification Results Delayed'
in_person_please_call:
body:
contact_message_html: Call <strong>%{contact_number}</strong> and provide them
with the error code <strong>%{support_code}</strong>.
intro_html: Call our contact center by <strong>%{date}</strong> to continue
verifying your identity.
header: Please give us a call
subject: Call %{app_name} to continue with your identity verification
in_person_ready_to_verify:
subject: You’re ready to verify your identity with %{app_name} in person
in_person_ready_to_verify_reminder:
Expand Down
8 changes: 8 additions & 0 deletions config/locales/user_mailer/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ es:
martes 30 de mayo.</strong>
subject: Los resultados de la verificación presencial de %{app_name} se han
retrasado
in_person_please_call:
body:
contact_message_html: Llame al <strong>%{contact_number}</strong> y facilíteles
el código de error <strong>%{support_code}</strong>.
intro_html: Llame a nuestro centro de atención antes del
<strong>%{date}</strong> para seguir verificando su identidad.
header: Llámenos
subject: Llame a %{app_name} para proseguir con la verificación de su identidad
in_person_ready_to_verify:
subject: Está listo para verificar su identidad con %{app_name} en persona
in_person_ready_to_verify_reminder:
Expand Down
8 changes: 8 additions & 0 deletions config/locales/user_mailer/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ fr:
de poste entre le 20 et le 29 mai, vos résultats ne vous seront
peut-être pas envoyés par courriel avant le mardi 30 mai.</strong>
subject: Les résultats de la vérification en personne de %{app_name} sont reportés
in_person_please_call:
body:
contact_message_html: Appelez le <strong>%{contact_number}</strong> et indiquez
le code d’erreur <strong>%{support_code}</strong>.
intro_html: Appelez notre centre de contact avant le <strong>%{date}</strong>
pour continuer à vérifier votre identité.
header: S’il vous plaît, appelez-nous
subject: Appelez %{app_name} pour poursuivre la vérification de votre identité
in_person_ready_to_verify:
subject: Vous êtes prêt à vérifier votre identité avec %{app_name} en personne
in_person_ready_to_verify_reminder:
Expand Down
40 changes: 40 additions & 0 deletions spec/jobs/get_usps_proofing_results_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,46 @@
expect(profile.fraud_review_pending_at).not_to be_nil
expect(profile).not_to be_active
end

context 'when the enrollment has passed' do
before(:each) do
pending_enrollment.profile.update!(fraud_pending_reason: 'threatmetrix_review')
stub_request_passed_proofing_results
end

it 'sends the please call email' do
user = pending_enrollment.user

freeze_time do
expect do
job.perform(Time.zone.now)
end.to have_enqueued_mail(UserMailer, :in_person_please_call).with(
params: { user: user, email_address: user.email_addresses.first },
args: [{ enrollment: pending_enrollment }],
)
end
end

it 'logs the expected analytics events' do
freeze_time do
job.perform(Time.zone.now)
end
expect(job_analytics).to have_logged_event(
:idv_in_person_usps_proofing_results_job_please_call_email_initiated,
hash_including(
job_name: 'GetUspsProofingResultsJob',
),
)
expect(job_analytics).to have_logged_event(
'GetUspsProofingResultsJob: Enrollment status updated',
hash_including(
passed: true,
reason: 'Passed with fraud pending',
job_name: 'GetUspsProofingResultsJob',
),
)
end
end
end
end
end
Expand Down
6 changes: 6 additions & 0 deletions spec/mailers/previews/user_mailer_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ def in_person_failed_fraud
)
end

def in_person_please_call
UserMailer.with(user: user, email_address: email_address_record).in_person_please_call(
enrollment: in_person_enrollment,
)
end

def in_person_outage_notification
UserMailer.with(user: user, email_address: email_address_record).in_person_outage_notification(
enrollment: in_person_enrollment,
Expand Down
11 changes: 11 additions & 0 deletions spec/mailers/user_mailer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,17 @@ def expect_email_body_to_have_help_and_contact_links
it_behaves_like 'an email that respects user email locale preference'
end

describe '#in_person_please_call' do
let(:mail) do
UserMailer.with(user: user, email_address: email_address).in_person_please_call(
enrollment: enrollment,
)
end

it_behaves_like 'a system email'
it_behaves_like 'an email that respects user email locale preference'
end

describe '#in_person_completion_survey' do
let(:mail) do
UserMailer.with(user: user, email_address: email_address).in_person_completion_survey
Expand Down