Skip to content

Commit

Permalink
Refactor(core): make save_payment_method as post_update_tracker trait…
Browse files Browse the repository at this point in the history
… function (#4307)

Co-authored-by: Narayan Bhat <[email protected]>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Apr 25, 2024
1 parent 4c793c3 commit 5f40eee
Show file tree
Hide file tree
Showing 20 changed files with 546 additions and 657 deletions.
4 changes: 4 additions & 0 deletions crates/data_models/src/payments/payment_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ pub enum PaymentAttemptUpdate {
error_message: Option<Option<String>>,
updated_by: String,
},
PaymentMethodDetailsUpdate {
payment_method_id: Option<String>,
updated_by: String,
},
VoidUpdate {
status: storage_enums::AttemptStatus,
cancellation_reason: Option<String>,
Expand Down
12 changes: 12 additions & 0 deletions crates/diesel_models/src/payment_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ pub enum PaymentAttemptUpdate {
cancellation_reason: Option<String>,
updated_by: String,
},
PaymentMethodDetailsUpdate {
payment_method_id: Option<String>,
updated_by: String,
},
BlocklistUpdate {
status: storage_enums::AttemptStatus,
error_code: Option<Option<String>>,
Expand Down Expand Up @@ -644,6 +648,14 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
merchant_connector_id: Some(None),
..Default::default()
},
PaymentAttemptUpdate::PaymentMethodDetailsUpdate {
payment_method_id,
updated_by,
} => Self {
payment_method_id,
updated_by,
..Default::default()
},
PaymentAttemptUpdate::ResponseUpdate {
status,
connector,
Expand Down
260 changes: 119 additions & 141 deletions crates/router/src/core/mandate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,156 +340,134 @@ where
.change_context(errors::ApiErrorResponse::MandateUpdateFailed)?;
Ok(resp)
}

pub async fn mandate_procedure<F, FData>(
state: &AppState,
mut resp: types::RouterData<F, FData, types::PaymentsResponseData>,
maybe_customer: &Option<domain::Customer>,
resp: &types::RouterData<F, FData, types::PaymentsResponseData>,
customer_id: &Option<String>,
pm_id: Option<String>,
merchant_connector_id: Option<String>,
storage_scheme: MerchantStorageScheme,
) -> errors::RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
) -> errors::RouterResult<Option<String>>
where
FData: MandateBehaviour,
{
match resp.response {
Err(_) => {}
Ok(_) => match resp.request.get_mandate_id() {
Some(mandate_id) => {
if let Some(ref mandate_id) = mandate_id.mandate_id {
let orig_mandate = state
.store
.find_mandate_by_merchant_id_mandate_id(
resp.merchant_id.as_ref(),
mandate_id,
storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
let mandate = match orig_mandate.mandate_type {
storage_enums::MandateType::SingleUse => state
.store
.update_mandate_by_merchant_id_mandate_id(
&resp.merchant_id,
mandate_id,
storage::MandateUpdate::StatusUpdate {
mandate_status: storage_enums::MandateStatus::Revoked,
},
orig_mandate,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::MandateUpdateFailed),
storage_enums::MandateType::MultiUse => state
.store
.update_mandate_by_merchant_id_mandate_id(
&resp.merchant_id,
mandate_id,
storage::MandateUpdate::CaptureAmountUpdate {
amount_captured: Some(
orig_mandate.amount_captured.unwrap_or(0)
+ resp.request.get_amount(),
),
},
orig_mandate,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::MandateUpdateFailed),
}?;
metrics::SUBSEQUENT_MANDATE_PAYMENT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
mandate.connector,
)],
);
resp.payment_method_id = Some(mandate.payment_method_id);
}
}
None => {
if resp.request.get_setup_mandate_details().is_some() {
resp.payment_method_id = pm_id.clone();
let (mandate_reference, network_txn_id) = match resp.response.as_ref().ok() {
Some(types::PaymentsResponseData::TransactionResponse {
mandate_reference,
network_txn_id,
..
}) => (mandate_reference.clone(), network_txn_id.clone()),
_ => (None, None),
};

let mandate_ids = mandate_reference
.as_ref()
.map(|md| {
md.encode_to_value()
.change_context(
errors::ApiErrorResponse::MandateSerializationFailed,
)
.map(masking::Secret::new)
})
.transpose()?;

if let Some(new_mandate_data) = payment_helper::generate_mandate(
resp.merchant_id.clone(),
resp.payment_id.clone(),
resp.connector.clone(),
resp.request.get_setup_mandate_details().cloned(),
maybe_customer,
pm_id.get_required_value("payment_method_id")?,
mandate_ids,
network_txn_id,
get_insensitive_payment_method_data_if_exists(&resp),
mandate_reference,
merchant_connector_id,
)? {
let connector = new_mandate_data.connector.clone();
logger::debug!("{:?}", new_mandate_data);
resp.request
.set_mandate_id(Some(api_models::payments::MandateIds {
mandate_id: Some(new_mandate_data.mandate_id.clone()),
mandate_reference_id: new_mandate_data
.connector_mandate_ids
.clone()
.map(|ids| {
Some(ids)
.parse_value::<api_models::payments::ConnectorMandateReferenceId>(
"ConnectorMandateId",
)
.change_context(errors::ApiErrorResponse::MandateDeserializationFailed)
})
.transpose()?
.map_or(
new_mandate_data.network_transaction_id.clone().map(|id| {
api_models::payments::MandateReferenceId::NetworkMandateId(
id,
)
}),
|connector_id| Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
api_models::payments::ConnectorMandateReferenceId {
connector_mandate_id: connector_id.connector_mandate_id,
payment_method_id: connector_id.payment_method_id,
update_history:None,

}
)))
}));
state
.store
.insert_mandate(new_mandate_data, storage_scheme)
.await
.to_duplicate_response(errors::ApiErrorResponse::DuplicateMandate)?;
metrics::MANDATE_COUNT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes("connector", connector)],
);
};
}
}
},
let Ok(ref response) = resp.response else {
return Ok(None);
};

