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

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 

7 

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 

21 

22logger = logging.getLogger(__name__) 

23 

24 

25class IndexView(AgencySessionRequiredMixin, FlowSessionRequiredMixin, EligibleSessionRequiredMixin, FormView): 

26 """View for the enrollment landing page.""" 

27 

28 template_name = "enrollment_switchio/index.html" 

29 form_class = forms.CardTokenizeSuccessForm 

30 

31 def get_context_data(self, **kwargs): 

32 context = super().get_context_data(**kwargs) 

33 

34 request = self.request 

35 flow = self.flow 

36 

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 

56 

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 

62 

63 def get(self, request: HttpRequest, *args, **kwargs): 

64 session = Session(request) 

65 switchio_config = self.agency.switchio_config 

66 

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) 

76 

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) 

86 

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) 

91 

92 return super().get(request=request, *args, **kwargs) 

93 

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") 

98 

99 status, exception = enroll(switchio_config=switchio_config, flow=flow, token=card_token) 

100 return handle_enrollment_results(self.request, status, exception) 

101 

102 

103class GatewayUrlView(AgencySessionRequiredMixin, EligibleSessionRequiredMixin, View): 

104 """View for the tokenization gateway registration""" 

105 

106 def get(self, request: HttpRequest, *args, **kwargs): 

107 session = Session(request) 

108 switchio_config = self.agency.switchio_config 

109 

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) 

114 

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) 

125 

126 if response.status is Status.SYSTEM_ERROR: 

127 redirect = reverse(routes.ENROLLMENT_SYSTEM_ERROR) 

128 else: 

129 redirect = reverse(routes.SERVER_ERROR) 

130 

131 data = {"redirect": redirect} 

132 return JsonResponse(data) 

133 

134 def _request_registration(self, request: HttpRequest, switchio_config: SwitchioConfig, session: Session) -> JsonResponse: 

135 response = request_registration(request, switchio_config) 

136 

137 if response.status is Status.SUCCESS: 

138 registration = response.registration 

139 session.registration_id = registration.regId 

140 session.gateway_url = registration.gtwUrl 

141 

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) 

147 

148 if response.status is Status.SYSTEM_ERROR: 

149 redirect = reverse(routes.ENROLLMENT_SYSTEM_ERROR) 

150 else: 

151 redirect = reverse(routes.SERVER_ERROR) 

152 

153 data = {"redirect": redirect} 

154 return JsonResponse(data) 

155 

156 def _gateway_url_response(self, session: Session): 

157 data = {"gateway_url": session.gateway_url} 

158 return JsonResponse(data)