diff --git a/app/controllers/idv/enter_password_controller.rb b/app/controllers/idv/enter_password_controller.rb index 64768ea7ec8..58de1dc2f86 100644 --- a/app/controllers/idv/enter_password_controller.rb +++ b/app/controllers/idv/enter_password_controller.rb @@ -127,7 +127,7 @@ def confirm_current_password end def init_profile - idv_session.create_profile_from_applicant_with_password( + profile = idv_session.create_profile_from_applicant_with_password( password, is_enhanced_ipp: resolved_authn_context_result.enhanced_ipp?, proofing_components: ProofingComponents.new( @@ -137,16 +137,20 @@ def init_profile user_session:, ).to_h, ) - if idv_session.verify_by_mail? - current_user.send_email_to_all_addresses(:verify_by_mail_letter_requested) - log_letter_enqueued_analytics(resend: false) - end - if idv_session.profile.active? + if profile.active? create_user_event(:account_verified) UserAlerts::AlertUserAboutAccountVerified.call( profile: idv_session.profile, ) + elsif profile.gpo_verification_pending? + current_user.send_email_to_all_addresses(:verify_by_mail_letter_requested) + log_letter_enqueued_analytics(resend: false) + elsif profile.in_person_verification_pending? + return + elsif profile.fraud_review_pending? + # Note that IPP + GPO flows send this email later on + current_user.send_email_to_all_addresses(:idv_please_call) end end diff --git a/app/jobs/get_usps_proofing_results_job.rb b/app/jobs/get_usps_proofing_results_job.rb index e529c76b334..5a18bc2d578 100644 --- a/app/jobs/get_usps_proofing_results_job.rb +++ b/app/jobs/get_usps_proofing_results_job.rb @@ -487,7 +487,7 @@ def handle_passed_with_fraud_review_pending(enrollment, response) ) # send email - send_please_call_email(enrollment:, visited_location_name: response['proofingPostOffice']) + send_please_call_email(enrollment:) analytics(user: enrollment.user). idv_in_person_usps_proofing_results_job_please_call_email_initiated( **email_analytics_attributes(enrollment), @@ -605,13 +605,12 @@ def send_failed_fraud_email(enrollment:, visited_location_name:) end end - def send_please_call_email(enrollment:, visited_location_name:) + def send_please_call_email(enrollment:) enrollment.user.confirmed_email_addresses.each do |email_address| # rubocop:disable IdentityIdp/MailLaterLinter - UserMailer.with(user: enrollment.user, email_address: email_address).in_person_please_call( - enrollment: enrollment, - visited_location_name: visited_location_name, - ).deliver_later(**notification_delivery_params(enrollment)) + UserMailer.with(user: enrollment.user, email_address: email_address). + idv_please_call. + deliver_later(**notification_delivery_params(enrollment)) # rubocop:enable IdentityIdp/MailLaterLinter end end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 32678fad843..46e480dab2a 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -248,6 +248,20 @@ def account_verified(profile:) end end + def idv_please_call(**) + with_user_locale(user) do + @hide_title = true + + mail( + to: email_address.email, + subject: t('user_mailer.idv_please_call.subject', app_name: APP_NAME), + template_name: 'idv_please_call', + ) + end + end + + alias_method :in_person_please_call, :idv_please_call + def in_person_completion_survey with_user_locale(user) do @header = t('user_mailer.in_person_completion_survey.header') @@ -373,21 +387,6 @@ def in_person_failed_fraud(enrollment:, visited_location_name: nil) end end - def in_person_please_call(enrollment:, visited_location_name: nil) - with_user_locale(user) do - @presenter = Idv::InPerson::VerificationResultsEmailPresenter.new( - enrollment: enrollment, - url_options: url_options, - visited_location_name: visited_location_name, - ) - @hide_title = true - mail( - to: email_address.email, - subject: t('user_mailer.in_person_please_call.subject', app_name: APP_NAME), - ) - end - end - def account_rejected with_user_locale(user) do mail( diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb index 071ce403a80..f7f48f83add 100644 --- a/app/services/idv/session.rb +++ b/app/services/idv/session.rb @@ -105,6 +105,7 @@ def respond_to_missing?(method_sym, include_private) VALID_SESSION_ATTRIBUTES.include?(attr_name_sym) || super end + # @return [Profile] def create_profile_from_applicant_with_password( user_password, is_enhanced_ipp:, proofing_components: ) @@ -141,6 +142,8 @@ def create_profile_from_applicant_with_password( if profile.gpo_verification_pending? create_gpo_entry(profile_maker.pii_attributes, profile) end + + profile end def opt_in_param diff --git a/app/views/user_mailer/in_person_please_call.html.erb b/app/views/user_mailer/idv_please_call.html.erb similarity index 69% rename from app/views/user_mailer/in_person_please_call.html.erb rename to app/views/user_mailer/idv_please_call.html.erb index f8cef7a4ae8..54f4a89ba9f 100644 --- a/app/views/user_mailer/in_person_please_call.html.erb +++ b/app/views/user_mailer/idv_please_call.html.erb @@ -4,16 +4,16 @@ width: 88, height: 88, ) %> -
<%= t( - 'user_mailer.in_person_please_call.body.intro_html', + 'user_mailer.idv_please_call.body.intro_html', date: I18n.l(14.days.from_now, format: I18n.t('time.formats.full_date')), ) %>
<%= t( - 'user_mailer.in_person_please_call.body.contact_message_html', + 'user_mailer.idv_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/en.yml b/config/locales/en.yml index 7593fbd2ff2..d739ce5b1f0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1867,6 +1867,10 @@ user_mailer.email_deleted.header: An email address was deleted from your %{app_n user_mailer.email_deleted.help_html: If you did not want to delete this email address, please visit the %{app_name_html} %{help_link_html} or %{contact_link_html}. user_mailer.email_deleted.subject: Email address deleted user_mailer.help_link_text: Help Center +user_mailer.idv_please_call.body.contact_message_html: Call %{contact_number} and provide them with the error code %{support_code}. +user_mailer.idv_please_call.body.intro_html: Call our contact center by %{date} to continue verifying your identity. +user_mailer.idv_please_call.header: Please give us a call +user_mailer.idv_please_call.subject: Call %{app_name} to continue with your identity verification user_mailer.in_person_completion_survey.body.cta.callout: Click the button below to get started. user_mailer.in_person_completion_survey.body.cta.label: Take our survey user_mailer.in_person_completion_survey.body.greeting: Hello, @@ -1892,10 +1896,6 @@ user_mailer.in_person_failed.intro: Your identity could not be verified at the % user_mailer.in_person_failed.subject: Your identity could not be verified in person user_mailer.in_person_failed.verifying_identity: 'When verifying your identity:' user_mailer.in_person_failed.verifying_step_not_expired: Your state‑issued ID or driver’s license must not be expired. We do not currently accept any other forms of identification, such as passports and military IDs. -user_mailer.in_person_please_call.body.contact_message_html: Call %{contact_number} and provide them with the error code %{support_code}. -user_mailer.in_person_please_call.body.intro_html: Call our contact center by %{date} to continue verifying your identity. -user_mailer.in_person_please_call.header: Please give us a call -user_mailer.in_person_please_call.subject: Call %{app_name} to continue with your identity verification user_mailer.in_person_ready_to_verify_reminder.greeting: Hello, user_mailer.in_person_ready_to_verify_reminder.heading.one: You have %{count} day left to verify your identity in person user_mailer.in_person_ready_to_verify_reminder.heading.other: You have %{count} days left to verify your identity in person diff --git a/config/locales/es.yml b/config/locales/es.yml index baffc5ec289..bc3078daf3e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1879,6 +1879,10 @@ user_mailer.email_deleted.header: Se eliminó una dirección de correo electrón user_mailer.email_deleted.help_html: Si no deseaba eliminar esta dirección de correo electrónico, visite %{help_link_html} de %{app_name_html} o %{contact_link_html}. user_mailer.email_deleted.subject: Dirección de correo electrónico eliminada user_mailer.help_link_text: Centro de ayuda +user_mailer.idv_please_call.body.contact_message_html: Llame al %{contact_number} y proporcione el código de error %{support_code}. +user_mailer.idv_please_call.body.intro_html: Llame a nuestro centro de contacto antes del %{date} para seguir verificando su identidad. +user_mailer.idv_please_call.header: Llámenos +user_mailer.idv_please_call.subject: Llame a %{app_name} para continuar con la verificación de identidad user_mailer.in_person_completion_survey.body.cta.callout: Haga clic en el botón siguiente para empezar. user_mailer.in_person_completion_survey.body.cta.label: Responda a nuestra encuesta user_mailer.in_person_completion_survey.body.greeting: 'Hola:' @@ -1904,10 +1908,6 @@ user_mailer.in_person_failed.intro: No se pudo verificar su identidad en la ofic user_mailer.in_person_failed.subject: No se pudo verificar su identidad en persona user_mailer.in_person_failed.verifying_identity: 'Cuando verifique su identidad:' user_mailer.in_person_failed.verifying_step_not_expired: Su licencia de conducir o identificación emitida por el estado debe estar vigente. Actualmente no aceptamos otras formas de identificación, como pasaportes o identificaciones militares. -user_mailer.in_person_please_call.body.contact_message_html: Llame al %{contact_number} y proporcione el código de error %{support_code}. -user_mailer.in_person_please_call.body.intro_html: Llame a nuestro centro de contacto antes del %{date} para seguir verificando su identidad. -user_mailer.in_person_please_call.header: Llámenos -user_mailer.in_person_please_call.subject: Llame a %{app_name} para continuar con la verificación de identidad user_mailer.in_person_ready_to_verify_reminder.greeting: 'Hola:' user_mailer.in_person_ready_to_verify_reminder.heading.one: Le queda %{count} día para verificar su identidad en persona user_mailer.in_person_ready_to_verify_reminder.heading.other: Le quedan %{count} días para verificar su identidad en persona diff --git a/config/locales/fr.yml b/config/locales/fr.yml index dc2ae99a5ed..1be7b35e1e7 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1867,6 +1867,10 @@ user_mailer.email_deleted.header: Une adresse e-mail a été supprimée de votre user_mailer.email_deleted.help_html: Si vous ne souhaitez pas supprimer cette adresse e-mail, veuillez visiter le %{help_link_html} de %{app_name_html} ou %{contact_link_html}. user_mailer.email_deleted.subject: Adresse e-mail supprimée user_mailer.help_link_text: Centre d’aide +user_mailer.idv_please_call.body.contact_message_html: Appelez le %{contact_number} et indiquez le code d’erreur %{support_code}. +user_mailer.idv_please_call.body.intro_html: Appelez notre centre de contact avant le %{date} pour continuer à vérifier votre identité. +user_mailer.idv_please_call.header: S’il vous plaît, appelez-nous +user_mailer.idv_please_call.subject: Appeler %{app_name} afin de poursuivre la vérification de votre identité user_mailer.in_person_completion_survey.body.cta.callout: Cliquez sur le bouton ci-dessous pour commencer. user_mailer.in_person_completion_survey.body.cta.label: Répondez à notre enquête user_mailer.in_person_completion_survey.body.greeting: Bonjour, @@ -1892,10 +1896,6 @@ user_mailer.in_person_failed.intro: Votre identité n’a pas pu être vérifié user_mailer.in_person_failed.subject: Votre identité n’a pas pu être vérifiée en personne user_mailer.in_person_failed.verifying_identity: 'Lors de la vérification de votre identité :' user_mailer.in_person_failed.verifying_step_not_expired: Votre carte d’identité délivrée par l’État ou votre permis de conduire ne doit pas être périmé. Nous n’acceptons actuellement aucune autre pièce d’identité, comme les passeports et les cartes d’identité militaires. -user_mailer.in_person_please_call.body.contact_message_html: Appelez le %{contact_number} et indiquez le code d’erreur %{support_code}. -user_mailer.in_person_please_call.body.intro_html: Appelez notre centre de contact avant le %{date} pour continuer à vérifier votre identité. -user_mailer.in_person_please_call.header: S’il vous plaît, appelez-nous -user_mailer.in_person_please_call.subject: Appeler %{app_name} afin de poursuivre la vérification de votre identité user_mailer.in_person_ready_to_verify_reminder.greeting: Bonjour, user_mailer.in_person_ready_to_verify_reminder.heading.one: Il vous reste %{count} jour pour vérifier votre identité en personne user_mailer.in_person_ready_to_verify_reminder.heading.other: Il vous reste %{count} jours pour vérifier votre identité en personne diff --git a/config/locales/zh.yml b/config/locales/zh.yml index ea75d8f839a..a92412b559c 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -1880,6 +1880,10 @@ user_mailer.email_deleted.header: 一个电邮地址被从你的 %{app_name} 用 user_mailer.email_deleted.help_html: 如果你没有想删除这一电邮地址,请访问 %{app_name_html} %{help_link_html} 或者 %{contact_link_html}。 user_mailer.email_deleted.subject: 电邮地址已删除 user_mailer.help_link_text: 帮助中心 +user_mailer.idv_please_call.body.contact_message_html: 打电话给 %{contact_number} 并向他们提供错误 代码 %{support_code}。 +user_mailer.idv_please_call.body.intro_html: 请在 %{date} 之前给我们的联系中心打电话,以继续验证你的身份。 +user_mailer.idv_please_call.header: 请给我们打个电话 +user_mailer.idv_please_call.subject: 致电 %{app_name} 继续进行身份验证 user_mailer.in_person_completion_survey.body.cta.callout: 点击下面的按钮来开始 user_mailer.in_person_completion_survey.body.cta.label: 填写我们的意见调查 user_mailer.in_person_completion_survey.body.greeting: 你好, @@ -1905,10 +1909,6 @@ user_mailer.in_person_failed.intro: 你的身份于 %{date}在 %{location} 邮 user_mailer.in_person_failed.subject: 你的身份未能亲身被验证。 user_mailer.in_person_failed.verifying_identity: '验证你的身份时:' user_mailer.in_person_failed.verifying_step_not_expired: 你的州政府颁发的身份证件或驾照绝对没有过期。我们目前不接受任何其他形式的身份证件,比如护照和军队身份证件。 -user_mailer.in_person_please_call.body.contact_message_html: 打电话给 %{contact_number} 并向他们提供错误 代码 %{support_code}。 -user_mailer.in_person_please_call.body.intro_html: 请在 %{date} 之前给我们的联系中心打电话,以继续验证你的身份。 -user_mailer.in_person_please_call.header: 请给我们打个电话 -user_mailer.in_person_please_call.subject: 致电 %{app_name} 继续进行身份验证 user_mailer.in_person_ready_to_verify_reminder.greeting: 你好, user_mailer.in_person_ready_to_verify_reminder.heading.one: 你距离亲身验证身份截止日期还有 %{count} 天 user_mailer.in_person_ready_to_verify_reminder.heading.other: 你距离亲身验证身份截止日期还有 %{count} 天 diff --git a/spec/controllers/idv/enter_password_controller_spec.rb b/spec/controllers/idv/enter_password_controller_spec.rb index 5bbaab77b78..5a130ab65b2 100644 --- a/spec/controllers/idv/enter_password_controller_spec.rb +++ b/spec/controllers/idv/enter_password_controller_spec.rb @@ -17,6 +17,8 @@ subject.idv_session end + let(:threatmetrix_review_status) { 'pass' } + before do stub_analytics stub_sign_in(user) @@ -27,6 +29,7 @@ subject.idv_session.flow_path = 'standard' subject.idv_session.pii_from_doc = Pii::StateId.new(**Idp::Constants::MOCK_IDV_APPLICANT) subject.idv_session.ssn = Idp::Constants::MOCK_IDV_APPLICANT_WITH_PHONE[:ssn] + subject.idv_session.threatmetrix_review_status = threatmetrix_review_status subject.idv_session.threatmetrix_session_id = 'random-session-id' subject.idv_session.resolution_successful = true subject.idv_session.applicant = Idp::Constants::MOCK_IDV_APPLICANT_WITH_PHONE @@ -404,6 +407,19 @@ def show expect(events_count).to eq 1 end + context 'user was flagged for fraud' do + let(:threatmetrix_review_status) { 'reject' } + + it 'sends the user a "please call us" email' do + put :create, params: { user: { password: ControllerHelper::VALID_PASSWORD } } + expect_delivered_email_count(1) + expect_delivered_email( + to: [user.email_addresses.first.email], + subject: t('user_mailer.idv_please_call.subject', app_name: APP_NAME), + ) + end + end + context 'with in person profile' do let!(:enrollment) do create(:in_person_enrollment, :establishing, user: user, profile: nil) @@ -450,6 +466,19 @@ def show put :create, params: { user: { password: ControllerHelper::VALID_PASSWORD } } end + context 'user was flagged for fraud' do + let(:threatmetrix_review_status) { 'reject' } + + it 'does not send the user a "please call us" email' do + # For IPP, this is sent later, after the user goes to the post office. + put :create, params: { user: { password: ControllerHelper::VALID_PASSWORD } } + expect_email_not_delivered( + to: [user.email_addresses.first.email], + subject: t('user_mailer.idv_please_call.subject', app_name: APP_NAME), + ) + end + end + it 'creates an in-person enrollment record' do put :create, params: { user: { password: ControllerHelper::VALID_PASSWORD } } @@ -896,6 +925,19 @@ def show expect(profile).to_not be_active end + context 'user was flagged for fraud' do + let(:threatmetrix_review_status) { 'reject' } + + it 'does not send the user a "please call us" email' do + # For GPO, this is sent after they enter their code + put :create, params: { user: { password: ControllerHelper::VALID_PASSWORD } } + expect_email_not_delivered( + to: [user.email_addresses.first.email], + subject: t('user_mailer.idv_please_call.subject', app_name: APP_NAME), + ) + end + end + it 'sends an email about the gpo letter' do expect do put :create, diff --git a/spec/jobs/get_usps_proofing_results_job_spec.rb b/spec/jobs/get_usps_proofing_results_job_spec.rb index 48282d9c7ad..253e92ace21 100644 --- a/spec/jobs/get_usps_proofing_results_job_spec.rb +++ b/spec/jobs/get_usps_proofing_results_job_spec.rb @@ -1490,7 +1490,7 @@ allow(analytics).to receive( :idv_in_person_usps_proofing_results_job_please_call_email_initiated, ) - allow(user_mailer).to receive(:in_person_please_call).and_return(mail_deliverer) + allow(user_mailer).to receive(:idv_please_call).and_return(mail_deliverer) subject.perform(current_time) end @@ -1545,10 +1545,7 @@ end it 'sends the please call email' do - expect(user_mailer).to have_received(:in_person_please_call).with( - enrollment: enrollment, - visited_location_name: visited_location_name, - ) + expect(user_mailer).to have_received(:idv_please_call) expect(mail_deliverer).to have_received(:deliver_later).with( queue: :intentionally_delayed, wait_until: (enrollment.reload.status_check_completed_at + diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb index 4bf1af6242b..bfb399a2599 100644 --- a/spec/mailers/previews/user_mailer_preview.rb +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -243,11 +243,8 @@ 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_id_ipp, - visited_location_name: in_person_visited_location_name, - ) + def idv_please_call + UserMailer.with(user: user, email_address: email_address_record).idv_please_call end def account_rejected diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 8155d26713a..cabd55129c1 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -799,6 +799,25 @@ def expect_email_body_to_have_help_and_contact_links end end + describe '#idv_please_call' do + let(:mail) do + UserMailer.with(user: user, email_address: email_address).idv_please_call + end + + it_behaves_like 'a system email' + it_behaves_like 'an email that respects user email locale preference' + + it 'renders the idv_please_call template' do + user_mailer = UserMailer.with(user: user, email_address: email_address) + + expect_any_instance_of(ActionMailer::Base).to receive(:mail). + with(hash_including(template_name: 'idv_please_call')). + and_call_original + + user_mailer.idv_please_call.deliver_later + end + end + context 'in person emails' do let(:current_address_matches_id) { false } let!(:enrollment) do @@ -1306,6 +1325,19 @@ def expect_email_body_to_have_help_and_contact_links it_behaves_like 'a system email' it_behaves_like 'an email that respects user email locale preference' + it 'renders the idv_please_call template' do + user_mailer = UserMailer.with(user: user, email_address: email_address) + + expect_any_instance_of(ActionMailer::Base).to receive(:mail). + with(hash_including(template_name: 'idv_please_call')). + and_call_original + + user_mailer.in_person_please_call( + enrollment: enrollment, + visited_location_name: visited_location_name, + ).deliver_later + end + context 'when the keyword argument visited_location_name is missing' do let(:mail) do UserMailer.with(user: user, email_address: email_address).in_person_please_call( diff --git a/spec/support/mailer_helper.rb b/spec/support/mailer_helper.rb index eb264b0adca..9e8c3577bd3 100644 --- a/spec/support/mailer_helper.rb +++ b/spec/support/mailer_helper.rb @@ -37,4 +37,28 @@ def expect_delivered_email(to: nil, subject: nil, body: nil) ERROR expect(email).to_not be(nil), error_message end + + def expect_email_not_delivered(to: nil, subject: nil, body: nil) + email = ActionMailer::Base.deliveries.find do |sent_mail| + next unless to.present? && sent_mail.to == to + next unless subject.present? && sent_mail.subject == subject + if body.present? + delivered_body = sent_mail.text_part.decoded.squish + body.to_a.each do |expected_body| + next unless delivered_body.include?(expected_body) + end + end + true + end + + error_message = <<~ERROR + Should not have found a sent email matching this, but we did: + to: #{to} + subject: #{subject} + body: #{body} + Sent mails: #{ActionMailer::Base.deliveries} + ERROR + + expect(email).to be(nil), error_message + end end