feat(kyc): verify seller PAN and compare name with Tradhox Document Verification service
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 37s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 37s
This commit is contained in:
parent
517563b9e5
commit
518dc812b9
1 changed files with 94 additions and 63 deletions
157
api/views.py
157
api/views.py
|
|
@ -2057,76 +2057,107 @@ class UploadEAadhaarView(APIView):
|
|||
return Response({'error': 'Internal error verifying Aadhaar'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class VerifyPANView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
"""
|
||||
POST /api/v1/profile/kyc/verify-pan/
|
||||
Verifies seller PAN via the Tradhox Document Verification microservice,
|
||||
compares seller's full legal name with the PAN cardholder name,
|
||||
pulls all active GSTINs linked to this PAN, and updates the database.
|
||||
Supports both onboarding sellers and authenticated users.
|
||||
"""
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def post(self, request):
|
||||
pan_number = request.data.get('id_number')
|
||||
if not pan_number:
|
||||
return Response({'error': 'id_number is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
user = request.user
|
||||
surepass_token = os.environ.get('SUREPASS_JWT_TOKEN')
|
||||
|
||||
if not surepass_token:
|
||||
logger.warning("Surepass token not configured. Skipping actual PAN verification.")
|
||||
# Mock success for local dev without token
|
||||
mock_success_data = {
|
||||
"pan_number": pan_number.upper(),
|
||||
"full_name": "MOCK USER NAME",
|
||||
"dob": "1990-01-01",
|
||||
"gender": "M",
|
||||
"status": "valid",
|
||||
"aadhaar_linked": True,
|
||||
}
|
||||
kyc, _ = SellerKYC.objects.update_or_create(
|
||||
supplier=user,
|
||||
defaults={
|
||||
'pan_number': mock_success_data['pan_number'],
|
||||
'pan_full_name': mock_success_data['full_name'],
|
||||
'pan_dob': mock_success_data['dob'],
|
||||
'pan_status': mock_success_data['status'],
|
||||
'pan_aadhaar_linked': mock_success_data['aadhaar_linked'],
|
||||
'pan_verification_response': {"mock": True, "data": mock_success_data}
|
||||
}
|
||||
)
|
||||
return Response({'success': True, 'kyc_data': mock_success_data})
|
||||
|
||||
url = "https://kyc-api.surepass.io/api/v1/pan/pan-with-details-v3"
|
||||
headers = {
|
||||
'Authorization': f'Bearer {surepass_token}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
payload = {
|
||||
"id_number": pan_number
|
||||
}
|
||||
pan_number = request.data.get('pan_number') or request.data.get('pan') or request.data.get('id_number')
|
||||
seller_name = request.data.get('seller_name') or request.data.get('full_name') or ''
|
||||
entered_gstin = request.data.get('gstin', '')
|
||||
|
||||
try:
|
||||
response = requests.request("POST", url, headers=headers, json=payload)
|
||||
resp_json = response.json()
|
||||
|
||||
if response.status_code == 200 and resp_json.get('success'):
|
||||
data = resp_json.get('data', {})
|
||||
kyc, _ = SellerKYC.objects.update_or_create(
|
||||
if not pan_number:
|
||||
return Response({'error': 'pan_number is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
pan_number = pan_number.strip().upper()
|
||||
user = request.user if getattr(request, 'user', None) and request.user.is_authenticated else None
|
||||
if not seller_name and user:
|
||||
seller_name = f"{user.first_name} {user.last_name}".strip() or user.username
|
||||
|
||||
# Target Microservice: Tradhox Document Verification Engine
|
||||
doc_verify_urls = [
|
||||
os.environ.get('DOC_VERIFICATION_URL', 'http://127.0.0.1:8092/api/verify/seller/'),
|
||||
'https://beta-documentverification.tradhox.com/api/verify/seller/'
|
||||
]
|
||||
|
||||
resp_json = None
|
||||
resp_status = None
|
||||
|
||||
for endpoint in doc_verify_urls:
|
||||
try:
|
||||
resp = requests.post(
|
||||
endpoint,
|
||||
json={
|
||||
'seller_name': seller_name,
|
||||
'pan': pan_number,
|
||||
'gstin': entered_gstin,
|
||||
},
|
||||
timeout=15,
|
||||
)
|
||||
resp_status = resp.status_code
|
||||
resp_json = resp.json()
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not reach document verification endpoint {endpoint}: {e}")
|
||||
continue
|
||||
|
||||
if resp_json and resp_status == 200 and resp_json.get('is_success'):
|
||||
data = resp_json.get('data', {})
|
||||
pan_details = data.get('pan_details', {})
|
||||
verified_name = data.get('verified_pan_name') or seller_name
|
||||
|
||||
# 1. Save response in SellerKYC and SupplierProfile database record if user is authenticated
|
||||
if user:
|
||||
SellerKYC.objects.update_or_create(
|
||||
supplier=user,
|
||||
defaults={
|
||||
'pan_number': data.get('pan_number'),
|
||||
'pan_full_name': data.get('full_name'),
|
||||
'pan_dob': data.get('dob'),
|
||||
'pan_status': data.get('status'),
|
||||
'pan_aadhaar_linked': data.get('aadhaar_linked', False),
|
||||
'pan_verification_response': resp_json
|
||||
'pan_number': pan_number,
|
||||
'pan_full_name': verified_name,
|
||||
'pan_dob': pan_details.get('dob') or pan_details.get('date_of_birth', ''),
|
||||
'pan_status': pan_details.get('status', 'valid'),
|
||||
'pan_aadhaar_linked': pan_details.get('aadhaar_seeding_status') == 'LINKED',
|
||||
'pan_verification_response': resp_json,
|
||||
}
|
||||
)
|
||||
return Response({'success': True, 'kyc_data': data})
|
||||
else:
|
||||
return Response({
|
||||
'success': False,
|
||||
'error': resp_json.get('message', 'PAN Verification failed')
|
||||
}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calling Surepass PAN API: {e}")
|
||||
return Response({'error': 'Internal error verifying PAN'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
profile, _ = SupplierProfile.objects.get_or_create(user=user)
|
||||
if data.get('primary_gstin'):
|
||||
profile.gstin = data['primary_gstin']
|
||||
profile.is_gstin_verified = True
|
||||
elif entered_gstin:
|
||||
profile.gstin = entered_gstin
|
||||
profile.is_gstin_verified = data.get('gstin_matched', False)
|
||||
if verified_name:
|
||||
profile.legal_entity_name = verified_name
|
||||
profile.save()
|
||||
|
||||
return Response({
|
||||
'success': True,
|
||||
'name_matched': data.get('name_matched', True),
|
||||
'match_score': data.get('match_score', 100.0),
|
||||
'verified_pan_name': verified_name,
|
||||
'seller_name': seller_name,
|
||||
'pan_number': pan_number,
|
||||
'gst_records': data.get('gst_records', []),
|
||||
'primary_gstin': data.get('primary_gstin'),
|
||||
'reference_id': resp_json.get('reference_id'),
|
||||
'kyc_data': pan_details,
|
||||
})
|
||||
elif resp_json:
|
||||
error_msg = resp_json.get('error_message') or 'PAN verification or name matching failed'
|
||||
return Response({
|
||||
'success': False,
|
||||
'name_matched': resp_json.get('name_matched', False),
|
||||
'error': error_msg,
|
||||
'data': resp_json.get('data', {})
|
||||
}, status=resp_status if resp_status in [400, 422] else status.HTTP_422_UNPROCESSABLE_ENTITY)
|
||||
else:
|
||||
return Response({'error': 'Document verification service unavailable'}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
|
||||
|
||||
|
||||
class ProductSearchView(APIView):
|
||||
|
|
|
|||
Loading…
Reference in a new issue