diff --git a/app/controllers/idv/agreement_controller.rb b/app/controllers/idv/agreement_controller.rb index 64ced91aba2..2ccd9277a11 100644 --- a/app/controllers/idv/agreement_controller.rb +++ b/app/controllers/idv/agreement_controller.rb @@ -40,7 +40,11 @@ def update if IdentityConfig.store.in_person_proofing_opt_in_enabled && IdentityConfig.store.in_person_proofing_enabled - redirect_to idv_how_to_verify_url + if params[:skip_hybrid_handoff] + redirect_to idv_choose_id_type_url + else + redirect_to idv_how_to_verify_url + end else redirect_to idv_hybrid_handoff_url end @@ -53,7 +57,7 @@ def self.step_info Idv::StepInfo.new( key: :agreement, controller: self, - next_steps: [:hybrid_handoff, :document_capture, :how_to_verify], + next_steps: [:hybrid_handoff, :choose_id_type, :document_capture, :how_to_verify], preconditions: ->(idv_session:, user:) { idv_session.welcome_visited }, undo_step: ->(idv_session:, user:) do idv_session.idv_consent_given_at = nil diff --git a/app/controllers/idv/choose_id_type_controller.rb b/app/controllers/idv/choose_id_type_controller.rb new file mode 100644 index 00000000000..75e668624d8 --- /dev/null +++ b/app/controllers/idv/choose_id_type_controller.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +module Idv + class ChooseIdTypeController < ApplicationController + include Idv::AvailabilityConcern + include IdvStepConcern + include StepIndicatorConcern + + before_action :redirect_if_passport_not_available + + def show + analytics.idv_doc_auth_choose_id_type_visited(**analytics_arguments) + end + + def update + clear_future_steps! + + @choose_id_type_form = Idv::ChooseIdTypeForm.new + + result = @choose_id_type_form.submit(choose_id_type_form_params) + + analytics.idv_doc_auth_choose_id_type_submitted( + **analytics_arguments.merge(result.to_h) + .merge({ chosen_id_type: }), + ) + + if result.success? + set_passport_requested + redirect_to next_step + else + render :show + end + end + + def self.step_info + Idv::StepInfo.new( + key: :choose_id_type, + controller: self, + next_steps: [:document_capture], + preconditions: ->(idv_session:, user:) { + idv_session.flow_path == 'standard' && + idv_session.passport_allowed == true + }, + undo_step: ->(idv_session:, user:) do + idv_session.passport_requested = nil + end, + ) + end + + private + + def redirect_if_passport_not_available + redirect_to idv_how_to_verify_url if !idv_session.passport_allowed + end + + def chosen_id_type + choose_id_type_form_params[:choose_id_type_preference] + end + + def set_passport_requested + if chosen_id_type == 'passport' + idv_session.passport_requested = true + else + idv_session.passport_requested = false + end + end + + def next_step + idv_document_capture_url + end + + def choose_id_type_form_params + params.require(:doc_auth).permit(:choose_id_type_preference) + end + + def analytics_arguments + { + step: 'choose_id_type', + analytics_id: 'Doc Auth', + flow_path: idv_session.flow_path, + } + end + end +end diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index d78224d68ea..37a199b744f 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -58,7 +58,7 @@ def self.step_info Idv::StepInfo.new( key: :hybrid_handoff, controller: self, - next_steps: [:link_sent, :document_capture, :socure_document_capture], + next_steps: [:choose_id_type, :link_sent, :document_capture, :socure_document_capture], preconditions: ->(idv_session:, user:) { idv_session.idv_consent_given? && (self.selected_remote(idv_session: idv_session) || # from opt-in screen @@ -149,7 +149,7 @@ def update_document_capture_session_requested_at(session_uuid) def bypass_send_link_steps idv_session.flow_path = 'standard' - redirect_to vendor_document_capture_url + redirect_to next_step analytics.idv_doc_auth_hybrid_handoff_submitted( **analytics_arguments.merge( @@ -158,6 +158,14 @@ def bypass_send_link_steps ) end + def next_step + if idv_session.passport_allowed + idv_choose_id_type_url + else + idv_document_capture_url + end + end + def extra_view_variables { idv_phone_form: build_form } end diff --git a/app/controllers/idv/welcome_controller.rb b/app/controllers/idv/welcome_controller.rb index d25415fbca5..b9b5badc77d 100644 --- a/app/controllers/idv/welcome_controller.rb +++ b/app/controllers/idv/welcome_controller.rb @@ -11,6 +11,7 @@ class WelcomeController < ApplicationController def show idv_session.proofing_started_at ||= Time.zone.now.iso8601 + idv_session.passport_allowed = IdentityConfig.store.doc_auth_passports_enabled analytics.idv_doc_auth_welcome_visited(**analytics_arguments) Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]) diff --git a/app/forms/idv/choose_id_type_form.rb b/app/forms/idv/choose_id_type_form.rb new file mode 100644 index 00000000000..1b11407e19f --- /dev/null +++ b/app/forms/idv/choose_id_type_form.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Idv + class ChooseIdTypeForm + include ActiveModel::Model + + validate :chosen_id_type_valid? + attr_reader :chosen_id_type + + def initialize(chosen_id_type = nil) + @chosen_id_type = chosen_id_type + end + + def submit(params) + @chosen_id_type = params[:choose_id_type_preference] + + FormResponse.new(success: chosen_id_type_valid?, errors: errors) + end + + def chosen_id_type_valid? + valid_types = ['passport', 'drivers_license'] # Will remove once pasport added to id slugs + return true if valid_types.include? @chosen_id_type + errors.add( + :chosen_id_type, + :invalid, + message: "`choose_id_type` #{@chosen_id_type} is invalid, expected one of #{valid_types}", + ) + false + end + end +end diff --git a/app/policies/idv/flow_policy.rb b/app/policies/idv/flow_policy.rb index a8510c31f09..8385438f1e5 100644 --- a/app/policies/idv/flow_policy.rb +++ b/app/policies/idv/flow_policy.rb @@ -18,6 +18,7 @@ class FlowPolicy agreement: Idv::AgreementController.step_info, how_to_verify: Idv::HowToVerifyController.step_info, hybrid_handoff: Idv::HybridHandoffController.step_info, + choose_id_type: Idv::ChooseIdTypeController.step_info, link_sent: Idv::LinkSentController.step_info, document_capture: Idv::DocumentCaptureController.step_info, socure_document_capture: Idv::Socure::DocumentCaptureController.step_info, diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index 22c963a4e3f..4c071817c36 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -1306,6 +1306,51 @@ def idv_doc_auth_capture_complete_visited( ) end + # @param [Boolean] success + # @param [String] step Current IdV step + # @param [String] analytics_id Current IdV flow identifier + # @param ["hybrid","standard"] flow_path Document capture user flow + # @param ['drivers_license', 'passport'] chosen_id_type Chosen id type of the user + # @param [Hash] error_details + def idv_doc_auth_choose_id_type_submitted( + success:, + step:, + analytics_id:, + flow_path:, + chosen_id_type:, + error_details: nil, + **extra + ) + track_event( + :idv_doc_auth_choose_id_type_submitted, + success:, + step:, + analytics_id:, + flow_path:, + chosen_id_type:, + error_details:, + **extra, + ) + end + + # @param [String] step Current IdV step + # @param [String] analytics_id Current IdV flow identifier + # @param ["hybrid","standard"] flow_path Document capture user flow + def idv_doc_auth_choose_id_type_visited( + step:, + analytics_id:, + flow_path:, + **extra + ) + track_event( + :idv_doc_auth_choose_id_type_visited, + step:, + analytics_id:, + flow_path:, + **extra, + ) + end + # User returns from Socure document capture, but is waiting on a result to be fetched # @param ["hybrid","standard"] flow_path Document capture user flow # @param [String] step Current IdV step diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb index 70435fb86c8..f0707baf90a 100644 --- a/app/services/idv/session.rb +++ b/app/services/idv/session.rb @@ -16,6 +16,8 @@ module Idv # @attr idv_phone_step_document_capture_session_uuid [String, nil] # @attr mail_only_warning_shown [Boolean, nil] # @attr opted_in_to_in_person_proofing [Boolean, nil] + # @attr passport_allowed [Boolean, nil] + # @attr passport_requested [Boolean, nil] # @attr personal_key [String, nil] # @attr personal_key_acknowledged [Boolean, nil] # @attr phone_for_mobile_flow [String, nil] @@ -60,6 +62,8 @@ class Session idv_phone_step_document_capture_session_uuid mail_only_warning_shown opted_in_to_in_person_proofing + passport_allowed + passport_requested personal_key personal_key_acknowledged phone_for_mobile_flow diff --git a/app/views/idv/choose_id_type/show.html.erb b/app/views/idv/choose_id_type/show.html.erb new file mode 100644 index 00000000000..a2e45997035 --- /dev/null +++ b/app/views/idv/choose_id_type/show.html.erb @@ -0,0 +1,51 @@ +<% self.title = t('doc_auth.headings.choose_id_type') %> + +<% 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 do %> + <%= t('doc_auth.headings.choose_id_type') %> +<% end %> + +

+ <%= t('doc_auth.info.choose_id_type') %> +

+ +<%= new_tab_link_to( + t('doc_auth.info.id_types_learn_more'), + help_center_redirect_url( + category: 'verify-your-identity', + article: 'accepted-identification-documents', + ), + ) +%> + +<%= simple_form_for( + :doc_auth, + url: idv_choose_id_type_path, + method: :put, + ) do |f| %> + <%= render ValidatedFieldComponent.new( + as: :radio_buttons, + collection: [ + [t('doc_auth.forms.id_type_preference.drivers_license'), :drivers_license], + [t('doc_auth.forms.id_type_preference.passport'), :passport], + ], + form: f, + input_html: { class: 'usa-radio__input--tile' }, + item_label_class: 'usa-radio__label text-bold width-full margin-y-2', + name: :choose_id_type_preference, + required: true, + wrapper: :uswds_radio_buttons, + error_messages: { valueMissing: t('doc_auth.errors.choose_id_type_check') }, + ) %> + <%= f.submit t('forms.buttons.continue'), class: 'margin-y-2' %> +<% end %> + +<%= render 'idv/doc_auth/cancel', step: 'choose_id_type' %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 1bfcbb18e25..3bef6fc2858 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -531,6 +531,7 @@ doc_auth.errors.camera.blocked: Your camera is blocked doc_auth.errors.camera.blocked_detail_html: 'Allow access to your camera to take photos for %{app_name}.Try taking photos again and allowing permission. If that doesn’t work, you may need to check your device settings to allow access.' doc_auth.errors.camera.failed: Camera failed to start, please try again. doc_auth.errors.card_type: Try again with your driver’s license or state ID card. +doc_auth.errors.choose_id_type_check: Select the type of document that you have doc_auth.errors.consent_form: Before you can continue, you must give us permission. Please check the box below and then click continue. doc_auth.errors.doc_type_not_supported_heading: We only accept a driver’s license or a state ID doc_auth.errors.doc.doc_type_check: Your driver’s license or state ID must be issued by a U.S. state or territory. We do not accept other forms of ID, like passports or military IDs. @@ -581,6 +582,8 @@ doc_auth.errors.upload_error: Sorry, something went wrong on our end. doc_auth.forms.change_file: Change file doc_auth.forms.choose_file_html: Drag file here or choose from folder doc_auth.forms.doc_success: We verified your information +doc_auth.forms.id_type_preference.drivers_license: U.S. driver’s license or state ID +doc_auth.forms.id_type_preference.passport: U.S. passport book doc_auth.forms.selected_file: Selected file doc_auth.headers.expired_id: Your ID may have expired doc_auth.headers.general.network_error: We are having technical difficulties @@ -594,6 +597,7 @@ doc_auth.headings.back: Back of your driver’s license or state ID doc_auth.headings.capture_complete: We verified your identity document doc_auth.headings.capture_scan_warning_html: We couldn’t read the barcode on your ID. If the information below is incorrect, please %{link_html} of your state‑issued ID. doc_auth.headings.capture_scan_warning_link: upload new photos +doc_auth.headings.choose_id_type: Choose your ID type doc_auth.headings.document_capture: Add photos of your driver’s license or state ID card doc_auth.headings.document_capture_back: Back of your ID doc_auth.headings.document_capture_front: Front of your ID @@ -635,6 +639,7 @@ doc_auth.info.capture_status_capturing: Capturing doc_auth.info.capture_status_none: Align doc_auth.info.capture_status_small_document: Move Closer doc_auth.info.capture_status_tap_to_capture: Tap to Capture +doc_auth.info.choose_id_type: Select the type of document that you have. You’ll need to take photos of your ID to verify your identity. doc_auth.info.exit.with_sp: Exit %{app_name} and return to %{sp_name} doc_auth.info.exit.without_sp: Exit identity verification and go to your account page doc_auth.info.getting_started_html: '%{sp_name} needs to make sure you are you — not someone pretending to be you. %{link_html}' @@ -644,6 +649,7 @@ doc_auth.info.how_to_verify_mobile: You have the option to verify your identity doc_auth.info.how_to_verify_troubleshooting_options_header: Want to learn more about how to verify your identity? doc_auth.info.hybrid_handoff: We’ll collect information about you by reading your state‑issued ID. doc_auth.info.hybrid_handoff_ipp_html: 'Don’t have a mobile phone? You can verify your identity at a United States Post Office instead.' +doc_auth.info.id_types_learn_more: Learn more about which ID types you can use doc_auth.info.image_loaded: Image loaded doc_auth.info.image_loading: Image loading doc_auth.info.image_updated: Image updated diff --git a/config/locales/es.yml b/config/locales/es.yml index 8ad0dddbe84..a470950d733 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -542,6 +542,7 @@ doc_auth.errors.camera.blocked: Su cámara está bloqueada doc_auth.errors.camera.blocked_detail_html: 'Permita el acceso a su cámara para tomar las fotografías de %{app_name}.Intente tomar las fotografías de nuevo permitiendo el acceso. Si eso no funciona, tal vez necesite revisar la configuración de su dispositivo para permitir el acceso.' doc_auth.errors.camera.failed: No se pudo activar la cámara; inténtelo de nuevo. doc_auth.errors.card_type: Inténtelo de nuevo con su licencia de conducir o tarjeta de identificación estatal. +doc_auth.errors.choose_id_type_check: Seleccione el tipo de documento que tenga doc_auth.errors.consent_form: Antes de continuar, debe darnos permiso. Marque la casilla a continuación y luego haga clic en continuar. doc_auth.errors.doc_type_not_supported_heading: Solo aceptamos una licencia de conducir o una identificación estatal. doc_auth.errors.doc.doc_type_check: Su licencia de conducir o identificación estatal debe ser emitida por un estado o territorio de los EE. UU. No aceptamos otras formas de identificación, como pasaportes o identificaciones militares. @@ -592,6 +593,8 @@ doc_auth.errors.upload_error: Lo sentimos, algo no funcionó bien. doc_auth.forms.change_file: Cambiar archivo doc_auth.forms.choose_file_html: Arrastrar el archivo aquí o seleccionarlo de la carpeta doc_auth.forms.doc_success: Verificamos su información +doc_auth.forms.id_type_preference.drivers_license: Licencia de conducir de los EE. UU. o identificación estatal +doc_auth.forms.id_type_preference.passport: Pasaporte estadounidense doc_auth.forms.selected_file: Archivo seleccionado doc_auth.headers.expired_id: Su identificación puede estar vencida doc_auth.headers.general.network_error: Estamos teniendo problemas técnicos @@ -605,6 +608,7 @@ doc_auth.headings.back: Reverso de su licencia de conducir o identificación est doc_auth.headings.capture_complete: Verificamos su documento de identidad doc_auth.headings.capture_scan_warning_html: No pudimos leer el código de barras en su identificación. Si la información que aparece a continuación es incorrecta, %{link_html} de su identificación emitida por el estado. doc_auth.headings.capture_scan_warning_link: cargue nuevas fotos +doc_auth.headings.choose_id_type: Elija el tipo de su identificación doc_auth.headings.document_capture: Añade fotos de tu licencia de conducir o credencial de identificación oficial doc_auth.headings.document_capture_back: Reverso de su identificación doc_auth.headings.document_capture_front: Frente de su identificación @@ -646,6 +650,7 @@ doc_auth.info.capture_status_capturing: Capturando doc_auth.info.capture_status_none: Alinear doc_auth.info.capture_status_small_document: Acercar doc_auth.info.capture_status_tap_to_capture: Tocar para capturar +doc_auth.info.choose_id_type: Seleccione el tipo de documento que tenga. Tendrá que tomar fotografías de su identificación para verificar su identidad. doc_auth.info.exit.with_sp: Salir de %{app_name} y volver a %{sp_name} doc_auth.info.exit.without_sp: Salga de la verificación de identidad y vaya a la página de su cuenta doc_auth.info.getting_started_html: '%{sp_name} necesita asegurarse de que se trata de usted y no de alguien que se hace pasar por usted. %{link_html}' @@ -655,6 +660,7 @@ doc_auth.info.how_to_verify_mobile: Tiene la opción de verificar su identidad e doc_auth.info.how_to_verify_troubleshooting_options_header: ¿Desea obtener más información sobre cómo verificar su identidad? doc_auth.info.hybrid_handoff: Recopilaremos información sobre usted leyendo su identificación emitida por el estado. doc_auth.info.hybrid_handoff_ipp_html: '¿No tiene un teléfono móvil? Puede verificar su identidad en una oficina de correos de los Estados Unidos.' +doc_auth.info.id_types_learn_more: Obtenga más información acerca de los tipos de identificación que puede usar doc_auth.info.image_loaded: Imagen cargada doc_auth.info.image_loading: Cargando la imagen doc_auth.info.image_updated: Imagen actualizada diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 7c63337f736..b4a43c9e67e 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -531,6 +531,7 @@ doc_auth.errors.camera.blocked: Votre appareil photo est bloqué doc_auth.errors.camera.blocked_detail_html: 'Autorisez l’accès à votre caméra pour prendre des photos pour %{app_name}.Essayez à nouveau de prendre des photos en autorisant %{app_name} à accéder à votre appareil. Si cela ne marche pas, pensez à vérifier les paramètres de votre appareil en matière d’autorisations d’accès.' doc_auth.errors.camera.failed: L’appareil photo n’a pas réussi à démarrer, veuillez réessayer. doc_auth.errors.card_type: Réessayez avec votre permis de conduire ou carte d’identité d’un État. +doc_auth.errors.choose_id_type_check: Sélectionnez le type de document dont vous disposez doc_auth.errors.consent_form: Avant de pouvoir continuer, vous devez nous donner la permission. Veuillez cocher la case ci-dessous, puis cliquez sur Suite. doc_auth.errors.doc_type_not_supported_heading: Nous n’acceptons que les permis de conduire ou les cartes d’identité délivrées par un État doc_auth.errors.doc.doc_type_check: Votre permis de conduire ou votre carte d’identité doit être délivré par un État ou un territoire des États-Unis. Nous n’acceptons pas d’autres pièces d’identité, comme les passeports ou les cartes d’identité militaires. @@ -581,6 +582,8 @@ doc_auth.errors.upload_error: Désolé, il y a eu un problème de notre côté. doc_auth.forms.change_file: Changer de fichier doc_auth.forms.choose_file_html: Faites glisser le fichier ici ou choisissez dans un dossier doc_auth.forms.doc_success: Nous avons vérifié vos informations +doc_auth.forms.id_type_preference.drivers_license: Permis de conduire ou carte d’identité d’un État +doc_auth.forms.id_type_preference.passport: Passeport américain doc_auth.forms.selected_file: Fichier sélectionné doc_auth.headers.expired_id: Votre pièce d’identité est peut-être périmée doc_auth.headers.general.network_error: Nous rencontrons des difficultés techniques @@ -594,6 +597,7 @@ doc_auth.headings.back: Verso de votre permis de conduire ou de votre carte d’ doc_auth.headings.capture_complete: Nous avons vérifié votre pièce d’identité doc_auth.headings.capture_scan_warning_html: Nous n’avons pas pu lire le code-barres de votre pièce d’identité. Si les informations ci-dessous ne sont pas correctes, veuillez %{link_html} de votre carte d’identité délivrée par l’État. doc_auth.headings.capture_scan_warning_link: télécharger de nouvelles photos +doc_auth.headings.choose_id_type: Choisir le type de votre pièce d’identité doc_auth.headings.document_capture: Ajoutez des photos de votre permis de conduire ou de votre carte d’identité nationale doc_auth.headings.document_capture_back: Verso de votre pièce d’identité doc_auth.headings.document_capture_front: Recto de votre carte d’identité @@ -635,6 +639,7 @@ doc_auth.info.capture_status_capturing: Prise de la photo doc_auth.info.capture_status_none: Alignez doc_auth.info.capture_status_small_document: Approchez-vous doc_auth.info.capture_status_tap_to_capture: Appuyez pour prendre la photo +doc_auth.info.choose_id_type: Sélectionnez le type de document dont vous disposez. Vous devrez prendre des photos de votre pièce d’identité pour confirmer votre identité. doc_auth.info.exit.with_sp: Quitter %{app_name} et revenir à %{sp_name} doc_auth.info.exit.without_sp: Quitter la vérification d’identité et accéder à la page de votre compte doc_auth.info.getting_started_html: '%{sp_name} doit s’assurer qu’il s’agit bien de vous et non de quelqu’un qui se fait passer pour vous. %{link_html}' @@ -644,6 +649,7 @@ doc_auth.info.how_to_verify_mobile: Vous avez la possibilité de confirmer votre doc_auth.info.how_to_verify_troubleshooting_options_header: Vous voulez en savoir plus sur la façon de confirmer votre identité? doc_auth.info.hybrid_handoff: Nous recueillerons des informations vous concernant en lisant votre pièce d’identité délivrée par un État. doc_auth.info.hybrid_handoff_ipp_html: 'Vous n’avez pas de téléphone portable? Vous pouvez confirmer votre identité dans un bureau de poste américain participant.' +doc_auth.info.id_types_learn_more: En savoir plus sur les types de pièces d’identité que vous pouvez utiliser doc_auth.info.image_loaded: Image chargée doc_auth.info.image_loading: Image en cours de chargement doc_auth.info.image_updated: Image mise à jour diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 997492b7744..6f84c42c6fa 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -542,6 +542,7 @@ doc_auth.errors.camera.blocked: 你的镜头被遮住了。 doc_auth.errors.camera.blocked_detail_html: '允许 %{app_name} 进入你的相机来拍照。尝试再次拍照并给予许可。如果还不行,你可能需要检查自己设备的设置来给予许可。' doc_auth.errors.camera.failed: 相机未开启,请再试一次。 doc_auth.errors.card_type: 再用你的驾照或州政府颁发的身份证件试一次。 +doc_auth.errors.choose_id_type_check: 选择你具备的身份证件类型 doc_auth.errors.consent_form: 在你能继续之前,你必须授予我们你的同意。请在下面的框打勾然后点击继续。 doc_auth.errors.doc_type_not_supported_heading: 我们只接受驾照或州政府颁发的 ID。 doc_auth.errors.doc.doc_type_check: 你的驾照或身份证件必须是美国一个州或属地颁发的。我们不接受任何其他形式的身份证件,比如护照和军队身份证件。 @@ -592,6 +593,8 @@ doc_auth.errors.upload_error: 抱歉,我们这边出错了。 doc_auth.forms.change_file: 更改文件 doc_auth.forms.choose_file_html: 将文件拖到此处或者从文件夹中选择。 doc_auth.forms.doc_success: 我们验证了你的信息 +doc_auth.forms.id_type_preference.drivers_license: 美国驾照或州身份证件 +doc_auth.forms.id_type_preference.passport: 美国护照本 doc_auth.forms.selected_file: 被选文件 doc_auth.headers.expired_id: 你的身份证件可能已过期 doc_auth.headers.general.network_error: 我们目前遇到技术困难 @@ -605,6 +608,7 @@ doc_auth.headings.back: 驾照或州政府颁发身份证件的背面。 doc_auth.headings.capture_complete: 我们验证了你的身份文件 doc_auth.headings.capture_scan_warning_html: 我们读取不到你身份证件上的条形码。如果以下信息不正确,请将州政府颁发的身份证件%{link_html}。 doc_auth.headings.capture_scan_warning_link: 上传新照片 +doc_auth.headings.choose_id_type: 选择你的身份证件类型 doc_auth.headings.document_capture: 添加你身份证件的照片 doc_auth.headings.document_capture_back: 你身份证件的背面 doc_auth.headings.document_capture_front: 你身份证件的正面 @@ -646,6 +650,7 @@ doc_auth.info.capture_status_capturing: 扫描中 doc_auth.info.capture_status_none: 对齐 doc_auth.info.capture_status_small_document: 靠近一些 doc_auth.info.capture_status_tap_to_capture: 点击来扫描 +doc_auth.info.choose_id_type: 选择你具备的身份证件类型你将需要拍你身份证件的照片来验证身份。 doc_auth.info.exit.with_sp: 退出 %{app_name} 并返回 %{sp_name} doc_auth.info.exit.without_sp: 退出身份验证并到你的账户页面 doc_auth.info.getting_started_html: '%{sp_name} 需要确保你是你,而不是别人冒充你。 了解更多有关验证你身份的信息 %{link_html}' @@ -655,6 +660,7 @@ doc_auth.info.how_to_verify_mobile: 你可以选择用你的手机在网上验 doc_auth.info.how_to_verify_troubleshooting_options_header: 想对验证身份获得更多了解吗? doc_auth.info.hybrid_handoff: 我们将通过读取州政府颁发给你的身份证件来收集你的信息。 doc_auth.info.hybrid_handoff_ipp_html: '没有手机?你可以去一个美国邮局验证身份。' +doc_auth.info.id_types_learn_more: 了解有关你能使用的身份证件类型的更多信息 doc_auth.info.image_loaded: 图像已加载 doc_auth.info.image_loading: 图像加载中 doc_auth.info.image_updated: 图像已上传 diff --git a/config/routes.rb b/config/routes.rb index 4201dae14e7..31457b2e174 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -391,6 +391,8 @@ get '/hybrid_mobile/socure/document_capture_errors' => 'hybrid_mobile/socure/errors#show', as: :hybrid_mobile_socure_document_capture_errors get '/hybrid_handoff' => 'hybrid_handoff#show' put '/hybrid_handoff' => 'hybrid_handoff#update' + get '/choose_id_type' => 'choose_id_type#show' + put '/choose_id_type' => 'choose_id_type#update' get '/link_sent' => 'link_sent#show' put '/link_sent' => 'link_sent#update' get '/link_sent/poll' => 'link_sent_poll#show' diff --git a/spec/controllers/idv/choose_id_type_controller_spec.rb b/spec/controllers/idv/choose_id_type_controller_spec.rb new file mode 100644 index 00000000000..7f1916055ed --- /dev/null +++ b/spec/controllers/idv/choose_id_type_controller_spec.rb @@ -0,0 +1,133 @@ +require 'rails_helper' + +RSpec.describe Idv::ChooseIdTypeController do + include FlowPolicyHelper + + let(:user) { create(:user) } + + before do + stub_sign_in(user) + stub_up_to(:hybrid_handoff, idv_session: subject.idv_session) + stub_analytics + end + + describe '#step info' do + it 'returns a valid StepInfo object' do + expect(Idv::ChooseIdTypeController.step_info).to be_valid + end + end + + describe 'before actions' do + it 'includes redirect_if_passport_not_available before_action' do + expect(subject).to have_actions( + :before, + :redirect_if_passport_not_available, + ) + end + end + + describe '#show' do + context 'passport is not available' do + it 'redirects to how to verify' do + subject.idv_session.passport_allowed = false + + get :show + + expect(response).to redirect_to(idv_how_to_verify_url) + end + end + + context 'passport is available' do + let(:analytics_name) { :idv_doc_auth_choose_id_type_visited } + let(:analytics_args) do + { + step: 'choose_id_type', + analytics_id: 'Doc Auth', + flow_path: 'standard', + } + end + + it 'renders the show template' do + subject.idv_session.passport_allowed = true + + get :show + + expect(response).to render_template :show + end + + it 'sends analytics_visited event' do + subject.idv_session.passport_allowed = true + + get :show + + expect(@analytics).to have_logged_event(analytics_name, analytics_args) + end + end + end + + describe '#update' do + let(:chosen_id_type) { 'drivers_license' } + let(:analytics_name) { :idv_doc_auth_choose_id_type_submitted } + let(:analytics_args) do + { + success: true, + step: 'choose_id_type', + analytics_id: 'Doc Auth', + flow_path: 'standard', + chosen_id_type: chosen_id_type, + } + end + + let(:params) do + { doc_auth: { choose_id_type_preference: chosen_id_type } } + end + + before do + allow(subject.idv_session).to receive(:passport_allowed).and_return(true) + end + + it 'invalidates future steps' do + expect(subject).to receive(:clear_future_steps!) + + put :update, params: params + end + + it 'sends analytics submitted event for id choice' do + put :update, params: params + + expect(@analytics).to have_logged_event(analytics_name, analytics_args) + end + + context 'user selects drivers license' do + it 'sets idv_session.passport_requested to false' do + put :update, params: params + + expect(subject.idv_session.passport_requested).to eq(false) + end + + it 'redirects to document capture session' do + put :update, params: params + + expect(response).to redirect_to(idv_document_capture_url) + end + end + + context 'user selects passport' do + let(:chosen_id_type) { 'passport' } + + it 'sets idv_session.passport_requested to true' do + put :update, params: params + + expect(subject.idv_session.passport_requested).to eq(true) + end + + # currently we do not have a passport route so it redirects to ipp route + # change when the new passport is added + it 'redirects to passport document capture' do + put :update, params: params + + expect(response).to redirect_to(idv_document_capture_url) + end + end + end +end diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb index 00a45e6f853..3315e00fd6d 100644 --- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb +++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb @@ -359,6 +359,28 @@ expect(@analytics).to have_logged_event(analytics_name, analytics_args) end + + context 'passports are not enabled' do + before do + allow(subject.idv_session).to receive(:passport_allowed).and_return(false) + end + it 'redirects to choose id type url' do + put :update, params: params + + expect(response).to redirect_to(idv_document_capture_url) + end + end + + context 'passports are enabled' do + before do + allow(subject.idv_session).to receive(:passport_allowed).and_return(true) + end + it 'redirects to choose id type url' do + put :update, params: params + + expect(response).to redirect_to(idv_choose_id_type_url) + end + end end end end diff --git a/spec/controllers/idv/welcome_controller_spec.rb b/spec/controllers/idv/welcome_controller_spec.rb index 793e1f6092a..26a66b36c83 100644 --- a/spec/controllers/idv/welcome_controller_spec.rb +++ b/spec/controllers/idv/welcome_controller_spec.rb @@ -100,6 +100,18 @@ expect(subject.idv_session.proofing_started_at).to eq(Time.zone.now.iso8601) end + context 'passports are enabled' do + before do + allow(IdentityConfig.store).to receive(:doc_auth_passports_enabled).and_return(true) + end + + it 'sets passport_allowed in idv session' do + get :show + + expect(subject.idv_session.passport_allowed).to eq(true) + end + end + context 'welcome already visited' do before do subject.idv_session.welcome_visited = true diff --git a/spec/features/idv/doc_auth/choose_id_type_spec.rb b/spec/features/idv/doc_auth/choose_id_type_spec.rb new file mode 100644 index 00000000000..69cff5195ec --- /dev/null +++ b/spec/features/idv/doc_auth/choose_id_type_spec.rb @@ -0,0 +1,38 @@ +require 'rails_helper' + +RSpec.feature 'choose id type step error checking' do + include DocAuthHelper + context 'desktop flow', :js do + before do + allow(IdentityConfig.store).to receive(:doc_auth_passports_enabled).and_return(true) + sign_in_and_2fa_user + complete_doc_auth_steps_before_hybrid_handoff_step + end + + it 'shows choose id type screen and continues after passport option' do + expect(page).to have_content(t('doc_auth.headings.upload_from_computer')) + click_on t('forms.buttons.upload_photos') + expect(page).to have_current_path(idv_choose_id_type_url) + choose(t('doc_auth.forms.id_type_preference.passport')) + click_on t('forms.buttons.continue') + expect(page).to have_current_path(idv_document_capture_url) + end + end + context 'mobile flow', :js, driver: :headless_chrome_mobile do + before do + allow(IdentityConfig.store).to receive(:doc_auth_passports_enabled).and_return(true) + allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true) + allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled).and_return(true) + sign_in_and_2fa_user + complete_doc_auth_steps_before_agreement_step + complete_agreement_step + end + + it 'shows choose id type screen and continues after drivers license option' do + expect(page).to have_current_path(idv_choose_id_type_url) + choose(t('doc_auth.forms.id_type_preference.drivers_license')) + click_on t('forms.buttons.continue') + expect(page).to have_current_path(idv_document_capture_url) + end + end +end diff --git a/spec/forms/idv/choose_id_type_form_spec.rb b/spec/forms/idv/choose_id_type_form_spec.rb new file mode 100644 index 00000000000..746b618673f --- /dev/null +++ b/spec/forms/idv/choose_id_type_form_spec.rb @@ -0,0 +1,39 @@ +require 'rails_helper' + +RSpec.describe Idv::ChooseIdTypeForm do + let(:subject) { Idv::ChooseIdTypeForm.new } + + describe '#submit' do + context 'when the form is valid' do + let(:params) { { choose_id_type_preference: 'passport' } } + + it 'returns a successful form response' do + result = subject.submit(params) + + expect(result).to be_kind_of(FormResponse) + expect(result.success?).to eq(true) + expect(result.errors).to be_empty + end + end + context 'when the choose_id_type_preference is nil' do + let(:params) { { choose_id_type_preference: nil } } + it 'returns a failed form response when id type is nil' do + result = subject.submit(params) + + expect(result).to be_kind_of(FormResponse) + expect(result.success?).to eq(false) + expect(result.errors).not_to be_empty + end + end + context 'when the choose_id_type_preference is not supported type' do + let(:params) { { choose_id_type_preference: 'unknown-type' } } + it 'returns a failed form response when id type is nil' do + result = subject.submit(params) + + expect(result).to be_kind_of(FormResponse) + expect(result.success?).to eq(false) + expect(result.errors).not_to be_empty + end + end + end +end diff --git a/spec/support/flow_policy_helper.rb b/spec/support/flow_policy_helper.rb index e99ad069d12..9c63c52c7e6 100644 --- a/spec/support/flow_policy_helper.rb +++ b/spec/support/flow_policy_helper.rb @@ -17,6 +17,9 @@ def stub_step(key:, idv_session:) idv_session.skip_doc_auth_from_how_to_verify = false when :hybrid_handoff idv_session.flow_path = 'standard' + when :choose_id_type + idv_session.flow_path = 'standard' + idv_session.passport_allowed == true when :link_sent idv_session.flow_path = 'hybrid' idv_session.pii_from_doc = Pii::StateId.new(**Idp::Constants::MOCK_IDV_APPLICANT) @@ -69,6 +72,8 @@ def keys_up_to(key:) %i[welcome agreement how_to_verify] when :hybrid_handoff %i[welcome agreement how_to_verify hybrid_handoff] + when :choose_id_type + %i[welcome agreement how_to_verify hybrid_handoff choose_id_type] when :link_sent %i[welcome agreement how_to_verify hybrid_handoff link_sent] when :document_capture