Validate KYC CSV Records
StripeSoftware Engineer · Technical ScreenLast updated
Implement a KYC CSV record validator that parses customer records, normalizes fields, and validates tax IDs and owner name matches.
Validate KYC CSV Records
When onboarding new businesses, Stripe verifies business identity by matching submitted application details against official registry records. You are tasked with writing a validation parser for a CSV export of onboarding records.
Write a function validate_kyc_records(csv_data: str) -> list[dict] that parses the CSV text and returns validation results for each record.
Input Format
csv_data is a multi-line string containing a CSV header followed by zero or more data rows. The header row is always:
record_id,business_name,tax_id,legal_owner_name,submitted_owner_name
Fields may contain leading or trailing whitespace that must be stripped.
Validation Rules
Evaluate each row against the following rules in order:
- Missing Fields: All 5 fields must be present and non-empty after trimming whitespace. If any field is empty, flag error
"MISSING_REQUIRED_FIELD". - Tax ID: A valid
tax_idmust consist of exactly 9 digits after stripping hyphens (-) and spaces. (e.g.,"12-3456789"and" 123456789 "are valid;"12-345"or"12345678A"are invalid). If invalid, flag error"INVALID_TAX_ID". - Owner Name Match: Compare
legal_owner_nameandsubmitted_owner_nameusing token overlap:- Normalize both names by converting to lowercase, removing all punctuation/symbols (keep only letters, digits, and spaces), and splitting on whitespace into sets of unique words.
- Let
Sbe the set of unique words insubmitted_owner_nameandLbe the set of unique words inlegal_owner_name. - A name is considered a match if at least one word overlaps (
len(S & L) >= 1) and the overlap covers at least 50% of the words submitted (len(S & L) / len(S) >= 0.5). If not, flag error"OWNER_NAME_MISMATCH".
A record may accumulate multiple distinct errors if it violates multiple rules (except MISSING_REQUIRED_FIELD skips rules 2 and 3 if the corresponding field is empty).
Output Format
Return a list of dictionaries, one per record in the order they appear:
[
{
"record_id": str, # trimmed record_id (or "" if missing)
"is_valid": bool, # True if errors is empty, else False
"errors": list[str] # list of error strings in the order defined
},
...
]
Examples
csv_input = """record_id,business_name,tax_id,legal_owner_name,submitted_owner_name
rec_1,Acme Corp,12-3456789,Jane Doe,Jane Doe
rec_2,Beta LLC,99-999,Robert Smith,Bob Smith
rec_3,Gamma Inc,11-2233445,Alice M. Walker,Alice Walker
"""
validate_kyc_records(csv_input)
# Returns:
# [
# {"record_id": "rec_1", "is_valid": True, "errors": []},
# {"record_id": "rec_2", "is_valid": False, "errors": ["INVALID_TAX_ID", "OWNER_NAME_MISMATCH"]},
# {"record_id": "rec_3", "is_valid": True, "errors": []}
# ]
Constraints
csv_datacontains between 1 and 10,000 rows.- Field values do not contain commas or escaped quotes.
Hints
Hint 1
Start by parsing the CSV lines with Python's built-in `csv.reader` to cleanly handle whitespace and varying row lengths.
Hint 2
For the Tax ID, remove all hyphens and spaces first, then check whether the resulting string has length 9 and satisfies `.isdigit()`.
Hint 3
For name matching, use regular expressions or `str.isalnum()` to strip non-alphanumeric characters, lowercase everything, and compare set intersections.
Follow-up questions
- How would you handle quoted fields that contain commas or newlines within a cell?
- If common nickname dictionaries (e.g., 'Bob' -> 'Robert') need to be supported in owner matching, how would you integrate that into the pipeline?
- How would you stream and process this data if the input CSV was several gigabytes and could not fit into memory?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.