diff --git a/app/assets/images/email/user-signup-ial2.png b/app/assets/images/email/user-signup-ial2.png
new file mode 100644
index 00000000000..9d8521fc12e
Binary files /dev/null and b/app/assets/images/email/user-signup-ial2.png differ
diff --git a/app/assets/stylesheets/email.css.scss b/app/assets/stylesheets/email.css.scss
index 0c13b1ef748..b9e1286126f 100644
--- a/app/assets/stylesheets/email.css.scss
+++ b/app/assets/stylesheets/email.css.scss
@@ -32,6 +32,11 @@
line-height: 10px;
}
+.s16 {
+ font-size: 16px;
+ line-height: 24px;
+}
+
.s30 {
font-size: 30px;
line-height: 30px;
diff --git a/app/jobs/get_usps_proofing_results_job.rb b/app/jobs/get_usps_proofing_results_job.rb
index 9f585b6f0de..8b3b78bd7dd 100644
--- a/app/jobs/get_usps_proofing_results_job.rb
+++ b/app/jobs/get_usps_proofing_results_job.rb
@@ -143,6 +143,7 @@ def update_enrollment_status(enrollment, response)
enrollment.profile.activate
enrollment.update(status: :passed)
handle_successful_status_update(enrollment)
+ send_verified_email(enrollment.user, enrollment)
else
# Unsupported ID type
enrollment.update(status: :failed)
@@ -151,8 +152,29 @@ def update_enrollment_status(enrollment, response)
when IPP_STATUS_FAILED
enrollment.update(status: :failed)
handle_failed_status(enrollment, response)
+ send_failed_email(enrollment.user, enrollment)
else
handle_unsupported_status(enrollment, response['status'])
end
end
+
+ def send_verified_email(user, enrollment)
+ user.confirmed_email_addresses.each do |email_address|
+ UserMailer.in_person_verified(
+ user,
+ email_address,
+ enrollment: enrollment,
+ ).deliver_now_or_later
+ end
+ end
+
+ def send_failed_email(user, enrollment)
+ user.confirmed_email_addresses.each do |email_address|
+ UserMailer.in_person_failed(
+ user,
+ email_address,
+ enrollment: enrollment,
+ ).deliver_now_or_later
+ end
+ end
end
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index 2831a2639f7..ccb2bef93d6 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -236,6 +236,27 @@ def in_person_ready_to_verify(user, email_address, first_name:, enrollment:)
end
end
+ def in_person_verified(user, email_address, enrollment:)
+ with_user_locale(user) do
+ @hide_title = true
+ @presenter = Idv::InPerson::VerificationResultsEmailPresenter.new(enrollment: enrollment)
+ mail(
+ to: email_address.email,
+ subject: t('user_mailer.in_person_verified.subject', app_name: APP_NAME),
+ )
+ end
+ end
+
+ def in_person_failed(user, email_address, enrollment:)
+ with_user_locale(user) do
+ @presenter = Idv::InPerson::VerificationResultsEmailPresenter.new(enrollment: enrollment)
+ mail(
+ to: email_address.email,
+ subject: t('user_mailer.in_person_failed.subject', app_name: APP_NAME),
+ )
+ end
+ end
+
private
def email_should_receive_nonessential_notifications?(email)
diff --git a/app/presenters/idv/in_person/verification_results_email_presenter.rb b/app/presenters/idv/in_person/verification_results_email_presenter.rb
new file mode 100644
index 00000000000..d8970ec9388
--- /dev/null
+++ b/app/presenters/idv/in_person/verification_results_email_presenter.rb
@@ -0,0 +1,22 @@
+module Idv
+ module InPerson
+ class VerificationResultsEmailPresenter
+ # update to user's time zone when out of pilot
+ USPS_SERVER_TIMEZONE = ActiveSupport::TimeZone['America/New_York']
+
+ def initialize(enrollment:)
+ @enrollment = enrollment
+ end
+
+ def location_name
+ @enrollment.selected_location_details['name']
+ end
+
+ def formatted_verified_date
+ @enrollment.status_updated_at.in_time_zone(USPS_SERVER_TIMEZONE).strftime(
+ I18n.t('time.formats.event_date'),
+ )
+ end
+ end
+ end
+end
diff --git a/app/views/layouts/user_mailer.html.erb b/app/views/layouts/user_mailer.html.erb
index f695ac2cfe6..243a7b28278 100644
--- a/app/views/layouts/user_mailer.html.erb
+++ b/app/views/layouts/user_mailer.html.erb
@@ -71,8 +71,11 @@
-
<%= @header || message.subject %>
-
<%= yield %>
+ <% unless @hide_title %>
+ <%= @header || message.subject %>
+
+ <% end %>
+ <%= yield %>
diff --git a/app/views/shared/_in-person-verification-results-email-lower.html.erb b/app/views/shared/_in-person-verification-results-email-lower.html.erb
new file mode 100644
index 00000000000..3ebe6dca242
--- /dev/null
+++ b/app/views/shared/_in-person-verification-results-email-lower.html.erb
@@ -0,0 +1,38 @@
+
+
+
+ <%= link_to idv_url, idv_url, target: '_blank', rel: 'noopener' %>
+
+
+
+
+ <%= t(
+ 'user_mailer.in_person_verified.warning_contact_us_html',
+ contact_us_url: MarketingSite.contact_url,
+ sign_in_url: idv_url,
+ )
+ %>
+
diff --git a/app/views/user_mailer/in_person_failed.html.erb b/app/views/user_mailer/in_person_failed.html.erb
new file mode 100644
index 00000000000..f34e8718abb
--- /dev/null
+++ b/app/views/user_mailer/in_person_failed.html.erb
@@ -0,0 +1,20 @@
+
+ <%= t('user_mailer.in_person_verified.greeting') %>
+
+ <%= t(
+ 'user_mailer.in_person_failed.intro',
+ location: @presenter.location_name,
+ date: @presenter.formatted_verified_date,
+ ) %>
+
+
+ <%= t('user_mailer.in_person_failed.body', app_name: APP_NAME) %>
+
+
<%= t('user_mailer.in_person_failed.verifying_identity') %>
+
+ - <%= t('user_mailer.in_person_failed.verifying_step_not_expired') %>
+ - <%= t('user_mailer.in_person_failed.verifying_step_proof_of_address') %>
+
+
+
+<%= render 'shared/in-person-verification-results-email-lower' %>
diff --git a/app/views/user_mailer/in_person_verified.html.erb b/app/views/user_mailer/in_person_verified.html.erb
new file mode 100644
index 00000000000..8ffd02a9cce
--- /dev/null
+++ b/app/views/user_mailer/in_person_verified.html.erb
@@ -0,0 +1,19 @@
+<%= image_tag(
+ 'email/user-signup-ial2.png',
+ width: 140,
+ height: 177,
+ alt: '',
+ class: 'float-center padding-bottom-4',
+ ) %>
+<%= message.subject %>
+
+ <%= t('user_mailer.in_person_verified.greeting') %>
+ <%= t(
+ 'user_mailer.in_person_verified.intro',
+ location: @presenter.location_name,
+ date: @presenter.formatted_verified_date,
+ ) %>
+ <%= t('user_mailer.in_person_verified.next_sign_in', app_name: APP_NAME) %>
+
+
+<%= render 'shared/in-person-verification-results-email-lower' %>
diff --git a/config/locales/user_mailer/en.yml b/config/locales/user_mailer/en.yml
index 587cc7c4aa0..634f1ad81aa 100644
--- a/config/locales/user_mailer/en.yml
+++ b/config/locales/user_mailer/en.yml
@@ -100,11 +100,35 @@ en:
%{app_name} %{help_link} or %{contact_link}.
subject: Email address deleted
help_link_text: Help Center
+ in_person_failed:
+ body: Click the button or copy the link below to try verifying your identity
+ online again through %{app_name}. If you are still experiencing issues,
+ please contact the agency you are trying to access.
+ intro: Your identity could not be verified at the %{location} Post Office on
+ %{date}.
+ subject: Your identity could not be verified in person
+ verifying_identity: 'When verifying your identity:'
+ 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.'
+ verifying_step_proof_of_address: 'If you try to verify your identity in person
+ again, you need to bring a valid proof of address if your current
+ address is different than the address on your ID.'
in_person_ready_to_verify:
greeting: Hi %{name},
intro: Here are the details to verify your identity in person at a United States
Post Office near you.
subject: You’re ready to verify your identity with %{app_name} in person
+ in_person_verified:
+ greeting: Hello,
+ intro: You successfully verified your identity at the %{location} Post Office on
+ %{date}.
+ next_sign_in: Next, click the button or copy the link below to sign in to %{app_name}.
+ sign_in: Sign in
+ subject: You successfully verified your identity with %{app_name}
+ warning_contact_us_html: If you did not attempt to verify your identity in
+ person, please contact us and sign in to change your password.
letter_reminder:
info_html: The letter you are about to receive will contain a confirmation code
that helps us verify your address. You can complete the identity
diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml
index 7ac1e5820c8..9f0634ed76a 100644
--- a/config/locales/user_mailer/es.yml
+++ b/config/locales/user_mailer/es.yml
@@ -106,11 +106,38 @@ es:
%{app_name} %{help_link} o el %{contact_link}.
subject: Dirección de correo electrónico eliminada
help_link_text: Centro de Ayuda
+ in_person_failed:
+ body: Haga clic en el botón o copie el enlace siguiente para volver a intentar
+ verificar su identidad en línea con %{app_name}. Si sigue teniendo
+ problemas, póngase en contacto con la agencia a la que intenta acceder.
+ intro: El %{date}, no se pudo verificar su identidad en la oficina de correos de
+ %{location}.
+ subject: No se pudo verificar su identidad en persona
+ verifying_identity: 'Al verificar su identidad:'
+ verifying_step_not_expired: Su documento de identidad o permiso de conducir
+ emitido por el estado debe estar vigente. Por el momento, no aceptamos
+ otras formas de identificación, como pasaportes o cartillas militares.
+ verifying_step_proof_of_address: Si vuelve a intentar verificar su identidad en
+ persona, deberá llevar un comprobante de domicilio vigente si su
+ dirección actual es distinta de la que aparece en su documento de
+ identidad.
in_person_ready_to_verify:
greeting: 'Hola, %{name}:'
intro: Estos son los detalles para verificar su identidad en persona en una
oficina de correos de los Estados Unidos cercana a usted.
subject: Está listo para verificar su identidad con %{app_name} en persona
+ in_person_verified:
+ greeting: Hola,
+ intro: El %{date}, verificó correctamente su identidad en la oficina de correos
+ de %{location}.
+ next_sign_in: Luego, haga clic en el botón o copie el enlace que aparece a
+ continuación para iniciar sesión en %{app_name}.
+ sign_in: Iniciar sesión
+ subject: Verificó correctamente su identidad con %{app_name}
+ warning_contact_us_html: Si usted no intentó verificar su identidad en persona,
+ por favor, póngase en contacto con
+ nosotros e inicie sesión para cambiar
+ su contraseña.
letter_reminder:
info_html: La carta que está a punto de recibir contendrá un código de
confirmación que nos ayudará a verificar su dirección. Puede completar
diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml
index f1a56ba36e1..1e72e5204ce 100644
--- a/config/locales/user_mailer/fr.yml
+++ b/config/locales/user_mailer/fr.yml
@@ -109,11 +109,40 @@ fr:
veuillez visiter le %{help_link} de %{app_name} ou %{contact_link}.
subject: Adresse email supprimée
help_link_text: Centre d’aide
+ in_person_failed:
+ body: Cliquez sur le bouton ou copiez le lien ci-dessous pour essayer de
+ vérifier à nouveau votre identité en ligne par le biais de %{app_name}.
+ Si vous rencontrez toujours des problèmes, veuillez contacter l’agence à
+ laquelle vous essayez d’accéder.
+ intro: Votre identité n’a pas pu être vérifiée au bureau de poste de %{location}
+ le %{date}.
+ subject: Votre identité n’a pas pu être vérifiée en personne
+ verifying_identity: 'Lors de la vérification de votre identité :'
+ verifying_step_not_expired: Votre carte d’identité ou votre permis de conduire
+ délivré par l’État ne doit pas être périmé. Nous n’acceptons
+ actuellement aucune autre forme d’identification, comme les passeports
+ et les cartes d’identité militaires.
+ verifying_step_proof_of_address: Si vous tentez à nouveau de vérifier votre
+ identité en personne, vous devez apporter un justificatif de domicile
+ valable si votre adresse actuelle est différente de celle figurant sur
+ votre pièce d’identité.
in_person_ready_to_verify:
greeting: Bonjour %{name},
intro: Voici les détails pour vérifier votre identité en personne dans un bureau
de poste des États-Unis près de chez vous.
subject: Vous êtes prêt à vérifier votre identité avec %{app_name} en personne
+ in_person_verified:
+ greeting: Bonjour,
+ intro: Vous avez vérifié avec succès votre identité au bureau de poste de
+ %{location} le %{date}.
+ next_sign_in: Ensuite, cliquez sur le bouton ou copiez le lien ci-dessous pour
+ vous connecter à %{app_name}.
+ sign_in: Se connecter
+ subject: Vous avez vérifié avec succès votre identité avec %{app_name}
+ warning_contact_us_html: Si vous n’avez pas essayé de vérifier votre identité en
+ personne, veuillez nous contacter et
+ vous connecter pour changer votre mot de
+ passe.
letter_reminder:
info_html: La lettre que vous êtes sur le point de recevoir contiendra un code
de confirmation nous permettant de vérifier votre adresse. Vous pouvez
diff --git a/spec/jobs/get_usps_proofing_results_job_spec.rb b/spec/jobs/get_usps_proofing_results_job_spec.rb
index 9ff78d25b28..5500a41e826 100644
--- a/spec/jobs/get_usps_proofing_results_job_spec.rb
+++ b/spec/jobs/get_usps_proofing_results_job_spec.rb
@@ -17,16 +17,36 @@
let!(:passed_enrollment) { create(:in_person_enrollment, :passed) }
let!(:pending_enrollment) do
- create(:in_person_enrollment, status: :pending, enrollment_code: SecureRandom.hex(16))
+ create(
+ :in_person_enrollment,
+ status: :pending,
+ enrollment_code: SecureRandom.hex(16),
+ selected_location_details: { name: 'FRIENDSHIP' },
+ )
end
let!(:pending_enrollment_2) do
- create(:in_person_enrollment, status: :pending, enrollment_code: SecureRandom.hex(16))
+ create(
+ :in_person_enrollment,
+ status: :pending,
+ enrollment_code: SecureRandom.hex(16),
+ selected_location_details: { name: 'BALTIMORE' },
+ )
end
let!(:pending_enrollment_3) do
- create(:in_person_enrollment, status: :pending, enrollment_code: SecureRandom.hex(16))
+ create(
+ :in_person_enrollment,
+ status: :pending,
+ enrollment_code: SecureRandom.hex(16),
+ selected_location_details: { name: 'WASHINGTON' },
+ )
end
let!(:pending_enrollment_4) do
- create(:in_person_enrollment, status: :pending, enrollment_code: SecureRandom.hex(16))
+ create(
+ :in_person_enrollment,
+ status: :pending,
+ enrollment_code: SecureRandom.hex(16),
+ selected_location_details: { name: 'ARLINGTON' },
+ )
end
let(:pending_enrollments) do
[
@@ -93,7 +113,7 @@
)
end
- it 'logs details about failed requests' do
+ it 'logs details about a failed proofing' do
stub_request_token
stub_request_failed_proofing_results
@@ -121,7 +141,29 @@
)
end
- it 'updates enrollment records and activates profiles on 2xx responses with valid JSON' do
+ it 'sends proofing failed email on response with failed status' do
+ stub_request_token
+ stub_request_failed_proofing_results
+
+ allow(InPersonEnrollment).to receive(:needs_usps_status_check).
+ and_return([pending_enrollment])
+
+ mailer = instance_double(ActionMailer::MessageDelivery, deliver_now_or_later: true)
+ user = pending_enrollment.user
+ user.email_addresses.each do |email_address|
+ expect(UserMailer).to receive(:in_person_failed).
+ with(
+ user,
+ email_address,
+ enrollment: instance_of(InPersonEnrollment),
+ ).
+ and_return(mailer)
+ end
+
+ job.perform(Time.zone.now)
+ end
+
+ it 'updates enrollment records and activates profiles on response with passed status' do
stub_request_token
stub_request_passed_proofing_results
@@ -148,6 +190,28 @@
end
end
+ it 'sends verifed email on 2xx responses with valid JSON' do
+ stub_request_token
+ stub_request_passed_proofing_results
+
+ allow(InPersonEnrollment).to receive(:needs_usps_status_check).
+ and_return([pending_enrollment])
+
+ mailer = instance_double(ActionMailer::MessageDelivery, deliver_now_or_later: true)
+ user = pending_enrollment.user
+ user.email_addresses.each do |email_address|
+ expect(UserMailer).to receive(:in_person_verified).
+ with(
+ user,
+ email_address,
+ enrollment: instance_of(InPersonEnrollment),
+ ).
+ and_return(mailer)
+ end
+
+ job.perform(Time.zone.now)
+ end
+
it 'receives a non-hash value' do
stub_request_token
stub_request_proofing_results_with_responses({})
diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb
index 0b1dd186ba0..d4535666b41 100644
--- a/spec/mailers/previews/user_mailer_preview.rb
+++ b/spec/mailers/previews/user_mailer_preview.rb
@@ -143,6 +143,22 @@ def in_person_ready_to_verify
)
end
+ def in_person_verified
+ UserMailer.in_person_verified(
+ user,
+ email_address_record,
+ enrollment: in_person_enrollment,
+ )
+ end
+
+ def in_person_failed
+ UserMailer.in_person_failed(
+ user,
+ email_address_record,
+ enrollment: in_person_enrollment,
+ )
+ end
+
private
def user
@@ -163,7 +179,8 @@ def in_person_enrollment
user: user,
profile: unsaveable(Profile.new(user: user)),
enrollment_code: '2048702198804358',
- created_at: Time.zone.now,
+ created_at: Time.zone.now - 2.hours,
+ status_updated_at: Time.zone.now - 1.hour,
current_address_matches_id: true,
selected_location_details: {
'name' => 'BALTIMORE',
diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb
index 878879d46c9..feb9d2188fe 100644
--- a/spec/mailers/user_mailer_spec.rb
+++ b/spec/mailers/user_mailer_spec.rb
@@ -520,6 +520,72 @@ def expect_email_body_to_have_help_and_contact_links
end
end
+ describe '#in_person_ready_to_verify' do
+ let(:first_name) { 'Michael' }
+ let!(:enrollment) {
+ create(
+ :in_person_enrollment,
+ :pending,
+ selected_location_details: { name: 'FRIENDSHIP' },
+ status_updated_at: Time.zone.now - 2.hours,
+ )
+ }
+
+ let(:mail) do
+ UserMailer.in_person_ready_to_verify(
+ user,
+ user.email_addresses.first,
+ first_name: first_name,
+ enrollment: enrollment,
+ )
+ end
+
+ it_behaves_like 'a system email'
+ it_behaves_like 'an email that respects user email locale preference'
+ end
+
+ describe '#in_person_verified' do
+ let(:enrollment) {
+ create(
+ :in_person_enrollment,
+ selected_location_details: { name: 'FRIENDSHIP' },
+ status_updated_at: Time.zone.now - 2.hours,
+ )
+ }
+
+ let(:mail) do
+ UserMailer.in_person_verified(
+ user,
+ user.email_addresses.first,
+ enrollment: enrollment,
+ )
+ end
+
+ it_behaves_like 'a system email'
+ it_behaves_like 'an email that respects user email locale preference'
+ end
+
+ describe '#in_person_failed' do
+ let(:enrollment) {
+ create(
+ :in_person_enrollment,
+ selected_location_details: { name: 'FRIENDSHIP' },
+ status_updated_at: Time.zone.now - 2.hours,
+ )
+ }
+
+ let(:mail) do
+ UserMailer.in_person_failed(
+ user,
+ user.email_addresses.first,
+ enrollment: enrollment,
+ )
+ end
+
+ it_behaves_like 'a system email'
+ it_behaves_like 'an email that respects user email locale preference'
+ end
+
def strip_tags(str)
ActionController::Base.helpers.strip_tags(str)
end
diff --git a/spec/presenters/idv/in_person/verification_results_email_presenter_spec.rb b/spec/presenters/idv/in_person/verification_results_email_presenter_spec.rb
new file mode 100644
index 00000000000..8d05000ea47
--- /dev/null
+++ b/spec/presenters/idv/in_person/verification_results_email_presenter_spec.rb
@@ -0,0 +1,32 @@
+require 'rails_helper'
+
+RSpec.describe Idv::InPerson::VerificationResultsEmailPresenter do
+ let(:location_name) { 'FRIENDSHIP' }
+ let(:status_updated_at) { described_class::USPS_SERVER_TIMEZONE.parse('2022-07-14T00:00:00Z') }
+ let!(:enrollment) do
+ create(
+ :in_person_enrollment,
+ :pending,
+ selected_location_details: { name: location_name },
+ )
+ end
+
+ subject(:presenter) { described_class.new(enrollment: enrollment) }
+
+ describe '#location_name' do
+ it 'returns the enrollment location name' do
+ expect(presenter.location_name).to eq(location_name)
+ end
+ end
+
+ describe '#formatted_verified_date' do
+ around do |example|
+ Time.use_zone('UTC') { example.run }
+ end
+
+ it 'returns a formatted verified date' do
+ enrollment.update(status_updated_at: status_updated_at)
+ expect(presenter.formatted_verified_date).to eq 'July 13, 2022'
+ end
+ end
+end