Repository: juspay/hyperswitch
File: crates/router/src/core/webhooks/incoming.rs (Rust)

Code before:
                    .await
                    .change_context(errors::ApiErrorResponse::WebhookResourceNotFound)
                    .attach_printable("Failed to fetch the payout attempt")?,
            },
            _ => Err(errors::ApiErrorResponse::WebhookProcessingFailure)
                .attach_printable("received a non-payout id when processing payout webhooks")?,
        };

        let payouts = db
            .find_payout_by_merchant_id_payout_id(
                merchant_context.get_merchant_account().get_id(),
                &payout_attempt.payout_id,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::WebhookResourceNotFound)
            .attach_printable("Failed to fetch the payout")?;

        let status = common_enums::PayoutStatus::foreign_try_from(event_type)
            .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
            .attach_printable("failed payout status mapping from event type")?;

        // if status is failure then update the error_code and error_message as well
        let payout_attempt_update = if status.is_payout_failure() {
            let payout_error_details = connector
                .get_payout_error_update_object(request_details)
                .switch()
                .attach_printable("Failed to get error object for payouts")?;

            PayoutAttemptUpdate::StatusUpdate {
                connector_payout_id: payout_attempt.connector_payout_id.clone(),
                status,
                error_message: payout_error_details.error_message,
                error_code: payout_error_details.error_code,
                is_eligible: payout_attempt.is_eligible,
                unified_code: None,
                unified_message: None,
                payout_connector_metadata: payout_attempt.payout_connector_metadata.clone(),
            }
        } else {
            PayoutAttemptUpdate::StatusUpdate {
                connector_payout_id: payout_attempt.connector_payout_id.clone(),
                status,
                error_message: None,
                error_code: None,
                is_eligible: payout_attempt.is_eligible,
                unified_code: None,
                unified_message: None,
                payout_connector_metadata: payout_attempt.payout_connector_metadata.clone(),
            }
        };

Review comment (bug):
Instead of restricting this to failure case, we can make this more extensible by making this as get_payout_details which would return PayoutWebhookData which can be used in future to get any details from the payload

Code after:
                    .await
                    .change_context(errors::ApiErrorResponse::WebhookResourceNotFound)
                    .attach_printable("Failed to fetch the payout attempt")?,
            },
            _ => Err(errors::ApiErrorResponse::WebhookProcessingFailure)
                .attach_printable("received a non-payout id when processing payout webhooks")?,
        };

        let payouts = db
            .find_payout_by_merchant_id_payout_id(
                merchant_context.get_merchant_account().get_id(),
                &payout_attempt.payout_id,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::WebhookResourceNotFound)
            .attach_printable("Failed to fetch the payout")?;

        let status = common_enums::PayoutStatus::foreign_try_from(event_type)
            .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
            .attach_printable("failed payout status mapping from event type")?;

        let payout_webhook_details = connector
            .get_payout_webhook_details(request_details)
            .switch()
            .attach_printable("Failed to get error object for payouts")?;

        // if status is failure then update the error_code and error_message as well
        let payout_attempt_update = if status.is_payout_failure() {
            PayoutAttemptUpdate::StatusUpdate {
                connector_payout_id: payout_attempt.connector_payout_id.clone(),
                status,
                error_message: payout_webhook_details.error_message,
                error_code: payout_webhook_details.error_code,
                is_eligible: payout_attempt.is_eligible,
                unified_code: None,
                unified_message: None,
                payout_connector_metadata: payout_attempt.payout_connector_metadata.clone(),
            }
        } else {
            PayoutAttemptUpdate::StatusUpdate {
                connector_payout_id: payout_attempt.connector_payout_id.clone(),
                status,
                error_message: None,
                error_code: None,
                is_eligible: payout_attempt.is_eligible,
                unified_code: None,
                unified_message: None,
                payout_connector_metadata: payout_attempt.payout_connector_metadata.clone(),
            }
        };