PAN verify for non-individual clients was sending name="" + dob=null to LincPay because
the code path always read firstname/middlename/lastname (NULL on entities) and
client.dateOfBirth (also NULL on entities). LincPay then returned plain-text "Invalid
DOB format. Required format is dd/MM/yyyy", which Gson can't parse (MalformedJsonException
at line 1 col 10), surfacing as a useless 503. Now ClientKycWritePlatformServiceJpa-
RepositoryImpl.verifyPan branches on LegalForm:
- PERSON → name = firstname + middlename + lastname, dob = client.dateOfBirth
- ENTITY → name = client.fullname, dob = clientNonPerson.dateOfRegistration
Injected ClientNonPersonRepositoryWrapper for the entity branch.
ClientNonPerson got two missing getters (getConstitution, getDateOfRegistration) needed
by the above plus the family/nominee rules below.
Defensive JSON parse in LincPayKycVerificationServiceImpl: parseJson now returns null
when the body doesn't even start with '{' or '[' (i.e. vendor sent plain text or HTML),
logs the literal body at WARN, and every verify method (PAN, GSTIN, Aadhaar OTP gen,
Aadhaar OTP verify, CIN, DIN) checks for null and returns
"<X> verification failed — vendor returned non-JSON: <first 200 chars>"
So future vendor outages surface the actual response text instead of a MalformedJson-
Exception with no context.
Non-individual client rules:
- ClientFamilyMembersWritePlatformService: addFamilyMember / addClientFamilyMember /
updateFamilyMember now refuse with PlatformApiDataValidationException when the client
is an entity. Family members are an individual-only concept. Delete is intentionally
unguarded so legacy rows can be cleaned up.
- ClientNomineeWritePlatformService: addNominee / updateNominee refuse for non-individual
clients UNLESS the constitution normalises to PROPRIETORSHIP (matches both
"PROPRIETOR SHIP" and "PROPRIETORSHIP"). Proprietorships are operationally individuals
with a separate trade name, so they keep nominee support; companies / LLPs / trusts /
societies / HUFs / partnerships don't carry a nomination concept. Delete unguarded.
Verified the LincPay endpoints directly:
/validate-pan AQRPV8345E + "Aditya Vyas" + 19/12/1988 → valid (individual)
/validate-pan AAMCG5406B + "GIZMOGLADE..." + 01/12/2025 → valid (company)
/validate-pan AAMCG5406B + "" + "" → "Invalid DOB format" plain text (the failing path)
/validate-gstn 23AADCL9984A1ZH → 200 with full company data
/validate-gstn 21AAMCG5406BIZ6 → 400 "Invalid GSTIN pattern" (bad checksum, not our bug)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>