Skip to content

Commit d791305

Browse files
Use provider id from privilege records rather than authorize.net
1 parent f022c79 commit d791305

3 files changed

Lines changed: 63 additions & 33 deletions

File tree

backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,16 @@ def _set_privilege_id_in_line_item(self, line_items: list[dict], item_id_prefix:
125125
if line_item.get('itemId').lower().startswith(item_id_prefix.lower()):
126126
line_item['privilegeId'] = privilege_id
127127

128-
def add_privilege_ids_to_transactions(self, compact: str, transactions: list[dict]) -> list[dict]:
128+
def add_privilege_information_to_transactions(self, compact: str, transactions: list[dict]) -> list[dict]:
129129
"""
130-
Add privilege IDs to transaction line items based on the jurisdiction they were purchased for.
130+
Add privilege and licensee IDs to transaction line items based on the jurisdiction they were purchased for.
131131
132132
:param compact: The compact name
133133
:param transactions: List of transaction records to process
134-
:return: Modified list of transactions with privilege IDs added to line items
134+
:return: Modified list of transactions with privilege and licensee IDs added to line items
135135
"""
136136
for transaction in transactions:
137137
line_items = transaction['lineItems']
138-
licensee_id = transaction['licenseeId']
139138
# Extract jurisdictions from line items with format priv:{compact}-{jurisdiction}-{license type abbr}
140139
jurisdictions_to_process = set()
141140
for line_item in line_items:
@@ -152,18 +151,56 @@ def add_privilege_ids_to_transactions(self, compact: str, transactions: list[dic
152151
KeyConditionExpression=Key('compactTransactionIdGSIPK').eq(gsi_pk),
153152
)
154153

154+
# Verify that the query returned at least one record
155+
records_for_transaction_id = response.get('Items', [])
156+
if not records_for_transaction_id:
157+
logger.error(
158+
'No privilege records found for this transaction id.',
159+
compact=compact,
160+
transaction_id=transaction['transactionId'],
161+
# attempt to grab the licensee id from the authorize.net data, which may be invalid if it was masked
162+
licensee_id=transaction['licenseeId'],
163+
)
164+
# We mark the data as UNKNOWN so it still shows up in the history,
165+
# and move onto the next transaction
166+
for jurisdiction in jurisdictions_to_process:
167+
item_id_prefix = f'priv:{compact}-{jurisdiction}'
168+
# we set the privilege id to UNKNOWN, so that it will be visible in the report
169+
self._set_privilege_id_in_line_item(
170+
line_items=line_items, item_id_prefix=item_id_prefix, privilege_id='UNKNOWN'
171+
)
172+
continue
173+
174+
# ensure we only map to one provider for this transaction id
175+
provider_ids = {
176+
item['providerId']
177+
for item in records_for_transaction_id
178+
if item['type'] == 'privilege' or item['type'] == 'privilegeUpdate'
179+
}
180+
# there should only be one provider id in the set
181+
if len(provider_ids) > 1:
182+
logger.error(
183+
'More than one matching provider id found for a transaction id.',
184+
compact=compact,
185+
transaction_id=transaction['transactionId'],
186+
# attempt to grab the licensee id from the authorize.net data, which may be invalid if it was masked
187+
provider_ids=transaction['licenseeId'],
188+
)
189+
190+
# The licensee id recorded in Authorize.net cannot be trusted, as Authorize.net masks any values that look
191+
# like a credit card number (consecutive digits separated by dashes). We need to grab the provider id from
192+
# the privileges associated with this transaction and set the licensee id on the transaction to that value
193+
# to ensure it is valid.
194+
transaction['licenseeId'] = provider_ids.pop()
195+
155196
# Process each privilege record
156197
for jurisdiction in jurisdictions_to_process:
157198
# Currently, we only support one license type per transaction when purchasing privileges
158199
# so we can just use this prefix to find the matching privilege record
159200
item_id_prefix = f'priv:{compact}-{jurisdiction}'
160201
# find the first privilege record for the jurisdiction that matches the provider ID
161202
matching_privilege = next(
162-
(
163-
item
164-
for item in response.get('Items', [])
165-
if item['jurisdiction'].lower() == jurisdiction and item['providerId'] == licensee_id
166-
),
203+
(item for item in records_for_transaction_id if item['jurisdiction'].lower() == jurisdiction),
167204
None,
168205
)
169206
if matching_privilege:
@@ -185,7 +222,7 @@ def add_privilege_ids_to_transactions(self, compact: str, transactions: list[dic
185222
compact=compact,
186223
transactionId=transaction['transactionId'],
187224
jurisdiction=jurisdiction,
188-
provider_id=licensee_id,
225+
provider_id=transaction['licenseeId'],
189226
matching_privilege_records=response.get('Items', []),
190227
)
191228
# we set the privilege id to UNKNOWN, so that it will be visible in the report

backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_transaction_client.py

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def test_store_multiple_transactions(self):
8888
# Verify the batch writer was called twice
8989
self.assertEqual(self.mock_batch_writer.put_item.call_count, 2)
9090

91-
def test_add_privilege_ids_to_transactions(self):
91+
def test_add_privilege_information_to_transactions(self):
9292
# Mock the provider table query response
9393
self.mock_config.provider_table = MagicMock()
9494
self.mock_config.compact_transaction_id_gsi_name = 'compactTransactionIdGSI'
@@ -123,7 +123,7 @@ def test_add_privilege_ids_to_transactions(self):
123123
]
124124

125125
# Call the method
126-
result = self.client.add_privilege_ids_to_transactions('aslp', test_transactions)
126+
result = self.client.add_privilege_information_to_transactions('aslp', test_transactions)
127127

128128
# Verify the GSI query was called with correct parameters
129129
self.mock_config.provider_table.query.assert_called_once_with(
@@ -136,7 +136,9 @@ def test_add_privilege_ids_to_transactions(self):
136136
self.assertEqual(result[0]['lineItems'][1]['privilegeId'], 'priv-456') # NY line item
137137
self.assertNotIn('privilegeId', result[0]['lineItems'][2]) # other item
138138

139-
def test_add_privilege_ids_to_transactions_performs_check_on_provider_id_for_match(self):
139+
def test_add_privilege_information_to_transactions_maps_provider_id_to_transaction(self):
140+
expected_provider_id = 'abcd1234-5678-9012-3456-7890a0d12345'
141+
expected_privilege_id = 'priv-123'
140142
# Mock the provider table query response
141143
self.mock_config.provider_table = MagicMock()
142144
self.mock_config.compact_transaction_id_gsi_name = 'compactTransactionIdGSI'
@@ -145,16 +147,8 @@ def test_add_privilege_ids_to_transactions_performs_check_on_provider_id_for_mat
145147
{
146148
'type': 'privilege',
147149
'jurisdiction': 'CA',
148-
'privilegeId': 'priv-123',
149-
'providerId': 'prov-123',
150-
},
151-
{
152-
'type': 'privilegeUpdate',
153-
'jurisdiction': 'NY',
154-
'previous': {'privilegeId': 'priv-456'},
155-
# this should never happen in practice, but we're testing for it here
156-
# as a sanity check
157-
'providerId': 'prov-456',
150+
'privilegeId': expected_privilege_id,
151+
'providerId': expected_provider_id,
158152
},
159153
]
160154
}
@@ -163,27 +157,26 @@ def test_add_privilege_ids_to_transactions_performs_check_on_provider_id_for_mat
163157
test_transactions = [
164158
{
165159
'transactionId': 'tx123',
166-
'licenseeId': 'prov-123',
160+
# reproducing real case where licensee id was masked in authorize.net
161+
'licenseeId': 'abcdXXXXXXXXXXXXXXXXXXX-4927a0d12345',
167162
'lineItems': [
168163
{'itemId': 'priv:aslp-CA', 'unitPrice': 100},
169-
{'itemId': 'priv:aslp-NY', 'unitPrice': 200},
170164
{'itemId': 'credit-card-transaction-fee', 'unitPrice': 50},
171165
],
172166
}
173167
]
174168

175169
# Call the method
176-
result = self.client.add_privilege_ids_to_transactions('aslp', test_transactions)
170+
result = self.client.add_privilege_information_to_transactions('aslp', test_transactions)
177171

178172
# Verify the GSI query was called with correct parameters
179173
self.mock_config.provider_table.query.assert_called_once_with(
180174
IndexName='compactTransactionIdGSI',
181175
KeyConditionExpression=Key('compactTransactionIdGSIPK').eq('COMPACT#aslp#TX#tx123#'),
182176
)
183177

184-
# Verify privilege IDs were added to correct line items
185-
self.assertEqual(result[0]['lineItems'][0]['privilegeId'], 'priv-123') # CA line item
186-
# In this case, the privilege ID is unknown because the provider ID does not match
187-
# again, this should never happen in practice
188-
self.assertEqual(result[0]['lineItems'][1]['privilegeId'], 'UNKNOWN')
189-
self.assertNotIn('privilegeId', result[0]['lineItems'][2]) # other item
178+
# Verify the correct provider ID was added to the transaction
179+
self.assertEqual(expected_provider_id, result[0]['licenseeId'])
180+
# Verify the privilege id is mapped as expected
181+
self.assertEqual(expected_privilege_id, result[0]['lineItems'][0]['privilegeId'])
182+
self.assertNotIn('privilegeId', result[0]['lineItems'][1]) # credit card fee line item

backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def process_settled_transactions(event: dict, context: LambdaContext) -> dict:
9797
logger.info('Fetching privilege ids for transactions', compact=compact)
9898
# first we must add the associated privilege ids to each transaction so we can show the association in our
9999
# reports
100-
transactions_with_privilege_ids = config.transaction_client.add_privilege_ids_to_transactions(
100+
transactions_with_privilege_ids = config.transaction_client.add_privilege_information_to_transactions(
101101
compact=compact, transactions=transaction_response['transactions']
102102
)
103103
logger.info('Storing transactions in DynamoDB', compact=compact)

0 commit comments

Comments
 (0)