Coverage for benefits/enrollment_littlepay/views.py: 90%
72 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-06 20:07 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-06 20:07 +0000
1import json
2import logging
4from django.conf import settings
5from django.http import JsonResponse
6from django.urls import reverse
7from django.views.generic import FormView, View
8import sentry_sdk
10from benefits.routes import routes
11from benefits.core import models, session
12from benefits.core.mixins import EligibleSessionRequiredMixin
14from benefits.enrollment import analytics, forms
15from benefits.enrollment.enrollment import Status, handle_enrollment_results
16from benefits.enrollment_littlepay.enrollment import enroll, request_card_tokenization_access
17from benefits.enrollment_littlepay.session import Session
19logger = logging.getLogger(__name__)
22class TokenView(EligibleSessionRequiredMixin, View):
23 """View handler for the card tokenization access token."""
25 def get(self, request, *args, **kwargs):
26 session = Session(request)
28 if not session.access_token_valid():
29 response = request_card_tokenization_access(request)
31 if response.status is Status.SUCCESS:
32 session.access_token = response.access_token
33 session.access_token_expiry = response.expires_at
34 elif response.status is Status.SYSTEM_ERROR or response.status is Status.EXCEPTION: 34 ↛ 47line 34 didn't jump to line 47 because the condition on line 34 was always true
35 logger.debug("Error occurred while requesting access token", exc_info=response.exception)
36 sentry_sdk.capture_exception(response.exception)
37 analytics.failed_access_token_request(request, response.status_code)
39 if response.status is Status.SYSTEM_ERROR:
40 redirect = reverse(routes.ENROLLMENT_SYSTEM_ERROR)
41 else:
42 redirect = reverse(routes.SERVER_ERROR)
44 data = {"redirect": redirect}
45 return JsonResponse(data)
47 data = {"token": session.access_token}
48 return JsonResponse(data)
51class IndexView(EligibleSessionRequiredMixin, FormView):
52 template_name = "enrollment_littlepay/index.html"
53 form_class = forms.CardTokenizeSuccessForm
55 def get_context_data(self, **kwargs):
56 request = self.request
57 agency = session.agency(request)
58 flow = session.flow(request)
60 tokenize_retry_form = forms.CardTokenizeFailForm(routes.ENROLLMENT_RETRY, "form-card-tokenize-fail-retry")
61 tokenize_server_error_form = forms.CardTokenizeFailForm(routes.SERVER_ERROR, "form-card-tokenize-fail-server-error")
62 tokenize_system_error_form = forms.CardTokenizeFailForm(
63 routes.ENROLLMENT_SYSTEM_ERROR, "form-card-tokenize-fail-system-error"
64 )
65 tokenize_success_form = forms.CardTokenizeSuccessForm(
66 action_url=routes.ENROLLMENT_LITTLEPAY_INDEX, auto_id=True, label_suffix=""
67 )
69 card_types = ["visa", "mastercard"]
70 if settings.LITTLEPAY_ADDITIONAL_CARDTYPES:
71 card_types.extend(["discover", "amex"])
73 context = {
74 "forms": [tokenize_retry_form, tokenize_server_error_form, tokenize_system_error_form, tokenize_success_form],
75 "cta_button": "tokenize_card",
76 "enrollment_method": models.EnrollmentMethods.DIGITAL,
77 "token_field": "card_token",
78 "form_retry": tokenize_retry_form.id,
79 "form_server_error": tokenize_server_error_form.id,
80 "form_success": tokenize_success_form.id,
81 "form_system_error": tokenize_system_error_form.id,
82 "overlay_language": self._get_overlay_language(request.LANGUAGE_CODE),
83 # convert the python list to a JSON string for use in JavaScript
84 "card_types": json.dumps(card_types),
85 }
87 enrollment_index_context_dict = flow.enrollment_index_context
89 match agency.littlepay_config.environment:
90 case models.Environment.QA.value: 90 ↛ 93line 90 didn't jump to line 93 because the pattern on line 90 always matched
91 url = "https://verify.qa.littlepay.com/assets/js/littlepay.min.js"
92 card_tokenize_env = "https://verify.qa.littlepay.com"
93 case models.Environment.PROD.value:
94 url = "https://verify.littlepay.com/assets/js/littlepay.min.js"
95 card_tokenize_env = "https://verify.littlepay.com"
96 case _:
97 raise ValueError("Unrecognized environment value")
99 transit_processor_context = dict(
100 name="Littlepay", website="https://littlepay.com", card_tokenize_url=url, card_tokenize_env=card_tokenize_env
101 )
103 enrollment_index_context_dict["transit_processor"] = transit_processor_context
104 context.update(enrollment_index_context_dict)
106 return context
108 def _get_overlay_language(self, django_language_code):
109 """Given a Django language code, return the corresponding language code to use with Littlepay's overlay."""
110 # mapping from Django's I18N LANGUAGE_CODE to Littlepay's overlay language code
111 overlay_language = {"en": "en", "es": "es-419"}.get(django_language_code, "en")
112 return overlay_language
114 def form_valid(self, form):
115 card_token = form.cleaned_data.get("card_token")
116 status, exception = enroll(self.request, card_token)
118 return handle_enrollment_results(self.request, status, exception)
120 def form_invalid(self, form):
121 raise Exception("Invalid card token form")