diff --git a/app/controllers/concerns/idv_step_concern.rb b/app/controllers/concerns/idv_step_concern.rb
index a2a9f9695e0..b643c810ff4 100644
--- a/app/controllers/concerns/idv_step_concern.rb
+++ b/app/controllers/concerns/idv_step_concern.rb
@@ -45,6 +45,28 @@ def flow_path
idv_session.flow_path
end
+ def confirm_hybrid_handoff_needed
+ setup_for_redo if params[:redo]
+
+ if idv_session.skip_hybrid_handoff?
+ # We previously skipped hybrid handoff. Keep doing that.
+ idv_session.flow_path = 'standard'
+ end
+
+ if !FeatureManagement.idv_allow_hybrid_flow?
+ # When hybrid flow is unavailable, skip it.
+ # But don't store that we skipped it in idv_session, in case it is back to
+ # available when the user tries to redo document capture.
+ idv_session.flow_path = 'standard'
+ end
+
+ if idv_session.flow_path == 'standard'
+ redirect_to idv_document_capture_url
+ elsif idv_session.flow_path == 'hybrid'
+ redirect_to idv_link_sent_url
+ end
+ end
+
private
def confirm_ssn_step_complete
@@ -96,4 +118,16 @@ def extra_analytics_properties
end
extra
end
+
+ def setup_for_redo
+ idv_session.redo_document_capture = true
+
+ # If we previously skipped hybrid handoff for the user (because they're on a mobile
+ # device with a camera), skip it _again_ here.
+ if idv_session.skip_hybrid_handoff?
+ idv_session.flow_path = 'standard'
+ else
+ idv_session.flow_path = nil
+ end
+ end
end
diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb
index 0c26a3972d9..d060691386b 100644
--- a/app/controllers/idv/hybrid_handoff_controller.rb
+++ b/app/controllers/idv/hybrid_handoff_controller.rb
@@ -192,40 +192,6 @@ def confirm_agreement_step_complete
redirect_to idv_agreement_url
end
- def confirm_hybrid_handoff_needed
- setup_for_redo if params[:redo]
-
- if idv_session.skip_hybrid_handoff?
- # We previously skipped hybrid handoff. Keep doing that.
- idv_session.flow_path = 'standard'
- end
-
- if !FeatureManagement.idv_allow_hybrid_flow?
- # When hybrid flow is unavailable, skip it.
- # But don't store that we skipped it in idv_session, in case it is back to
- # available when the user tries to redo document capture.
- idv_session.flow_path = 'standard'
- end
-
- if idv_session.flow_path == 'standard'
- redirect_to idv_document_capture_url
- elsif idv_session.flow_path == 'hybrid'
- redirect_to idv_link_sent_url
- end
- end
-
- def setup_for_redo
- idv_session.redo_document_capture = true
-
- # If we previously skipped hybrid handoff for the user (because they're on a mobile
- # device with a camera), skip it _again_ here.
- if idv_session.skip_hybrid_handoff?
- idv_session.flow_path = 'standard'
- else
- idv_session.flow_path = nil
- end
- end
-
def formatted_destination_phone
raw_phone = params.require(:doc_auth).permit(:phone)
PhoneFormatter.format(raw_phone, country_code: 'US')
diff --git a/app/controllers/idv/phone_question_controller.rb b/app/controllers/idv/phone_question_controller.rb
new file mode 100644
index 00000000000..2705fc12170
--- /dev/null
+++ b/app/controllers/idv/phone_question_controller.rb
@@ -0,0 +1,21 @@
+module Idv
+ class PhoneQuestionController < ApplicationController
+ include ActionView::Helpers::DateHelper
+ include IdvStepConcern
+ include StepIndicatorConcern
+
+ before_action :confirm_verify_info_step_needed
+ before_action :confirm_agreement_step_complete
+ before_action :confirm_hybrid_handoff_needed, only: :show
+
+ def show
+ @title = t('doc_auth.headings.phone_question')
+ end
+
+ def confirm_agreement_step_complete
+ return if idv_session.idv_consent_given
+
+ redirect_to idv_agreement_url
+ end
+ end
+end
diff --git a/app/views/idv/phone_question/show.html.erb b/app/views/idv/phone_question/show.html.erb
new file mode 100644
index 00000000000..7faff61162b
--- /dev/null
+++ b/app/views/idv/phone_question/show.html.erb
@@ -0,0 +1,32 @@
+<% title @title %>
+
+<% content_for(:pre_flash_content) do %>
+ <%= render StepIndicatorComponent.new(
+ steps: Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS,
+ current_step: :verify_id,
+ locale_scope: 'idv',
+ class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4',
+ ) %>
+<% end %>
+
+<%= render PageHeadingComponent.new.with_content(@title) %>
+
+
+ <%= button_to(
+ '',
+ method: 'get',
+ class: 'usa-button usa-button--wide usa-button--big',
+ ) do %>
+ <%= t('doc_auth.buttons.have_phone') %>
+ <% end %>
+
+
+
+
+ <%= link_to(
+ t('doc_auth.phone_question.do_not_have'),
+ '',
+ ) %>
+
+
+<%= render 'idv/doc_auth/cancel', step: 'phone_question' %>
diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml
index 3045f1eba57..b5f01497c83 100644
--- a/config/locales/doc_auth/en.yml
+++ b/config/locales/doc_auth/en.yml
@@ -8,6 +8,7 @@ en:
buttons:
add_new_photos: Add new photos
continue: Continue
+ have_phone: Yes, I have a phone
take_or_upload_picture_html: 'Take photo or
Upload photo'
take_picture: Take photo
@@ -143,6 +144,7 @@ en:
hybrid_handoff: How would you like to add your ID?
interstitial: We are processing your images
lets_go: How verifying your identity works
+ phone_question: Do you have a phone you can use to take photos of your ID?
review_issues: Check your images and try again
secure_account: Secure your account
ssn: Enter your Social Security number
@@ -230,6 +232,8 @@ en:
- 'Verify by mail: We’ll mail a letter to your home
address. This takes 5 to 10 days.'
welcome: 'You will need your:'
+ phone_question:
+ do_not_have: I don’t have a phone
tips:
document_capture_header_text: 'For best results:'
document_capture_hint: Must be a JPG or PNG
diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml
index 324beeb3615..96d3c2dad44 100644
--- a/config/locales/doc_auth/es.yml
+++ b/config/locales/doc_auth/es.yml
@@ -8,6 +8,7 @@ es:
buttons:
add_new_photos: Añadir nuevas fotos
continue: Continuar
+ have_phone: Sí tengo teléfono
take_or_upload_picture_html: 'Toma una foto o
Sube una foto'
take_picture: Toma una foto
@@ -172,6 +173,8 @@ es:
hybrid_handoff: '¿Cómo desea añadir su documento de identidad?'
interstitial: Estamos procesando sus imágenes
lets_go: Cómo funciona la verificación de su identidad
+ phone_question: ¿Tiene un teléfono con el que pueda tomar fotos de su documento
+ de identidad?
review_issues: Revise sus imágenes e inténtelo de nuevo
secure_account: Asegure su cuenta
ssn: Ingrese su número de Seguro Social
@@ -267,6 +270,8 @@ es:
- 'Verificar por correo: Le enviaremos una carta a su
domicilio. Esto tarda entre 5 y 10 días.'
welcome: 'Necesitará su:'
+ phone_question:
+ do_not_have: No tengo teléfono
tips:
document_capture_header_text: 'Para obtener los mejores resultados:'
document_capture_hint: Debe ser un JPG o PNG
diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml
index cfdf5e88925..3ec6678a271 100644
--- a/config/locales/doc_auth/fr.yml
+++ b/config/locales/doc_auth/fr.yml
@@ -8,6 +8,7 @@ fr:
buttons:
add_new_photos: Ajoutez de nouvelles photos
continue: Continuer
+ have_phone: Oui j’ai un téléphone
take_or_upload_picture_html: 'Prendre une photo
ou Télécharger une photo'
take_picture: Prendre une photo
@@ -178,6 +179,8 @@ fr:
hybrid_handoff: Comment voulez-vous ajouter votre identifiant ?
interstitial: Nous traitons vos images
lets_go: Comment fonctionne la vérification de votre identité
+ phone_question: Avez-vous un téléphone que vous pouvez utiliser pour prendre des
+ photos de votre identifiant?
review_issues: Vérifiez vos images et essayez à nouveau
secure_account: Sécuriser votre compte
ssn: Saisissez votre numéro de sécurité sociale
@@ -276,6 +279,8 @@ fr:
lettre à votre adresse personnelle. Cela prend 5 à 10
jours.'
welcome: 'Vous aurez besoin de votre:'
+ phone_question:
+ do_not_have: Je n’ai pas de téléphone
tips:
document_capture_header_text: 'Pour obtenir les meilleurs résultats:'
document_capture_hint: Doit être un JPG ou PNG
diff --git a/config/routes.rb b/config/routes.rb
index c453446771f..597c7019208 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -350,6 +350,7 @@
put '/phone_confirmation' => 'otp_verification#update', as: :nil
get '/review' => 'review#new'
put '/review' => 'review#create'
+ get '/phone_question' => 'phone_question#show'
get '/session/errors/warning' => 'session_errors#warning'
get '/session/errors/state_id_warning' => 'session_errors#state_id_warning'
get '/phone/errors/timeout' => 'phone_errors#timeout'
diff --git a/spec/controllers/idv/phone_question_controller_spec.rb b/spec/controllers/idv/phone_question_controller_spec.rb
new file mode 100644
index 00000000000..d07a2da4d95
--- /dev/null
+++ b/spec/controllers/idv/phone_question_controller_spec.rb
@@ -0,0 +1,104 @@
+require 'rails_helper'
+
+RSpec.describe Idv::PhoneQuestionController do
+ include IdvHelper
+
+ let(:user) { create(:user) }
+
+ before do
+ stub_sign_in(user)
+ stub_analytics
+ stub_attempts_tracker
+ subject.user_session['idv/doc_auth'] = {}
+ subject.idv_session.idv_consent_given = true
+ end
+
+ describe 'before_actions' do
+ it 'includes authentication before_action' do
+ expect(subject).to have_actions(
+ :before,
+ :confirm_two_factor_authenticated,
+ )
+ end
+
+ it 'includes outage before_action' do
+ expect(subject).to have_actions(
+ :before,
+ :check_for_mail_only_outage,
+ )
+ end
+
+ it 'checks that agreement step is complete' do
+ expect(subject).to have_actions(
+ :before,
+ :confirm_agreement_step_complete,
+ )
+ end
+
+ it 'checks that hybrid_handoff is needed' do
+ expect(subject).to have_actions(
+ :before,
+ :confirm_hybrid_handoff_needed,
+ )
+ end
+ end
+
+ describe '#show' do
+ it 'renders the show template' do
+ get :show
+
+ expect(response).to render_template :show
+ end
+
+ context 'agreement step is not complete' do
+ before do
+ subject.idv_session.idv_consent_given = nil
+ end
+
+ it 'redirects to idv_agreement_url' do
+ get :show
+
+ expect(response).to redirect_to(idv_agreement_url)
+ end
+ end
+
+ context 'hybrid_handoff already visited' do
+ it 'redirects to document_capture in standard flow' do
+ subject.idv_session.flow_path = 'standard'
+
+ get :show
+
+ expect(response).to redirect_to(idv_document_capture_url)
+ end
+
+ it 'redirects to link_sent in hybrid flow' do
+ subject.idv_session.flow_path = 'hybrid'
+
+ get :show
+
+ expect(response).to redirect_to(idv_link_sent_url)
+ end
+ end
+
+ context 'on mobile device' do
+ it 'redirects to hybrid_handoff controller' do
+ subject.idv_session.skip_hybrid_handoff = true
+
+ get :show
+
+ expect(response).to redirect_to(idv_document_capture_url)
+ end
+ end
+
+ context 'hybrid flow is not available' do
+ before do
+ allow(FeatureManagement).to receive(:idv_allow_hybrid_flow?).and_return(false)
+ end
+
+ it 'redirects the user straight to document capture' do
+ get :show
+ expect(response).to redirect_to(idv_document_capture_url)
+ end
+ end
+ end
+end
diff --git a/spec/features/idv/doc_auth/phone_question_spec.rb b/spec/features/idv/doc_auth/phone_question_spec.rb
new file mode 100644
index 00000000000..79d1faf6bd5
--- /dev/null
+++ b/spec/features/idv/doc_auth/phone_question_spec.rb
@@ -0,0 +1,45 @@
+require 'rails_helper'
+
+RSpec.feature 'phone question step', :js do
+ include IdvStepHelper
+ include DocAuthHelper
+
+ let(:fake_analytics) { FakeAnalytics.new }
+
+ before do
+ allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics)
+ sign_in_and_2fa_user
+ complete_doc_auth_steps_before_hybrid_handoff_step
+ visit(idv_phone_question_url)
+ end
+
+ it 'contains phone question header' do
+ expect(page).to have_content(t('doc_auth.headings.phone_question'))
+ end
+
+ it 'contains option to confirm having phone' do
+ expect(page).to have_content(t('doc_auth.buttons.have_phone'))
+ end
+
+ it 'contains option to confirm not having phone' do
+ expect(page).to have_content(t('doc_auth.phone_question.do_not_have'))
+ end
+
+ it 'allows user to cancel identify verification' do
+ click_link t('links.cancel')
+ expect(page).to have_current_path(idv_cancel_path(step: 'phone_question'))
+
+ expect(fake_analytics).to have_logged_event(
+ 'IdV: cancellation visited',
+ hash_including(step: 'phone_question'),
+ )
+
+ click_spinner_button_and_wait t('idv.cancel.actions.account_page')
+
+ expect(current_path).to eq(account_path)
+ expect(fake_analytics).to have_logged_event(
+ 'IdV: cancellation confirmed',
+ hash_including(step: 'phone_question'),
+ )
+ end
+end