match resp.request.get_mandate_id() {
Some(mandate_id) => {
let Some(ref mandate_id) = mandate_id.mandate_id else {
return Ok(None);
};
let orig_mandate = state
.store
.find_mandate_by_merchant_id_mandate_id(
resp.merchant_id.as_ref(),
mandate_id,
storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
let mandate = match orig_mandate.mandate_type {
storage_enums::MandateType::SingleUse => state
.store
.update_mandate_by_merchant_id_mandate_id(
&resp.merchant_id,
mandate_id,
storage::MandateUpdate::StatusUpdate {
mandate_status: storage_enums::MandateStatus::Revoked,
},
orig_mandate,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::MandateUpdateFailed),
storage_enums::MandateType::MultiUse => state
.store
.update_mandate_by_merchant_id_mandate_id(
&resp.merchant_id,
mandate_id,
storage::MandateUpdate::CaptureAmountUpdate {
amount_captured: Some(
orig_mandate.amount_captured.unwrap_or(0)
+ resp.request.get_amount(),
),
},
orig_mandate,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::MandateUpdateFailed),
}?;
metrics::SUBSEQUENT_MANDATE_PAYMENT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
mandate.connector,
)],
);
Ok(Some(mandate_id.clone()))
}
None => {
let Some(_mandate_details) = resp.request.get_setup_mandate_details() else {
return Ok(None);
};
let (mandate_reference, network_txn_id) = match &response {
types::PaymentsResponseData::TransactionResponse {
mandate_reference,
network_txn_id,
..
} => (mandate_reference.clone(), network_txn_id.clone()),
_ => (None, None),
};

let mandate_ids = mandate_reference
.as_ref()
.map(|md| {
md.encode_to_value()
.change_context(errors::ApiErrorResponse::MandateSerializationFailed)
.map(masking::Secret::new)
})
.transpose()?;

let Some(new_mandate_data) = payment_helper::generate_mandate(
resp.merchant_id.clone(),
resp.payment_id.clone(),
resp.connector.clone(),
resp.request.get_setup_mandate_details().cloned(),
customer_id,
pm_id.get_required_value("payment_method_id")?,
mandate_ids,
network_txn_id,
get_insensitive_payment_method_data_if_exists(resp),
mandate_reference,
merchant_connector_id,
)?
else {
return Ok(None);
};

let connector = new_mandate_data.connector.clone();
logger::debug!("{:?}", new_mandate_data);

let res_mandate_id = new_mandate_data.mandate_id.clone();

state
.store
.insert_mandate(new_mandate_data, storage_scheme)
.await
.to_duplicate_response(errors::ApiErrorResponse::DuplicateMandate)?;
metrics::MANDATE_COUNT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes("connector", connector)],
);
Ok(Some(res_mandate_id))
}
}
Ok(resp)
}

#[instrument(skip(state))]
Expand Down
Loading

0 comments on commit 5f40eee

Please sign in to comment.