Coverage for benefits/enrollment_switchio/views.py: 98%
94 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-08 16:26 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-08 16:26 +0000
1import logging
2from django.http import HttpRequest, JsonResponse
3from django.shortcuts import redirect
4from django.urls import reverse
5from django.views.generic import FormView, View
6import sentry_sdk
8from benefits.enrollment_switchio.models import SwitchioConfig
9from benefits.routes import routes
10from benefits.core import models
11from benefits.core.mixins import EligibleSessionRequiredMixin, AgencySessionRequiredMixin, FlowSessionRequiredMixin
12from benefits.enrollment import analytics, forms
13from benefits.enrollment.enrollment import Status, handle_enrollment_results
14from benefits.enrollment_switchio.enrollment import (
15 enroll,
16 request_registration,
17 get_registration_status,
18 get_latest_active_token_value,
19)
20from benefits.enrollment_switchio.session import Session
22logger = logging.getLogger(__name__)
25class IndexView(AgencySessionRequiredMixin, FlowSessionRequiredMixin, EligibleSessionRequiredMixin, FormView):
26 """View for the enrollment landing page."""
28 template_name = "enrollment_switchio/index.html"
29 form_class = forms.CardTokenizeSuccessForm
31 def get_context_data(self, **kwargs):
32 context = super().get_context_data(**kwargs)
34 request = self.request
35 flow = self.flow
37 tokenize_system_error_form = forms.CardTokenizeFailForm(
38 routes.ENROLLMENT_SYSTEM_ERROR, "form-card-tokenize-fail-system-error"
39 )
40 tokenize_success_form = forms.CardTokenizeSuccessForm(
41 action_url=routes.ENROLLMENT_SWITCHIO_INDEX, auto_id=True, label_suffix=""
42 )
43 context.update(
44 {
45 **flow.enrollment_index_context,
46 "forms": [tokenize_system_error_form, tokenize_success_form],
47 "form_success": tokenize_success_form.id,
48 "form_system_error": tokenize_system_error_form.id,
49 "cta_button": "tokenize_card",
50 "enrollment_method": models.EnrollmentMethods.DIGITAL,
51 "transit_processor": {"name": "Switchio", "website": "https://switchio.com/transport/"},
52 "locale": self._get_locale(request.LANGUAGE_CODE),
53 }
54 )
55 return context
57 def _get_locale(self, django_language_code):
58 """Given a Django language code, return the corresponding locale to use with Switchio's tokenization gateway."""
59 # mapping from Django's I18N LANGUAGE_CODE to Switchio's locales
60 locale = {"en": "en", "es": "es"}.get(django_language_code, "en")
61 return locale
63 def get(self, request: HttpRequest, *args, **kwargs):
64 session = Session(request)
65 switchio_config = self.agency.switchio_config
67 if session.registration_id:
68 response = get_registration_status(switchio_config=switchio_config, registration_id=session.registration_id)
69 if response.status is Status.SUCCESS:
70 reg_state = response.registration_status.regState
71 if reg_state == "tokenization_finished":
72 # give card token to index template so it can send
73 # "finished card tokenization" event and POST either the
74 # CardTokenizeSuccessForm or CardTokenizeFailForm.
75 context_data = self.get_context_data(**kwargs)
77 context_data["card_token"] = get_latest_active_token_value(response.registration_status.tokens)
78 return self.render_to_response(context=context_data)
79 elif reg_state == "verification_failed":
80 return redirect(routes.ENROLLMENT_RETRY)
81 elif reg_state == "tokenization_failed": 81 ↛ 92line 81 didn't jump to line 92 because the condition on line 81 was always true
82 sentry_sdk.capture_exception(Exception("Tokenization failed"))
83 return redirect(routes.ENROLLMENT_SYSTEM_ERROR)
84 else:
85 sentry_sdk.capture_exception(response.exception)
87 if response.status is Status.SYSTEM_ERROR:
88 return redirect(routes.ENROLLMENT_SYSTEM_ERROR)
89 elif response.status is Status.EXCEPTION: 89 ↛ 92line 89 didn't jump to line 92 because the condition on line 89 was always true
90 return redirect(routes.SERVER_ERROR)
92 return super().get(request=request, *args, **kwargs)
94 def form_valid(self, form):
95 switchio_config = self.agency.switchio_config
96 flow = self.flow
97 card_token = form.cleaned_data.get("card_token")
99 status, exception = enroll(switchio_config=switchio_config, flow=flow, token=card_token)
100 return handle_enrollment_results(self.request, status, exception)
103class GatewayUrlView(AgencySessionRequiredMixin, EligibleSessionRequiredMixin, View):
104 """View for the tokenization gateway registration"""
106 def get(self, request: HttpRequest, *args, **kwargs):
107 session = Session(request)
108 switchio_config = self.agency.switchio_config
110 if session.registration_id is None or session.gateway_url is None:
111 return self._request_registration(request, switchio_config, session)
112 else:
113 response = get_registration_status(switchio_config=switchio_config, registration_id=session.registration_id)
115 if response.status is Status.SUCCESS:
116 # if the registration session is no longer valid, request a new registration session.
117 if response.registration_status.regState in ["expired", "deleted"]:
118 return self._request_registration(request, switchio_config, session)
119 else:
120 return self._gateway_url_response(session)
121 else:
122 logger.debug(f"Error occurred while attempting to get registration status for {session.registration_id}")
123 sentry_sdk.capture_exception(response.exception)
124 analytics.failed_pretokenization_request(request, response.status_code)
126 if response.status is Status.SYSTEM_ERROR:
127 redirect = reverse(routes.ENROLLMENT_SYSTEM_ERROR)
128 else:
129 redirect = reverse(routes.SERVER_ERROR)
131 data = {"redirect": redirect}
132 return JsonResponse(data)
134 def _request_registration(self, request: HttpRequest, switchio_config: SwitchioConfig, session: Session) -> JsonResponse:
135 response = request_registration(request, switchio_config)
137 if response.status is Status.SUCCESS:
138 registration = response.registration
139 session.registration_id = registration.regId
140 session.gateway_url = registration.gtwUrl
142 return self._gateway_url_response(session)
143 else:
144 logger.debug("Error occurred while requesting a tokenization gateway registration", exc_info=response.exception)
145 sentry_sdk.capture_exception(response.exception)
146 analytics.failed_pretokenization_request(request, response.status_code)
148 if response.status is Status.SYSTEM_ERROR:
149 redirect = reverse(routes.ENROLLMENT_SYSTEM_ERROR)
150 else:
151 redirect = reverse(routes.SERVER_ERROR)
153 data = {"redirect": redirect}
154 return JsonResponse(data)
156 def _gateway_url_response(self, session: Session):
157 data = {"gateway_url": session.gateway_url}
158 return JsonResponse(data)