Coverage for benefits/enrollment_switchio/models.py: 97%
56 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-07 00:01 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-07 00:01 +0000
1import logging
3from django.core.exceptions import ValidationError
4from django.db import models
6from benefits.core.models import EnrollmentGroup, Environment, SecretNameField, SystemName, TransitProcessorConfig
7from benefits.secrets import get_secret_by_name
9logger = logging.getLogger(__name__)
12class SwitchioGroupIDs:
13 # SystemName.name: Switchio group ID
14 MEDICARE = "MEDICARE"
15 CALFRESH = "LOW_INCOME"
16 OLDER_ADULT = "OLDER_ADULT"
17 VETERAN = "VETERAN"
18 GCTD_CARD = "AGENCY_CARD"
21class SwitchioConfig(TransitProcessorConfig):
22 """Configuration for connecting to Switchio, an entity that applies transit agency fare rules to rider transactions."""
24 tokenization_api_key = models.TextField(
25 help_text="The API key used to access the Switchio API for tokenization.", default="", blank=True
26 )
27 tokenization_api_secret_name = SecretNameField(
28 help_text="The name of the secret containing the api_secret value used to access the Switchio API for tokenization.", # noqa: E501
29 default="",
30 blank=True,
31 )
32 pto_id = models.PositiveIntegerField(
33 help_text="The Public Transport Operator ID to use with the Switchio API for enrollment.",
34 default=0,
35 blank=True,
36 )
38 @property
39 def tokenization_api_base_url(self):
40 return get_secret_by_name("switchio-tokenization-api-base-url")
42 @property
43 def enrollment_api_base_url(self):
44 return get_secret_by_name("switchio-enrollment-api-base-url")
46 @property
47 def enrollment_api_authorization_header(self):
48 return get_secret_by_name("switchio-enrollment-api-authorization-header")
50 @property
51 def tokenization_api_secret(self):
52 secret_field = self._meta.get_field("tokenization_api_secret_name")
53 return secret_field.secret_value(self)
55 @property
56 def client_certificate_data(self):
57 """This SwitchioConfig's client certificate as a string."""
58 if self.environment == Environment.DEV.value: 58 ↛ 65line 58 didn't jump to line 65 because the condition on line 58 was always true
59 # Special case to handle un-purgeable cert in Azure dev env Key Vault with the desired `switchio-client-cert` name
60 # See: https://cal-itp.slack.com/archives/C037Y3UE71P/p1776806316220499
61 # Also affects local setup using standard fixtures with secrets
62 # TODO: Remove this special case when the deleted cert is automatically purged on July 20, 2026
63 return get_secret_by_name("switchio-int-client-cert")
65 return get_secret_by_name("switchio-client-cert")
67 @property
68 def ca_certificate_data(self):
69 """This SwitchioConfig's CA certificate as a string."""
70 return get_secret_by_name("switchio-ca-cert")
72 @property
73 def private_key_data(self):
74 """This SwitchioConfig's private key as a string."""
75 return get_secret_by_name("switchio-private-key")
77 def clean(self):
78 field_errors = {}
80 if self.pk and self.transitagency_set and any([agency.active for agency in self.transitagency_set.all()]):
81 message = "This field is required when this configuration is referenced by an active transit agency."
82 needed = dict(
83 tokenization_api_key=self.tokenization_api_key,
84 tokenization_api_secret_name=self.tokenization_api_secret_name,
85 pto_id=self.pto_id,
86 )
87 field_errors.update({k: ValidationError(message) for k, v in needed.items() if not v})
89 if field_errors:
90 raise ValidationError(field_errors)
93class SwitchioGroup(EnrollmentGroup):
95 @property
96 def group_id(self):
97 """Get the Switchio group ID, which is the same for all agencies for a given flow.
99 Returns the value of the attribute on SwitchioGroupIDs whose attribute name
100 matches the one in SystemName that's used by this group's enrollment flow.
101 """
102 return getattr(SwitchioGroupIDs, SystemName(self.enrollment_flow.system_name).name, None)
104 @staticmethod
105 def by_id(id):
106 """Get a SwitchioGroup instance by its ID."""
107 logger.debug(f"Get {SwitchioGroup.__name__} by id: {id}")
108 return SwitchioGroup.objects.get(id=id)