feat: add verification engine orchestrator and Codeplus vendor integration
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 37s

This commit is contained in:
vickytechkey 2026-10-17 15:22:16 +05:30
parent 418c7e681d
commit 71058e84c9
2 changed files with 57 additions and 12 deletions

View file

@ -125,14 +125,16 @@ class VerificationEngine:
total_latency,
)
last_data = attempts[-1].get("data") if attempts and isinstance(attempts[-1].get("data"), dict) else {}
return EngineResult(
is_success=False,
document_type=doc_type_clean,
status=VerificationStatus.FAILED,
successful_vendor=None,
data={},
data=last_data,
attempts=attempts,
error_message=f"All vendors exhausted. Last error: {last_error}",
error_message=last_error,
total_latency_ms=total_latency,
)

View file

@ -704,6 +704,27 @@ class CodeplusVendor(BaseVendor):
)
account_ifsc = result_block.get("account_ifsc") or ifsc
recommended_action = result_block.get("recommended_action") or raw_json.get("recommended_action")
def resolve_bank_failure_reason(code: Optional[str], raw_reason: Optional[str]) -> str:
if code in ("U01", "U1"):
return "Beneficiary bank reported: Account does not exist or invalid account number at this branch."
if code in ("U03", "U3"):
return "Beneficiary bank reported: Net debit cap exceeded / transaction limit reached."
if code in ("U09", "U9"):
return "Beneficiary bank server timed out. Please try again in a few moments."
if code in ("U13",):
return "Beneficiary bank reported: Account is dormant, inactive, or frozen."
if code in ("U28",):
return "Beneficiary bank reported: Branch does not participate in IMPS penny drop verification."
if code in ("U30",):
return "Beneficiary bank reported: Verification transaction declined by beneficiary bank."
if raw_reason and str(raw_reason).lower() not in ("bank verification failed", "failed", "invalid_bank_account"):
return str(raw_reason)
return "The destination bank could not verify this account. The account number or IFSC code is incorrect, inactive, or not accepting IMPS penny drop."
friendly_failure_reason = resolve_bank_failure_reason(npci_error_code, failure_reason or raw_json.get("message"))
if response.status_code == 200:
# Success condition: status == 'success' and is_valid is True
if api_status == "success" and (is_valid is True or (is_valid is None and account_holder_name and not failure_reason)):
@ -720,6 +741,7 @@ class CodeplusVendor(BaseVendor):
"account_exists": True,
"failure_reason": None,
"npci_error_code": None,
"recommended_action": recommended_action,
"user": result_block.get("user"),
"tranx_id": raw_json.get("tranx_id"),
"transaction_id": raw_json.get("tranx_id"),
@ -739,7 +761,6 @@ class CodeplusVendor(BaseVendor):
# HTTP 200 with verification failure or account invalid
err_code = str(npci_error_code or "INVALID_BANK_ACCOUNT")
err_message = failure_reason or raw_json.get("message") or "Bank account verification failed"
normalized_result = {
"document_type": "BANK_ACCOUNT",
"verification_mode": "penny_drop_v5",
@ -751,8 +772,10 @@ class CodeplusVendor(BaseVendor):
"account_holder_name": account_holder_name,
"is_valid": False,
"account_exists": False,
"failure_reason": failure_reason,
"failure_reason": friendly_failure_reason,
"raw_failure_reason": failure_reason,
"npci_error_code": npci_error_code,
"recommended_action": recommended_action,
"user": result_block.get("user"),
"tranx_id": raw_json.get("tranx_id"),
"transaction_id": raw_json.get("tranx_id"),
@ -765,7 +788,7 @@ class CodeplusVendor(BaseVendor):
logger.warning(
"Codeplus Bank Verify PD v5 returned failure: npci_error=%s, failure_reason=%s",
npci_error_code,
failure_reason,
friendly_failure_reason,
)
return VendorResult(
@ -775,32 +798,52 @@ class CodeplusVendor(BaseVendor):
data=normalized_result,
raw_response=raw_json,
error_code=err_code,
error_message=err_message,
error_message=friendly_failure_reason,
retryable=False,
)
# Upstream HTTP error status (400, 500, etc.)
is_server_error = response.status_code >= 500
err_code = str(npci_error_code or f"HTTP_{response.status_code}")
err_message = failure_reason or raw_json.get("message") or raw_json.get("detail") or raw_json.get("error") or "Bank verification failed"
if isinstance(err_message, dict):
err_message = str(err_message)
normalized_result = {
"document_type": "BANK_ACCOUNT",
"verification_mode": "penny_drop_v5",
"account_number": account_number,
"masked_account_number": masked_acc,
"ifsc": ifsc,
"account_ifsc": account_ifsc,
"beneficiary_name": account_holder_name,
"account_holder_name": account_holder_name,
"is_valid": False,
"account_exists": False,
"failure_reason": friendly_failure_reason,
"raw_failure_reason": failure_reason or raw_json.get("message"),
"npci_error_code": npci_error_code,
"recommended_action": recommended_action,
"user": result_block.get("user"),
"tranx_id": raw_json.get("tranx_id"),
"transaction_id": raw_json.get("tranx_id"),
"timestamp": raw_json.get("timestamp"),
"chargeble": raw_json.get("chargeble"),
"user_consent": raw_json.get("user_consent"),
"bank_details": result_block if result_block else raw_json,
}
logger.warning(
"Codeplus Bank Verify PD v5 failed: status_code=%d, code=%s, message=%s",
response.status_code,
err_code,
err_message,
friendly_failure_reason,
)
return VendorResult(
vendor_name=self.name,
is_success=False,
status=VerificationStatus.ERROR if is_server_error else VerificationStatus.FAILED,
data=raw_json if isinstance(raw_json, dict) else {},
data=normalized_result,
raw_response=raw_json,
error_code=err_code,
error_message=str(err_message),
error_message=friendly_failure_reason,
retryable=is_server_error or response.status_code in (429, 408, 401, 403),
)