diff --git a/verifier/views.py b/verifier/views.py index ed5f22c..e554c23 100644 --- a/verifier/views.py +++ b/verifier/views.py @@ -284,30 +284,112 @@ class SellerPanGstVerifyView(APIView): (gst_records[0].get("document_id") or gst_records[0].get("gstin")) if gst_records and isinstance(gst_records[0], dict) else None ) + target_gstin = entered_gstin or primary_gstin or "" gstin_matched = False - if entered_gstin: - gstin_matched = any( - (g.get("document_id") or g.get("gstin") or "").upper() == entered_gstin - for g in gst_records if isinstance(g, dict) - ) + gst_details = {} + verified_gst_name = "" + gst_name_match = {"matched": True, "score": 100.0, "reason": "No GST provided"} + gst_error = None + + if target_gstin: + # Check if entered GSTIN exists in fetched GST records for this PAN + matching_gst_record = None + for g in gst_records: + if isinstance(g, dict): + doc_id = (g.get("document_id") or g.get("gstin") or "").upper() + if doc_id == target_gstin: + matching_gst_record = g + gstin_matched = True + break + + # Validate that 15-character GSTIN contains the 10-character PAN at chars 3-12 + if len(target_gstin) == 15 and target_gstin[2:12].upper() != pan.upper(): + gstin_matched = False + gst_error = f"GSTIN '{target_gstin}' does not match the provided PAN card '{pan}'." + + # Check if matching record has legal_name + if matching_gst_record and matching_gst_record.get("legal_name"): + verified_gst_name = matching_gst_record.get("legal_name") + gst_details = matching_gst_record + else: + # Call gst_verify to get legal_name and active status + try: + gst_verify_result = engine.verify( + document_type="gst_verify", + data={"gst_number": target_gstin}, + preferred_vendor=preferred_vendor, + ) + for att in gst_verify_result.attempts: + att["attempt_number"] = len(all_attempts) + 1 + all_attempts.append(att) + + if gst_verify_result.is_success and gst_verify_result.data: + gst_details = gst_verify_result.data.get("gst_details") or gst_verify_result.data + verified_gst_name = ( + gst_verify_result.data.get("legal_name") + or gst_details.get("legalName") + or gst_details.get("tradeName") + or "" + ) + gstin_matched = True + except Exception as gst_err: + logger.warning("Optional gst_verify call failed: %s", gst_err) + + # If verified_gst_name wasn't returned by upstream, fallback to matching record or verified_pan_name if PAN matches GSTIN + if not verified_gst_name and gstin_matched: + verified_gst_name = verified_pan_name + + # Compare GST legal name against seller full name and verified PAN name + if verified_gst_name: + gst_name_match = compare_names(seller_name, verified_gst_name) + if not gst_name_match["matched"] and verified_pan_name: + pan_to_gst_match = compare_names(verified_pan_name, verified_gst_name) + if pan_to_gst_match["matched"]: + gst_name_match = pan_to_gst_match total_latency = (time.time() - start_time) * 1000 - overall_success = pan_result.is_success and match_result["matched"] - overall_status = "SUCCESS" if overall_success else "NAME_MISMATCH" + + pan_name_matched = bool(match_result.get("matched", False)) + gst_name_matched = bool(gst_name_match.get("matched", False)) if target_gstin else True + + if gst_error: + overall_success = False + overall_status = "GSTIN_PAN_MISMATCH" + error_msg = gst_error + elif not pan_name_matched: + overall_success = False + overall_status = "NAME_MISMATCH" + error_msg = f"PAN name mismatch: The entered name '{seller_name}' does not match PAN record '{verified_pan_name}' ({match_result.get('reason', '')})" + elif target_gstin and not gst_name_matched: + overall_success = False + overall_status = "GST_NAME_MISMATCH" + error_msg = f"GST name mismatch: The entered name '{seller_name}' does not match GST legal name '{verified_gst_name}' ({gst_name_match.get('reason', '')})" + else: + overall_success = pan_result.is_success and pan_name_matched + overall_status = "SUCCESS" if overall_success else "FAILED" + error_msg = None combined_data = { "pan_number": pan, "verified_pan_name": verified_pan_name, "seller_name": seller_name, - "name_matched": match_result["matched"], - "match_score": match_result["score"], - "match_reason": match_result["reason"], + "name_matched": pan_name_matched, + "pan_name_matched": pan_name_matched, + "pan_match_score": match_result.get("score", 0.0), + "pan_match_reason": match_result.get("reason", ""), "pan_details": pan_data, + "gst_number": target_gstin, + "verified_gst_name": verified_gst_name, + "gst_name_matched": gst_name_matched, + "gst_match_score": gst_name_match.get("score", 0.0), + "gst_match_reason": gst_name_match.get("reason", ""), + "gst_details": gst_details, "gst_records": gst_records, "active_gstins": active_gstins, "primary_gstin": primary_gstin, "entered_gstin": entered_gstin, "gstin_matched": gstin_matched, + "is_seller_verified": overall_success, } # Step 4: Persist verification transaction & attempt logs in database @@ -335,16 +417,13 @@ class SellerPanGstVerifyView(APIView): latency_ms=att.get("latency_ms", 0.0), ) - error_msg = None - if not overall_success: - error_msg = f"Name mismatch: The entered name '{seller_name}' does not match PAN record '{verified_pan_name}' ({match_result['reason']})" - res_payload = { "reference_id": str(tx.reference_id), "is_success": overall_success, "status": overall_status, "document_type": "SELLER_PAN_GST", "successful_vendor": pan_result.successful_vendor, + "is_seller_verified": overall_success, "data": combined_data, "attempts_count": len(all_attempts), "attempts": all_attempts,