feat(core): consume card details from billing connectors and first error codes and store them in payment intent table (#8250)

Co-authored-by: Nishanth Challa <nishanth.challa@Nishanth-Challa-C0WGKCFHLF.local>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
CHALLA NISHANTH BABU
2025-06-16 16:36:59 +05:30
committed by GitHub
parent 1ed2f210b2
commit abe9708d1c
16 changed files with 403 additions and 34 deletions

View File

@ -199,6 +199,8 @@ impl<F: Send + Clone + Sync>
retry_count: request.retry_count,
invoice_next_billing_time: request.invoice_next_billing_time,
triggered_by: request.triggered_by,
card_network: request.card_network.clone(),
card_issuer: request.card_issuer.clone(),
};
let payment_address = hyperswitch_domain_models::payment_address::PaymentAddress::new(
payment_intent

View File

@ -4900,6 +4900,22 @@ impl ForeignFrom<&diesel_models::types::BillingConnectorPaymentDetails>
}
}
#[cfg(feature = "v2")]
impl ForeignFrom<&diesel_models::types::BillingConnectorPaymentMethodDetails>
for api_models::payments::BillingConnectorPaymentMethodDetails
{
fn foreign_from(metadata: &diesel_models::types::BillingConnectorPaymentMethodDetails) -> Self {
match metadata {
diesel_models::types::BillingConnectorPaymentMethodDetails::Card(card_details) => {
Self::Card(api_models::payments::BillingConnectorAdditionalCardInfo {
card_issuer: card_details.card_issuer.clone(),
card_network: card_details.card_network.clone(),
})
}
}
}
}
#[cfg(feature = "v2")]
impl ForeignFrom<&hyperswitch_domain_models::payments::payment_attempt::ErrorDetails>
for api_models::payments::ErrorDetails
@ -4965,6 +4981,17 @@ impl ForeignFrom<&diesel_models::types::FeatureMetadata> for api_models::payment
),
invoice_next_billing_time: payment_revenue_recovery_metadata
.invoice_next_billing_time,
billing_connector_payment_method_details:payment_revenue_recovery_metadata
.billing_connector_payment_method_details.as_ref().map(api_models::payments::BillingConnectorPaymentMethodDetails::foreign_from),
first_payment_attempt_network_advice_code: payment_revenue_recovery_metadata
.first_payment_attempt_network_advice_code
.clone(),
first_payment_attempt_network_decline_code: payment_revenue_recovery_metadata
.first_payment_attempt_network_decline_code
.clone(),
first_payment_attempt_pg_error_code: payment_revenue_recovery_metadata
.first_payment_attempt_pg_error_code
.clone(),
}
});
let apple_pay_details = feature_metadata

View File

@ -69,14 +69,14 @@ pub async fn perform_execute_payment(
match record_attempt {
Ok(_) => {
let action = types::Action::execute_payment(
let action = Box::pin(types::Action::execute_payment(
state,
revenue_recovery_payment_data.merchant_account.get_id(),
payment_intent,
execute_task_process,
revenue_recovery_payment_data,
&revenue_recovery_metadata,
)
))
.await?;
Box::pin(action.execute_payment_task_response_handler(
state,

View File

@ -183,16 +183,22 @@ pub async fn record_internal_attempt_api(
message: "get_revenue_recovery_attempt was not constructed".to_string(),
})?;
let request_payload = revenue_recovery_attempt_data.create_payment_record_request(
&revenue_recovery_payment_data.billing_mca.id,
Some(
revenue_recovery_metadata
.active_attempt_payment_connector_id
.clone(),
),
Some(revenue_recovery_metadata.connector),
common_enums::TriggeredBy::Internal,
);
let request_payload = revenue_recovery_attempt_data
.create_payment_record_request(
state,
&revenue_recovery_payment_data.billing_mca.id,
Some(
revenue_recovery_metadata
.active_attempt_payment_connector_id
.clone(),
),
Some(revenue_recovery_metadata.connector),
common_enums::TriggeredBy::Internal,
)
.await
.change_context(errors::ApiErrorResponse::GenericNotFoundError {
message: "Cannot Create the payment record Request".to_string(),
})?;
let merchant_context_from_revenue_recovery_payment_data =
MerchantContext::NormalMerchant(Box::new(Context(

View File

@ -545,14 +545,17 @@ impl RevenueRecoveryAttempt {
errors::RevenueRecoveryError,
> {
let payment_connector_id = payment_connector_account.as_ref().map(|account: &hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount| account.id.clone());
let request_payload = self.create_payment_record_request(
billing_connector_account_id,
payment_connector_id,
payment_connector_account
.as_ref()
.map(|account| account.connector_name),
common_enums::TriggeredBy::External,
);
let request_payload = self
.create_payment_record_request(
state,
billing_connector_account_id,
payment_connector_id,
payment_connector_account
.as_ref()
.map(|account| account.connector_name),
common_enums::TriggeredBy::External,
)
.await?;
let attempt_response = Box::pin(payments::record_attempt_core(
state.clone(),
req_state.clone(),
@ -593,13 +596,15 @@ impl RevenueRecoveryAttempt {
Ok(response)
}
pub fn create_payment_record_request(
pub async fn create_payment_record_request(
&self,
state: &SessionState,
billing_merchant_connector_account_id: &id_type::MerchantConnectorAccountId,
payment_merchant_connector_account_id: Option<id_type::MerchantConnectorAccountId>,
payment_connector: Option<common_enums::connector_enums::Connector>,
triggered_by: common_enums::TriggeredBy,
) -> api_payments::PaymentsAttemptRecordRequest {
) -> CustomResult<api_payments::PaymentsAttemptRecordRequest, errors::RevenueRecoveryError>
{
let revenue_recovery_attempt_data = &self.0;
let amount_details =
api_payments::PaymentAttemptAmountDetails::from(revenue_recovery_attempt_data);
@ -609,9 +614,27 @@ impl RevenueRecoveryAttempt {
attempt_triggered_by: triggered_by,
}),
};
let card_info = revenue_recovery_attempt_data
.card_isin
.clone()
.async_and_then(|isin| async move {
let issuer_identifier_number = isin.clone();
state
.store
.get_card_info(issuer_identifier_number.as_str())
.await
.map_err(|error| services::logger::warn!(card_info_error=?error))
.ok()
})
.await
.flatten();
let card_issuer = card_info.and_then(|info| info.card_issuer);
let error =
Option::<api_payments::RecordAttemptErrorDetails>::from(revenue_recovery_attempt_data);
api_payments::PaymentsAttemptRecordRequest {
Ok(api_payments::PaymentsAttemptRecordRequest {
amount_details,
status: revenue_recovery_attempt_data.status,
billing: None,
@ -637,7 +660,9 @@ impl RevenueRecoveryAttempt {
retry_count: revenue_recovery_attempt_data.retry_count,
invoice_next_billing_time: revenue_recovery_attempt_data.invoice_next_billing_time,
triggered_by,
}
card_network: revenue_recovery_attempt_data.card_network.clone(),
card_issuer,
})
}
pub async fn find_payment_merchant_connector_account(