fix(payments): improve YooKassa webhook processing for terminal failures
Refactor the YooKassa webhook handling to better manage terminal payment failures. Introduce checks for payment status based on metadata, ensuring that only terminal failures are committed to the database. Enhance error handling and logging for non-terminal failures, providing clearer responses for retry scenarios. This improves the robustness of payment processing and reduces unnecessary retries.
This commit is contained in:
@@ -690,20 +690,44 @@ async def yookassa_webhook_route(request: web.Request):
|
||||
yookassa_service,
|
||||
lknpd_service)
|
||||
if not processed:
|
||||
# process_successful_payment uses False for permanent business failures
|
||||
# (e.g. user not found / metadata issues) and may have already updated
|
||||
# the payment status. Commit the status and ACK the webhook to stop
|
||||
# indefinite provider retries.
|
||||
try:
|
||||
await session.commit()
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
logging.exception(
|
||||
"Failed to commit failure status for YooKassa payment %s",
|
||||
payment_dict_for_processing.get("id"),
|
||||
metadata_for_result = payment_dict_for_processing.get("metadata") or {}
|
||||
payment_db_id_raw = metadata_for_result.get("payment_db_id")
|
||||
payment_db_id_for_check = None
|
||||
if isinstance(payment_db_id_raw, int):
|
||||
payment_db_id_for_check = payment_db_id_raw
|
||||
elif isinstance(payment_db_id_raw, str) and payment_db_id_raw.isdigit():
|
||||
payment_db_id_for_check = int(payment_db_id_raw)
|
||||
|
||||
terminal_failure_recorded = False
|
||||
if payment_db_id_for_check is not None:
|
||||
payment_after_processing = await payment_dal.get_payment_by_db_id(
|
||||
session,
|
||||
payment_db_id_for_check,
|
||||
)
|
||||
return web.Response(status=503, text="yookassa_processing_failed_retry")
|
||||
return web.Response(status=200, text="ok")
|
||||
terminal_failure_recorded = bool(
|
||||
payment_after_processing
|
||||
and isinstance(payment_after_processing.status, str)
|
||||
and payment_after_processing.status.startswith("failed")
|
||||
)
|
||||
|
||||
if terminal_failure_recorded:
|
||||
try:
|
||||
await session.commit()
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
logging.exception(
|
||||
"Failed to commit failure status for YooKassa payment %s",
|
||||
payment_dict_for_processing.get("id"),
|
||||
)
|
||||
return web.Response(status=503, text="yookassa_processing_failed_retry")
|
||||
return web.Response(status=200, text="ok")
|
||||
|
||||
await session.rollback()
|
||||
logging.warning(
|
||||
"YooKassa payment %s processing returned non-terminal failure; responding 503 for retry",
|
||||
payment_dict_for_processing.get("id"),
|
||||
)
|
||||
return web.Response(status=503, text="yookassa_processing_failed_retry")
|
||||
await session.commit()
|
||||
else:
|
||||
logging.warning(
|
||||
|
||||
Reference in New Issue
Block a user