diff --git a/app/assets/images/email/phone_icon.png b/app/assets/images/email/phone_icon.png new file mode 100644 index 00000000000..1e44354ba7b Binary files /dev/null and b/app/assets/images/email/phone_icon.png differ diff --git a/app/jobs/get_usps_proofing_results_job.rb b/app/jobs/get_usps_proofing_results_job.rb index fe136a9409b..51355517576 100644 --- a/app/jobs/get_usps_proofing_results_job.rb +++ b/app/jobs/get_usps_proofing_results_job.rb @@ -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 @@ -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) @@ -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 + 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:) diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index cc49ebb6a47..bde5ae80289 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -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( diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index eff2e191771..6d9b95b472a 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -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 diff --git a/app/views/user_mailer/in_person_please_call.html.erb b/app/views/user_mailer/in_person_please_call.html.erb new file mode 100644 index 00000000000..46394e5727c --- /dev/null +++ b/app/views/user_mailer/in_person_please_call.html.erb @@ -0,0 +1,21 @@ +<%= image_tag( + asset_url('email/phone_icon.png'), + alt: t('image_description.phone_icon'), + width: 88, + height: 88, + ) %> +

<%= t('user_mailer.in_person_please_call.header') %>

+

+ <%= t( + 'user_mailer.in_person_please_call.body.intro_html', + date: I18n.l(14.days.from_now, format: I18n.t('time.formats.event_date')), + ) %> +

+

+ <%= 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, + ) %> +

+ diff --git a/config/locales/image_description/en.yml b/config/locales/image_description/en.yml index 1218a232aee..ee86dcf7d69 100644 --- a/config/locales/image_description/en.yml +++ b/config/locales/image_description/en.yml @@ -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 diff --git a/config/locales/image_description/es.yml b/config/locales/image_description/es.yml index 93a16be6472..bc6fa84ec34 100644 --- a/config/locales/image_description/es.yml +++ b/config/locales/image_description/es.yml @@ -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 diff --git a/config/locales/image_description/fr.yml b/config/locales/image_description/fr.yml index 80a0cc972f4..cfbb10ebe9c 100644 --- a/config/locales/image_description/fr.yml +++ b/config/locales/image_description/fr.yml @@ -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 diff --git a/config/locales/user_mailer/en.yml b/config/locales/user_mailer/en.yml index aaa84453c1f..31a2967c43c 100644 --- a/config/locales/user_mailer/en.yml +++ b/config/locales/user_mailer/en.yml @@ -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. subject: '%{app_name} In-Person Verification Results Delayed' + in_person_please_call: + body: + contact_message_html: Call %{contact_number} and provide them + with the error code %{support_code}. + intro_html: Call our contact center by %{date} 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: diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml index 7c63447255d..5e6397de193 100644 --- a/config/locales/user_mailer/es.yml +++ b/config/locales/user_mailer/es.yml @@ -173,6 +173,14 @@ es: martes 30 de mayo. subject: Los resultados de la verificación presencial de %{app_name} se han retrasado + in_person_please_call: + body: + contact_message_html: Llame al %{contact_number} y facilíteles + el código de error %{support_code}. + intro_html: Llame a nuestro centro de atención antes del + %{date} 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: diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml index b0a9337a88a..1d8c72adc93 100644 --- a/config/locales/user_mailer/fr.yml +++ b/config/locales/user_mailer/fr.yml @@ -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. 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 %{contact_number} et indiquez + le code d’erreur %{support_code}. + intro_html: Appelez notre centre de contact avant le %{date} + 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: diff --git a/spec/jobs/get_usps_proofing_results_job_spec.rb b/spec/jobs/get_usps_proofing_results_job_spec.rb index 59250578295..15012943d94 100644 --- a/spec/jobs/get_usps_proofing_results_job_spec.rb +++ b/spec/jobs/get_usps_proofing_results_job_spec.rb @@ -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 diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb index 367028a3d84..c39f6e1b2b7 100644 --- a/spec/mailers/previews/user_mailer_preview.rb +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -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, diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index a49ff10a5d1..b9be6eb47bb 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -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