From d063040ee0173999b382c24b34bf3474523a5555 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 12 Aug 2026 15:16:02 -0400 Subject: [PATCH] Add postal_code and state to missing fields in the user api --- compliance/api.py | 28 +++++++++++++++++++----- compliance/api_test.py | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/compliance/api.py b/compliance/api.py index 4841fe5b9d..70f2262f77 100644 --- a/compliance/api.py +++ b/compliance/api.py @@ -122,8 +122,18 @@ def _normalize_administrative_area( } -def _missing_bill_to_fields(bill_to: dict[str, str]) -> list[str]: - """Return the CyberSource bill-to field names missing from the given data.""" +def _missing_bill_to_fields( + bill_to: dict[str, str], *, always_require_postal_and_state: bool = False +) -> list[str]: + """ + Return the CyberSource bill-to field names missing from the given data. + + CyberSource itself only requires administrative_area/postal_code for US + and CA addresses. Pass always_require_postal_and_state=True to flag them + as missing whenever they're empty regardless of country - used when + reporting missing profile fields back to the user, since they're useful + to have on file no matter where they live. + """ missing_fields = [] if not bill_to.get("first_name"): @@ -132,7 +142,7 @@ def _missing_bill_to_fields(bill_to: dict[str, str]) -> list[str]: missing_fields.append("last_name") required_fields = ["address1", "locality", "country", "email"] - if bill_to.get("country") in {"US", "CA"}: + if always_require_postal_and_state or bill_to.get("country") in {"US", "CA"}: required_fields.extend(["administrative_area", "postal_code"]) missing_fields.extend(field for field in required_fields if not bill_to.get(field)) @@ -182,9 +192,17 @@ def _build_bill_to(user) -> dict[str, str]: def get_missing_export_compliance_fields(user) -> list[str]: - """Return the profile field names required for an export compliance check that are missing.""" + """ + Return the profile field names required for an export compliance check that are missing. + + Unlike the CyberSource check itself, state and postal code are always + flagged here when empty, even for countries where CyberSource doesn't + require them. + """ bill_to = _build_bill_to(user) - missing_bill_to_fields = _missing_bill_to_fields(bill_to) + missing_bill_to_fields = _missing_bill_to_fields( + bill_to, always_require_postal_and_state=True + ) return sorted( {BILL_TO_FIELD_TO_PROFILE_FIELD[field] for field in missing_bill_to_fields} ) diff --git a/compliance/api_test.py b/compliance/api_test.py index bc99f70457..3366b6123f 100644 --- a/compliance/api_test.py +++ b/compliance/api_test.py @@ -16,6 +16,7 @@ decrypt_export_compliance_log, get_cybersource_client, get_latest_export_compliance_log, + get_missing_export_compliance_fields, log_export_compliance_check, verify_user_with_exports, ) @@ -106,6 +107,53 @@ def test_build_export_payload_requires_legal_address(export_settings): ] +def test_build_export_payload_does_not_require_state_and_postal_code_outside_us_ca( + export_settings, +): + """CyberSource itself only requires state/postal code for US and CA addresses.""" + user = UserFactory.create(name="Ada Lovelace", email="ada@example.com") + user.legal_address.country = "FR" + user.legal_address.street_address_1 = "5 Rue de Rivoli" + user.legal_address.city = "Paris" + user.legal_address.state = "" + user.legal_address.postal_code = "" + user.legal_address.save() + + payload = _build_export_payload(user) + + assert payload.order_information.bill_to.country == "FR" + + +def test_get_missing_export_compliance_fields_always_flags_state_and_postal_code(): + """ + The profile-facing missing fields list should flag state/postal code + whenever they're empty, even for countries where CyberSource doesn't + require them. + """ + user = UserFactory.create(name="Ada Lovelace", email="ada@example.com") + user.legal_address.country = "FR" + user.legal_address.street_address_1 = "5 Rue de Rivoli" + user.legal_address.city = "Paris" + user.legal_address.state = "" + user.legal_address.postal_code = "" + user.legal_address.save() + + assert get_missing_export_compliance_fields(user) == ["postal_code", "state"] + + +def test_get_missing_export_compliance_fields_us_address(): + """State/postal code should still be flagged as missing for US addresses.""" + user = UserFactory.create(name="Ada Lovelace", email="ada@example.com") + user.legal_address.country = "US" + user.legal_address.street_address_1 = "77 Massachusetts Ave" + user.legal_address.city = "Cambridge" + user.legal_address.state = "" + user.legal_address.postal_code = "" + user.legal_address.save() + + assert get_missing_export_compliance_fields(user) == ["postal_code", "state"] + + def test_normalize_administrative_area_strips_country_prefix(): """ISO-3166-2 values should be reduced to the region code for CyberSource.""" assert _normalize_administrative_area("US", "US-MA") == "MA"