mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat(recovery-events): add revenue recovery topic and vector config to push these events to s3 (#8285)
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> Co-authored-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
b5dddbc1a8
commit
17d34a29e4
@ -2146,8 +2146,9 @@ where
|
||||
let payment_attempt = self.payment_attempt;
|
||||
let payment_intent = self.payment_intent;
|
||||
let response = api_models::payments::PaymentAttemptRecordResponse {
|
||||
id: payment_attempt.get_id().to_owned(),
|
||||
id: payment_attempt.id.clone(),
|
||||
status: payment_attempt.status,
|
||||
amount: payment_attempt.amount_details.get_net_amount(),
|
||||
payment_intent_feature_metadata: payment_intent
|
||||
.feature_metadata
|
||||
.as_ref()
|
||||
@ -2156,6 +2157,10 @@ where
|
||||
.feature_metadata
|
||||
.as_ref()
|
||||
.map(api_models::payments::PaymentAttemptFeatureMetadata::foreign_from),
|
||||
error_details: payment_attempt
|
||||
.error
|
||||
.map(api_models::payments::RecordAttemptErrorDetails::from),
|
||||
created_at: payment_attempt.created_at,
|
||||
};
|
||||
Ok(services::ApplicationResponse::JsonWithHeaders((
|
||||
response,
|
||||
@ -5217,6 +5222,8 @@ impl ForeignFrom<&diesel_models::types::FeatureMetadata> for api_models::payment
|
||||
first_payment_attempt_pg_error_code: payment_revenue_recovery_metadata
|
||||
.first_payment_attempt_pg_error_code
|
||||
.clone(),
|
||||
invoice_billing_started_at_time: payment_revenue_recovery_metadata
|
||||
.invoice_billing_started_at_time,
|
||||
}
|
||||
});
|
||||
let apple_pay_details = feature_metadata
|
||||
|
||||
@ -34,6 +34,7 @@ use crate::{
|
||||
errors::{self, RouterResult},
|
||||
payments::{self, helpers, operations::Operation},
|
||||
revenue_recovery::{self as revenue_recovery_core},
|
||||
webhooks::recovery_incoming as recovery_incoming_flow,
|
||||
},
|
||||
db::StorageInterface,
|
||||
logger,
|
||||
@ -101,6 +102,23 @@ impl RevenueRecoveryPaymentsAttemptStatus {
|
||||
) -> Result<(), errors::ProcessTrackerError> {
|
||||
let db = &*state.store;
|
||||
|
||||
let recovery_payment_intent =
|
||||
hyperswitch_domain_models::revenue_recovery::RecoveryPaymentIntent::from(
|
||||
payment_intent,
|
||||
);
|
||||
|
||||
let recovery_payment_attempt =
|
||||
hyperswitch_domain_models::revenue_recovery::RecoveryPaymentAttempt::from(
|
||||
&payment_attempt,
|
||||
);
|
||||
|
||||
let recovery_payment_tuple = recovery_incoming_flow::RecoveryPaymentTuple::new(
|
||||
&recovery_payment_intent,
|
||||
&recovery_payment_attempt,
|
||||
);
|
||||
|
||||
let retry_count = process_tracker.retry_count;
|
||||
|
||||
match self {
|
||||
Self::Succeeded => {
|
||||
// finish psync task as the payment was a success
|
||||
@ -110,6 +128,19 @@ impl RevenueRecoveryPaymentsAttemptStatus {
|
||||
business_status::PSYNC_WORKFLOW_COMPLETE,
|
||||
)
|
||||
.await?;
|
||||
// publish events to kafka
|
||||
if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
|
||||
state,
|
||||
&recovery_payment_tuple,
|
||||
Some(retry_count+1)
|
||||
)
|
||||
.await{
|
||||
router_env::logger::error!(
|
||||
"Failed to publish revenue recovery event to kafka: {:?}",
|
||||
e
|
||||
);
|
||||
};
|
||||
|
||||
// Record a successful transaction back to Billing Connector
|
||||
// TODO: Add support for retrying failed outgoing recordback webhooks
|
||||
record_back_to_billing_connector(
|
||||
@ -130,6 +161,17 @@ impl RevenueRecoveryPaymentsAttemptStatus {
|
||||
business_status::PSYNC_WORKFLOW_COMPLETE,
|
||||
)
|
||||
.await?;
|
||||
// publish events to kafka
|
||||
if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
|
||||
state,
|
||||
&recovery_payment_tuple,
|
||||
Some(retry_count+1)
|
||||
)
|
||||
.await{
|
||||
router_env::logger::error!(
|
||||
"Failed to publish revenue recovery event to kafka : {:?}", e
|
||||
);
|
||||
};
|
||||
|
||||
// get a reschedule time
|
||||
let schedule_time = get_schedule_time_to_retry_mit_payments(
|
||||
@ -291,13 +333,66 @@ impl Action {
|
||||
revenue_recovery_metadata,
|
||||
)
|
||||
.await;
|
||||
let recovery_payment_intent =
|
||||
hyperswitch_domain_models::revenue_recovery::RecoveryPaymentIntent::from(
|
||||
payment_intent,
|
||||
);
|
||||
|
||||
// handle proxy api's response
|
||||
match response {
|
||||
Ok(payment_data) => match payment_data.payment_attempt.status.foreign_into() {
|
||||
RevenueRecoveryPaymentsAttemptStatus::Succeeded => Ok(Self::SuccessfulPayment(
|
||||
payment_data.payment_attempt.clone(),
|
||||
)),
|
||||
RevenueRecoveryPaymentsAttemptStatus::Succeeded => {
|
||||
let recovery_payment_attempt =
|
||||
hyperswitch_domain_models::revenue_recovery::RecoveryPaymentAttempt::from(
|
||||
&payment_data.payment_attempt,
|
||||
);
|
||||
|
||||
let recovery_payment_tuple = recovery_incoming_flow::RecoveryPaymentTuple::new(
|
||||
&recovery_payment_intent,
|
||||
&recovery_payment_attempt,
|
||||
);
|
||||
|
||||
// publish events to kafka
|
||||
if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
|
||||
state,
|
||||
&recovery_payment_tuple,
|
||||
Some(process.retry_count+1)
|
||||
)
|
||||
.await{
|
||||
router_env::logger::error!(
|
||||
"Failed to publish revenue recovery event to kafka: {:?}",
|
||||
e
|
||||
);
|
||||
};
|
||||
|
||||
Ok(Self::SuccessfulPayment(
|
||||
payment_data.payment_attempt.clone(),
|
||||
))
|
||||
}
|
||||
RevenueRecoveryPaymentsAttemptStatus::Failed => {
|
||||
let recovery_payment_attempt =
|
||||
hyperswitch_domain_models::revenue_recovery::RecoveryPaymentAttempt::from(
|
||||
&payment_data.payment_attempt,
|
||||
);
|
||||
|
||||
let recovery_payment_tuple = recovery_incoming_flow::RecoveryPaymentTuple::new(
|
||||
&recovery_payment_intent,
|
||||
&recovery_payment_attempt,
|
||||
);
|
||||
|
||||
// publish events to kafka
|
||||
if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
|
||||
state,
|
||||
&recovery_payment_tuple,
|
||||
Some(process.retry_count+1)
|
||||
)
|
||||
.await{
|
||||
router_env::logger::error!(
|
||||
"Failed to publish revenue recovery event to kafka: {:?}",
|
||||
e
|
||||
);
|
||||
};
|
||||
|
||||
Self::decide_retry_failure_action(
|
||||
db,
|
||||
merchant_id,
|
||||
|
||||
@ -13,7 +13,9 @@ use hyperswitch_domain_models::{
|
||||
router_response_types::revenue_recovery as revenue_recovery_response, types as router_types,
|
||||
};
|
||||
use hyperswitch_interfaces::webhooks as interface_webhooks;
|
||||
use masking::{PeekInterface, Secret};
|
||||
use router_env::{instrument, tracing};
|
||||
use services::kafka;
|
||||
|
||||
use crate::{
|
||||
core::{
|
||||
@ -133,6 +135,25 @@ pub async fn recovery_incoming_webhook_flow(
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Publish event to Kafka
|
||||
if let Some(ref attempt) = recovery_attempt_from_payment_attempt {
|
||||
// Passing `merchant_context` here
|
||||
let recovery_payment_tuple =
|
||||
&RecoveryPaymentTuple::new(&recovery_intent_from_payment_attempt, attempt);
|
||||
if let Err(e) = RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
|
||||
&state,
|
||||
recovery_payment_tuple,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
router_env::logger::error!(
|
||||
"Failed to publish revenue recovery event to kafka : {:?}",
|
||||
e
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
let attempt_triggered_by = recovery_attempt_from_payment_attempt
|
||||
.as_ref()
|
||||
.and_then(|attempt| attempt.get_attempt_triggered_by());
|
||||
@ -342,10 +363,20 @@ impl RevenueRecoveryInvoice {
|
||||
let payment_id = payments_response.id.clone();
|
||||
let status = payments_response.status;
|
||||
let feature_metadata = payments_response.feature_metadata;
|
||||
let merchant_id = merchant_context.get_merchant_account().get_id().clone();
|
||||
let revenue_recovery_invoice_data = &self.0;
|
||||
Ok(Some(revenue_recovery::RecoveryPaymentIntent {
|
||||
payment_id,
|
||||
status,
|
||||
feature_metadata,
|
||||
merchant_id,
|
||||
merchant_reference_id: Some(
|
||||
revenue_recovery_invoice_data.merchant_reference_id.clone(),
|
||||
),
|
||||
invoice_amount: revenue_recovery_invoice_data.amount,
|
||||
invoice_currency: revenue_recovery_invoice_data.currency,
|
||||
created_at: revenue_recovery_invoice_data.billing_started_at,
|
||||
billing_address: revenue_recovery_invoice_data.billing_address.clone(),
|
||||
}))
|
||||
}
|
||||
Err(err)
|
||||
@ -402,10 +433,21 @@ impl RevenueRecoveryInvoice {
|
||||
.change_context(errors::RevenueRecoveryError::PaymentIntentCreateFailed)
|
||||
.attach_printable("expected json response")?;
|
||||
|
||||
let merchant_id = merchant_context.get_merchant_account().get_id().clone();
|
||||
let revenue_recovery_invoice_data = &self.0;
|
||||
|
||||
Ok(revenue_recovery::RecoveryPaymentIntent {
|
||||
payment_id: response.id,
|
||||
status: response.status,
|
||||
feature_metadata: response.feature_metadata,
|
||||
merchant_id,
|
||||
merchant_reference_id: Some(
|
||||
revenue_recovery_invoice_data.merchant_reference_id.clone(),
|
||||
),
|
||||
invoice_amount: revenue_recovery_invoice_data.amount,
|
||||
invoice_currency: revenue_recovery_invoice_data.currency,
|
||||
created_at: revenue_recovery_invoice_data.billing_started_at,
|
||||
billing_address: revenue_recovery_invoice_data.billing_address.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -511,10 +553,18 @@ impl RevenueRecoveryAttempt {
|
||||
})
|
||||
});
|
||||
let payment_attempt =
|
||||
final_attempt.map(|attempt_res| revenue_recovery::RecoveryPaymentAttempt {
|
||||
attempt_id: attempt_res.id.to_owned(),
|
||||
attempt_status: attempt_res.status.to_owned(),
|
||||
feature_metadata: attempt_res.feature_metadata.to_owned(),
|
||||
final_attempt.map(|res| revenue_recovery::RecoveryPaymentAttempt {
|
||||
attempt_id: res.id.to_owned(),
|
||||
attempt_status: res.status.to_owned(),
|
||||
feature_metadata: res.feature_metadata.to_owned(),
|
||||
amount: res.amount.net_amount,
|
||||
network_advice_code: res.error.clone().and_then(|e| e.network_advice_code), // Placeholder, to be populated if available
|
||||
network_decline_code: res
|
||||
.error
|
||||
.clone()
|
||||
.and_then(|e| e.network_decline_code), // Placeholder, to be populated if available
|
||||
error_code: res.error.clone().map(|error| error.code),
|
||||
created_at: res.created_at,
|
||||
});
|
||||
// If we have an attempt, combine it with payment_intent in a tuple.
|
||||
let res_with_payment_intent_and_attempt =
|
||||
@ -579,11 +629,31 @@ impl RevenueRecoveryAttempt {
|
||||
attempt_id: attempt_response.id.clone(),
|
||||
attempt_status: attempt_response.status,
|
||||
feature_metadata: attempt_response.payment_attempt_feature_metadata,
|
||||
amount: attempt_response.amount,
|
||||
network_advice_code: attempt_response
|
||||
.error_details
|
||||
.clone()
|
||||
.and_then(|error| error.network_decline_code), // Placeholder, to be populated if available
|
||||
network_decline_code: attempt_response
|
||||
.error_details
|
||||
.clone()
|
||||
.and_then(|error| error.network_decline_code), // Placeholder, to be populated if available
|
||||
error_code: attempt_response
|
||||
.error_details
|
||||
.clone()
|
||||
.map(|error| error.code),
|
||||
created_at: attempt_response.created_at,
|
||||
},
|
||||
revenue_recovery::RecoveryPaymentIntent {
|
||||
payment_id: payment_intent.payment_id.clone(),
|
||||
status: attempt_response.status.into(), // Using status from attempt_response
|
||||
feature_metadata: attempt_response.payment_intent_feature_metadata, // Using feature_metadata from attempt_response
|
||||
merchant_id: payment_intent.merchant_id.clone(),
|
||||
merchant_reference_id: payment_intent.merchant_reference_id.clone(),
|
||||
invoice_amount: payment_intent.invoice_amount,
|
||||
invoice_currency: payment_intent.invoice_currency,
|
||||
created_at: payment_intent.created_at,
|
||||
billing_address: payment_intent.billing_address.clone(),
|
||||
},
|
||||
))
|
||||
}
|
||||
@ -665,6 +735,8 @@ impl RevenueRecoveryAttempt {
|
||||
connector_customer_id: revenue_recovery_attempt_data.connector_customer_id.clone(),
|
||||
retry_count: revenue_recovery_attempt_data.retry_count,
|
||||
invoice_next_billing_time: revenue_recovery_attempt_data.invoice_next_billing_time,
|
||||
invoice_billing_started_at_time: revenue_recovery_attempt_data
|
||||
.invoice_billing_started_at_time,
|
||||
triggered_by,
|
||||
card_network: revenue_recovery_attempt_data.card_network.clone(),
|
||||
card_issuer,
|
||||
@ -1180,3 +1252,104 @@ impl BillingConnectorInvoiceSyncFlowRouterData {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RecoveryPaymentTuple(
|
||||
revenue_recovery::RecoveryPaymentIntent,
|
||||
revenue_recovery::RecoveryPaymentAttempt,
|
||||
);
|
||||
|
||||
impl RecoveryPaymentTuple {
|
||||
pub fn new(
|
||||
payment_intent: &revenue_recovery::RecoveryPaymentIntent,
|
||||
payment_attempt: &revenue_recovery::RecoveryPaymentAttempt,
|
||||
) -> Self {
|
||||
Self(payment_intent.clone(), payment_attempt.clone())
|
||||
}
|
||||
|
||||
pub async fn publish_revenue_recovery_event_to_kafka(
|
||||
state: &SessionState,
|
||||
recovery_payment_tuple: &Self,
|
||||
retry_count: Option<i32>,
|
||||
) -> CustomResult<(), errors::RevenueRecoveryError> {
|
||||
let recovery_payment_intent = &recovery_payment_tuple.0;
|
||||
let recovery_payment_attempt = &recovery_payment_tuple.1;
|
||||
let revenue_recovery_feature_metadata = recovery_payment_intent
|
||||
.feature_metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.revenue_recovery.as_ref());
|
||||
|
||||
let billing_city = recovery_payment_intent
|
||||
.billing_address
|
||||
.as_ref()
|
||||
.and_then(|billing_address| billing_address.address.as_ref())
|
||||
.and_then(|address| address.city.clone())
|
||||
.map(Secret::new);
|
||||
|
||||
let billing_state = recovery_payment_intent
|
||||
.billing_address
|
||||
.as_ref()
|
||||
.and_then(|billing_address| billing_address.address.as_ref())
|
||||
.and_then(|address| address.state.clone());
|
||||
|
||||
let billing_country = recovery_payment_intent
|
||||
.billing_address
|
||||
.as_ref()
|
||||
.and_then(|billing_address| billing_address.address.as_ref())
|
||||
.and_then(|address| address.country);
|
||||
|
||||
let card_info = revenue_recovery_feature_metadata.and_then(|metadata| {
|
||||
metadata
|
||||
.billing_connector_payment_method_details
|
||||
.as_ref()
|
||||
.and_then(|details| details.get_billing_connector_card_info())
|
||||
});
|
||||
|
||||
#[allow(clippy::as_conversions)]
|
||||
let retry_count = Some(retry_count.unwrap_or_else(|| {
|
||||
revenue_recovery_feature_metadata
|
||||
.map(|data| data.total_retry_count as i32)
|
||||
.unwrap_or(0)
|
||||
}));
|
||||
|
||||
let event = kafka::revenue_recovery::RevenueRecovery {
|
||||
merchant_id: &recovery_payment_intent.merchant_id,
|
||||
invoice_amount: recovery_payment_intent.invoice_amount,
|
||||
invoice_currency: &recovery_payment_intent.invoice_currency,
|
||||
invoice_date: revenue_recovery_feature_metadata.and_then(|data| {
|
||||
data.invoice_billing_started_at_time
|
||||
.map(|time| time.assume_utc())
|
||||
}),
|
||||
invoice_due_date: revenue_recovery_feature_metadata
|
||||
.and_then(|data| data.invoice_next_billing_time.map(|time| time.assume_utc())),
|
||||
billing_city,
|
||||
billing_country: billing_country.as_ref(),
|
||||
billing_state,
|
||||
attempt_amount: recovery_payment_attempt.amount,
|
||||
attempt_currency: &recovery_payment_intent.invoice_currency.clone(),
|
||||
attempt_status: &recovery_payment_attempt.attempt_status.clone(),
|
||||
pg_error_code: recovery_payment_attempt.error_code.clone(),
|
||||
network_advice_code: recovery_payment_attempt.network_advice_code.clone(),
|
||||
network_error_code: recovery_payment_attempt.network_decline_code.clone(),
|
||||
first_pg_error_code: revenue_recovery_feature_metadata
|
||||
.and_then(|data| data.first_payment_attempt_pg_error_code.clone()),
|
||||
first_network_advice_code: revenue_recovery_feature_metadata
|
||||
.and_then(|data| data.first_payment_attempt_network_advice_code.clone()),
|
||||
first_network_error_code: revenue_recovery_feature_metadata
|
||||
.and_then(|data| data.first_payment_attempt_network_decline_code.clone()),
|
||||
attempt_created_at: recovery_payment_attempt.created_at.assume_utc(),
|
||||
payment_method_type: revenue_recovery_feature_metadata
|
||||
.map(|data| &data.payment_method_type),
|
||||
payment_method_subtype: revenue_recovery_feature_metadata
|
||||
.map(|data| &data.payment_method_subtype),
|
||||
card_network: card_info
|
||||
.as_ref()
|
||||
.and_then(|info| info.card_network.as_ref()),
|
||||
card_issuer: card_info.and_then(|data| data.card_issuer.clone()),
|
||||
retry_count,
|
||||
payment_gateway: revenue_recovery_feature_metadata.map(|data| data.connector),
|
||||
};
|
||||
state.event_handler.log_event(&event);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@ pub enum EventType {
|
||||
Consolidated,
|
||||
Authentication,
|
||||
RoutingApiLogs,
|
||||
RevenueRecovery,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Clone)]
|
||||
|
||||
@ -28,6 +28,7 @@ mod payment_intent;
|
||||
mod payment_intent_event;
|
||||
mod refund;
|
||||
mod refund_event;
|
||||
pub mod revenue_recovery;
|
||||
use diesel_models::{authentication::Authentication, refund::Refund};
|
||||
use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent};
|
||||
use serde::Serialize;
|
||||
@ -162,6 +163,7 @@ pub struct KafkaSettings {
|
||||
consolidated_events_topic: String,
|
||||
authentication_analytics_topic: String,
|
||||
routing_logs_topic: String,
|
||||
revenue_recovery_topic: String,
|
||||
}
|
||||
|
||||
impl KafkaSettings {
|
||||
@ -277,6 +279,7 @@ pub struct KafkaProducer {
|
||||
authentication_analytics_topic: String,
|
||||
ckh_database_name: Option<String>,
|
||||
routing_logs_topic: String,
|
||||
revenue_recovery_topic: String,
|
||||
}
|
||||
|
||||
struct RdKafkaProducer(ThreadedProducer<DefaultProducerContext>);
|
||||
@ -327,6 +330,7 @@ impl KafkaProducer {
|
||||
authentication_analytics_topic: conf.authentication_analytics_topic.clone(),
|
||||
ckh_database_name: None,
|
||||
routing_logs_topic: conf.routing_logs_topic.clone(),
|
||||
revenue_recovery_topic: conf.revenue_recovery_topic.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -665,6 +669,7 @@ impl KafkaProducer {
|
||||
EventType::Consolidated => &self.consolidated_events_topic,
|
||||
EventType::Authentication => &self.authentication_analytics_topic,
|
||||
EventType::RoutingApiLogs => &self.routing_logs_topic,
|
||||
EventType::RevenueRecovery => &self.revenue_recovery_topic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
crates/router/src/services/kafka/revenue_recovery.rs
Normal file
43
crates/router/src/services/kafka/revenue_recovery.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use common_utils::{id_type, types::MinorUnit};
|
||||
use masking::Secret;
|
||||
use time::OffsetDateTime;
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub struct RevenueRecovery<'a> {
|
||||
pub merchant_id: &'a id_type::MerchantId,
|
||||
pub invoice_amount: MinorUnit,
|
||||
pub invoice_currency: &'a common_enums::Currency,
|
||||
#[serde(default, with = "time::serde::timestamp::nanoseconds::option")]
|
||||
pub invoice_due_date: Option<OffsetDateTime>,
|
||||
#[serde(with = "time::serde::timestamp::nanoseconds::option")]
|
||||
pub invoice_date: Option<OffsetDateTime>,
|
||||
pub billing_country: Option<&'a common_enums::CountryAlpha2>,
|
||||
pub billing_state: Option<Secret<String>>,
|
||||
pub billing_city: Option<Secret<String>>,
|
||||
pub attempt_amount: MinorUnit,
|
||||
pub attempt_currency: &'a common_enums::Currency,
|
||||
pub attempt_status: &'a common_enums::AttemptStatus,
|
||||
pub pg_error_code: Option<String>,
|
||||
pub network_advice_code: Option<String>,
|
||||
pub network_error_code: Option<String>,
|
||||
pub first_pg_error_code: Option<String>,
|
||||
pub first_network_advice_code: Option<String>,
|
||||
pub first_network_error_code: Option<String>,
|
||||
#[serde(default, with = "time::serde::timestamp::nanoseconds")]
|
||||
pub attempt_created_at: OffsetDateTime,
|
||||
pub payment_method_type: Option<&'a common_enums::PaymentMethod>,
|
||||
pub payment_method_subtype: Option<&'a common_enums::PaymentMethodType>,
|
||||
pub card_network: Option<&'a common_enums::CardNetwork>,
|
||||
pub card_issuer: Option<String>,
|
||||
pub retry_count: Option<i32>,
|
||||
pub payment_gateway: Option<common_enums::connector_enums::Connector>,
|
||||
}
|
||||
|
||||
impl super::KafkaMessage for RevenueRecovery<'_> {
|
||||
fn key(&self) -> String {
|
||||
self.merchant_id.get_string_repr().to_string()
|
||||
}
|
||||
|
||||
fn event_type(&self) -> crate::events::EventType {
|
||||
crate::events::EventType::RevenueRecovery
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user