mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(router): add support for stateful straight through routing (#752)
This commit is contained in:
@ -63,9 +63,16 @@ pub struct PaymentsRequest {
|
||||
#[serde(default, deserialize_with = "amount::deserialize_option")]
|
||||
pub amount: Option<Amount>,
|
||||
|
||||
#[schema(value_type = Option<RoutingAlgorithm>, example = json!({
|
||||
"type": "single",
|
||||
"data": "stripe"
|
||||
}))]
|
||||
pub routing: Option<serde_json::Value>,
|
||||
|
||||
/// This allows the merchant to manually select a connector with which the payment can go through
|
||||
#[schema(value_type = Option<Connector>, max_length = 255, example = "stripe")]
|
||||
#[schema(value_type = Option<Vec<Connector>>, max_length = 255, example = json!(["stripe", "adyen"]))]
|
||||
pub connector: Option<Vec<api_enums::Connector>>,
|
||||
|
||||
/// The currency of the payment request can be specified here
|
||||
#[schema(value_type = Option<Currency>, example = "USD")]
|
||||
pub currency: Option<api_enums::Currency>,
|
||||
|
||||
@ -475,7 +475,7 @@ pub fn to_currency_base_unit(
|
||||
| storage_models::enums::Currency::OMR => amount_f64 / 1000.00,
|
||||
_ => amount_f64 / 100.00,
|
||||
};
|
||||
Ok(format!("{:.2}", amount))
|
||||
Ok(format!("{amount:.2}"))
|
||||
}
|
||||
|
||||
pub fn str_to_f32<S>(value: &str, serializer: S) -> Result<S::Ok, S::Error>
|
||||
@ -496,7 +496,7 @@ pub fn collect_values_by_removing_signature(
|
||||
serde_json::Value::Null => vec!["null".to_owned()],
|
||||
serde_json::Value::Bool(b) => vec![b.to_string()],
|
||||
serde_json::Value::Number(n) => match n.as_f64() {
|
||||
Some(f) => vec![format!("{:.2}", f)],
|
||||
Some(f) => vec![format!("{f:.2}")],
|
||||
None => vec![n.to_string()],
|
||||
},
|
||||
serde_json::Value::String(s) => {
|
||||
|
||||
@ -670,12 +670,12 @@ pub async fn add_delete_tokenized_data_task(
|
||||
})
|
||||
.into_report()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| format!("unable to convert into value {:?}", lookup_key))?;
|
||||
.attach_printable_lazy(|| format!("unable to convert into value {lookup_key:?}"))?;
|
||||
|
||||
let schedule_time = get_delete_tokenize_schedule_time(db, &pm, 0).await;
|
||||
|
||||
let process_tracker_entry = storage::ProcessTrackerNew {
|
||||
id: format!("{}_{}", runner, lookup_key),
|
||||
id: format!("{runner}_{lookup_key}"),
|
||||
name: Some(String::from(runner)),
|
||||
tag: vec![String::from("BASILISK-V3")],
|
||||
runner: Some(String::from(runner)),
|
||||
@ -694,10 +694,7 @@ pub async fn add_delete_tokenized_data_task(
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| {
|
||||
format!(
|
||||
"Failed while inserting task in process_tracker: lookup_key: {}",
|
||||
lookup_key
|
||||
)
|
||||
format!("Failed while inserting task in process_tracker: lookup_key: {lookup_key}")
|
||||
})?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ use self::{
|
||||
};
|
||||
use crate::{
|
||||
core::{
|
||||
errors::{self, RouterResponse, RouterResult},
|
||||
errors::{self, CustomResult, RouterResponse, RouterResult},
|
||||
payment_methods::vault,
|
||||
},
|
||||
db::StorageInterface,
|
||||
@ -34,7 +34,7 @@ use crate::{
|
||||
self, api,
|
||||
storage::{self, enums as storage_enums},
|
||||
},
|
||||
utils::OptionExt,
|
||||
utils::{Encode, OptionExt, ValueExt},
|
||||
};
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@ -99,28 +99,14 @@ where
|
||||
|
||||
payment_data.payment_method_data = payment_method_data;
|
||||
|
||||
let connector_details = operation
|
||||
.to_domain()?
|
||||
.get_connector(
|
||||
&merchant_account,
|
||||
state,
|
||||
&req,
|
||||
payment_data.payment_attempt.connector.as_ref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let connector = match should_call_connector(&operation, &payment_data) {
|
||||
true => Some(
|
||||
route_connector(
|
||||
state,
|
||||
&merchant_account,
|
||||
&mut payment_data,
|
||||
connector_details,
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
false => None,
|
||||
};
|
||||
let connector = get_connector_choice(
|
||||
&operation,
|
||||
state,
|
||||
&req,
|
||||
&merchant_account,
|
||||
&mut payment_data,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let (operation, mut payment_data) = operation
|
||||
.to_update_tracker()?
|
||||
@ -133,12 +119,12 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
operation
|
||||
.to_domain()?
|
||||
.add_task_to_process_tracker(state, &payment_data.payment_attempt)
|
||||
.await?;
|
||||
|
||||
if let Some(connector_details) = connector {
|
||||
operation
|
||||
.to_domain()?
|
||||
.add_task_to_process_tracker(state, &payment_data.payment_attempt)
|
||||
.await?;
|
||||
|
||||
payment_data = match connector_details {
|
||||
api::ConnectorCallType::Single(connector) => {
|
||||
call_connector_service(
|
||||
@ -153,6 +139,7 @@ where
|
||||
)
|
||||
.await?
|
||||
}
|
||||
|
||||
api::ConnectorCallType::Multiple(connectors) => {
|
||||
call_multiple_connectors_service(
|
||||
state,
|
||||
@ -164,34 +151,6 @@ where
|
||||
)
|
||||
.await?
|
||||
}
|
||||
api::ConnectorCallType::Routing => {
|
||||
let connector = payment_data
|
||||
.payment_attempt
|
||||
.connector
|
||||
.clone()
|
||||
.get_required_value("connector")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("No connector selected for routing")?;
|
||||
|
||||
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||
&state.conf.connectors,
|
||||
&connector,
|
||||
api::GetToken::Connector,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
call_connector_service(
|
||||
state,
|
||||
&merchant_account,
|
||||
&validate_result.payment_id,
|
||||
connector_data,
|
||||
&operation,
|
||||
payment_data,
|
||||
&customer,
|
||||
call_connector_action,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
if payment_data.payment_intent.status != storage_enums::IntentStatus::RequiresCustomerAction
|
||||
{
|
||||
@ -694,7 +653,7 @@ pub async fn list_payments(
|
||||
) -> RouterResponse<api::PaymentListResponse> {
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use crate::types::transformers::ForeignFrom;
|
||||
use crate::types::transformers::ForeignTryFrom;
|
||||
|
||||
helpers::validate_payment_list_request(&constraints)?;
|
||||
let merchant_id = &merchant.merchant_id;
|
||||
@ -724,7 +683,11 @@ pub async fn list_payments(
|
||||
.collect::<Vec<(storage::PaymentIntent, storage::PaymentAttempt)>>()
|
||||
.await;
|
||||
|
||||
let data: Vec<api::PaymentsResponse> = pi.into_iter().map(ForeignFrom::foreign_from).collect();
|
||||
let data: Vec<api::PaymentsResponse> = pi
|
||||
.into_iter()
|
||||
.map(ForeignTryFrom::foreign_try_from)
|
||||
.collect::<Result<_, _>>()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
Ok(services::ApplicationResponse::Json(
|
||||
api::PaymentListResponse {
|
||||
@ -766,47 +729,192 @@ pub async fn add_process_sync_task(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn route_connector<F>(
|
||||
pub fn update_straight_through_routing<F>(
|
||||
payment_data: &mut PaymentData<F>,
|
||||
request_straight_through: serde_json::Value,
|
||||
) -> CustomResult<(), errors::ParsingError>
|
||||
where
|
||||
F: Send + Clone,
|
||||
{
|
||||
let mut routing_data: storage::RoutingData = payment_data
|
||||
.payment_attempt
|
||||
.connector
|
||||
.clone()
|
||||
.unwrap_or_else(|| serde_json::json!({}))
|
||||
.parse_value("RoutingData")
|
||||
.attach_printable("Invalid routing data format in payment attempt")?;
|
||||
|
||||
let request_straight_through: api::RoutingAlgorithm = request_straight_through
|
||||
.parse_value("RoutingAlgorithm")
|
||||
.attach_printable("Invalid straight through routing rules format")?;
|
||||
|
||||
routing_data.algorithm = Some(request_straight_through);
|
||||
|
||||
let encoded_routing_data = Encode::<storage::RoutingData>::encode_to_value(&routing_data)
|
||||
.attach_printable("Unable to serialize routing data to serde value")?;
|
||||
|
||||
payment_data.payment_attempt.connector = Some(encoded_routing_data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_connector_choice<F, Req>(
|
||||
operation: &BoxedOperation<'_, F, Req>,
|
||||
state: &AppState,
|
||||
req: &Req,
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
payment_data: &mut PaymentData<F>,
|
||||
) -> RouterResult<Option<api::ConnectorCallType>>
|
||||
where
|
||||
F: Send + Clone,
|
||||
{
|
||||
let connector_choice = operation
|
||||
.to_domain()?
|
||||
.get_connector(merchant_account, state, req)
|
||||
.await?;
|
||||
|
||||
let connector = if should_call_connector(operation, payment_data) {
|
||||
Some(match connector_choice {
|
||||
api::ConnectorChoice::SessionMultiple(connectors) => {
|
||||
api::ConnectorCallType::Multiple(connectors)
|
||||
}
|
||||
|
||||
api::ConnectorChoice::StraightThrough(straight_through) => connector_selection(
|
||||
state,
|
||||
merchant_account,
|
||||
payment_data,
|
||||
Some(straight_through),
|
||||
)?,
|
||||
|
||||
api::ConnectorChoice::Decide => {
|
||||
connector_selection(state, merchant_account, payment_data, None)?
|
||||
}
|
||||
})
|
||||
} else if let api::ConnectorChoice::StraightThrough(val) = connector_choice {
|
||||
update_straight_through_routing(payment_data, val)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to update straight through routing algorithm")?;
|
||||
None
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(connector)
|
||||
}
|
||||
|
||||
pub fn connector_selection<F>(
|
||||
state: &AppState,
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
payment_data: &mut PaymentData<F>,
|
||||
connector_call_type: api::ConnectorCallType,
|
||||
request_straight_through: Option<serde_json::Value>,
|
||||
) -> RouterResult<api::ConnectorCallType>
|
||||
where
|
||||
F: Send + Clone,
|
||||
{
|
||||
match connector_call_type {
|
||||
api::ConnectorCallType::Single(connector) => {
|
||||
payment_data.payment_attempt.connector = Some(connector.connector_name.to_string());
|
||||
let mut routing_data: storage::RoutingData = payment_data
|
||||
.payment_attempt
|
||||
.connector
|
||||
.clone()
|
||||
.unwrap_or_else(|| serde_json::json!({}))
|
||||
.parse_value("RoutingData")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Invalid routing data format in payment attempt")?;
|
||||
|
||||
Ok(api::ConnectorCallType::Single(connector))
|
||||
}
|
||||
let request_straight_through: Option<api::RoutingAlgorithm> = request_straight_through
|
||||
.map(|val| val.parse_value("RoutingAlgorithm"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Invalid straight through routing rules format")?;
|
||||
|
||||
api::ConnectorCallType::Routing => {
|
||||
let routing_algorithm: api::RoutingAlgorithm = merchant_account
|
||||
.routing_algorithm
|
||||
.clone()
|
||||
.parse_value("RoutingAlgorithm")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not decode merchant routing rules")?;
|
||||
let decided_connector = decide_connector(
|
||||
state,
|
||||
merchant_account,
|
||||
request_straight_through,
|
||||
&mut routing_data,
|
||||
)?;
|
||||
|
||||
let connector_name = match routing_algorithm {
|
||||
api::RoutingAlgorithm::Single(conn) => conn.to_string(),
|
||||
};
|
||||
let encoded_routing_data = Encode::<storage::RoutingData>::encode_to_value(&routing_data)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Unable to serialize routing data to serde value")?;
|
||||
|
||||
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||
&state.conf.connectors,
|
||||
&connector_name,
|
||||
api::GetToken::Connector,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Routing algorithm gave invalid connector")?;
|
||||
payment_data.payment_attempt.connector = Some(encoded_routing_data);
|
||||
|
||||
payment_data.payment_attempt.connector = Some(connector_name);
|
||||
|
||||
Ok(api::ConnectorCallType::Single(connector_data))
|
||||
}
|
||||
|
||||
call_type @ api::ConnectorCallType::Multiple(_) => Ok(call_type),
|
||||
}
|
||||
Ok(decided_connector)
|
||||
}
|
||||
|
||||
pub fn decide_connector(
|
||||
state: &AppState,
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
request_straight_through: Option<api::RoutingAlgorithm>,
|
||||
routing_data: &mut storage::RoutingData,
|
||||
) -> RouterResult<api::ConnectorCallType> {
|
||||
if let Some(ref connector_name) = routing_data.routed_through {
|
||||
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||
&state.conf.connectors,
|
||||
connector_name,
|
||||
api::GetToken::Connector,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Invalid connector name received in 'routed_through'")?;
|
||||
|
||||
return Ok(api::ConnectorCallType::Single(connector_data));
|
||||
}
|
||||
|
||||
if let Some(routing_algorithm) = request_straight_through {
|
||||
let connector_name = match &routing_algorithm {
|
||||
api::RoutingAlgorithm::Single(conn) => conn.to_string(),
|
||||
};
|
||||
|
||||
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||
&state.conf.connectors,
|
||||
&connector_name,
|
||||
api::GetToken::Connector,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Invalid connector name received in routing algorithm")?;
|
||||
|
||||
routing_data.routed_through = Some(connector_name);
|
||||
routing_data.algorithm = Some(routing_algorithm);
|
||||
return Ok(api::ConnectorCallType::Single(connector_data));
|
||||
}
|
||||
|
||||
if let Some(ref routing_algorithm) = routing_data.algorithm {
|
||||
let connector_name = match routing_algorithm {
|
||||
api::RoutingAlgorithm::Single(conn) => conn.to_string(),
|
||||
};
|
||||
|
||||
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||
&state.conf.connectors,
|
||||
&connector_name,
|
||||
api::GetToken::Connector,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Invalid connector name received in routing algorithm")?;
|
||||
|
||||
routing_data.routed_through = Some(connector_name);
|
||||
return Ok(api::ConnectorCallType::Single(connector_data));
|
||||
}
|
||||
|
||||
let routing_algorithm: api::RoutingAlgorithm = merchant_account
|
||||
.routing_algorithm
|
||||
.clone()
|
||||
.parse_value("RoutingAlgorithm")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Unable to deserialize merchant routing algorithm")?;
|
||||
|
||||
let connector_name = match routing_algorithm {
|
||||
api::RoutingAlgorithm::Single(conn) => conn.to_string(),
|
||||
};
|
||||
|
||||
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||
&state.conf.connectors,
|
||||
&connector_name,
|
||||
api::GetToken::Connector,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Routing algorithm gave invalid connector")?;
|
||||
|
||||
routing_data.routed_through = Some(connector_name);
|
||||
|
||||
Ok(api::ConnectorCallType::Single(connector_data))
|
||||
}
|
||||
|
||||
@ -471,9 +471,14 @@ where
|
||||
Op: std::fmt::Debug,
|
||||
{
|
||||
if check_if_operation_confirm(operation) {
|
||||
let connector_name = payment_attempt
|
||||
let routed_through: storage::RoutedThroughData = payment_attempt
|
||||
.connector
|
||||
.clone()
|
||||
.parse_value("RoutedThroughData")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
let connector_name = routed_through
|
||||
.routed_through
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
let schedule_time = payment_sync::get_sync_process_schedule_time(
|
||||
@ -624,20 +629,13 @@ pub async fn get_customer_from_details<F: Clone>(
|
||||
}
|
||||
|
||||
pub async fn get_connector_default(
|
||||
state: &AppState,
|
||||
request_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
let connectors = &state.conf.connectors;
|
||||
if let Some(connector_name) = request_connector {
|
||||
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||
connectors,
|
||||
connector_name,
|
||||
api::GetToken::Connector,
|
||||
)?;
|
||||
Ok(api::ConnectorCallType::Single(connector_data))
|
||||
} else {
|
||||
Ok(api::ConnectorCallType::Routing)
|
||||
}
|
||||
_state: &AppState,
|
||||
request_connector: Option<serde_json::Value>,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
Ok(request_connector.map_or(
|
||||
api::ConnectorChoice::Decide,
|
||||
api::ConnectorChoice::StraightThrough,
|
||||
))
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
|
||||
@ -126,8 +126,7 @@ pub trait Domain<F: Clone, R>: Send + Sync {
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &R,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse>;
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -195,9 +194,8 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsRetrieveRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, previously_used_connector).await
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@ -263,9 +261,8 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsCaptureRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, previously_used_connector).await
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,8 +316,7 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsCancelRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, previously_used_connector).await
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,19 +265,10 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for CompleteAuthorize {
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
// Use a new connector in the confirm call or use the same one which was passed when
|
||||
// creating the payment or if none is passed then use the routing algorithm
|
||||
let request_connector = request
|
||||
.connector
|
||||
.as_ref()
|
||||
.and_then(|connector| connector.first().map(|c| c.to_string()));
|
||||
helpers::get_connector_default(
|
||||
state,
|
||||
request_connector.as_ref().or(previously_used_connector),
|
||||
)
|
||||
.await
|
||||
helpers::get_connector_default(state, request.routing.clone()).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -287,19 +287,10 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for PaymentConfirm {
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
// Use a new connector in the confirm call or use the same one which was passed when
|
||||
// creating the payment or if none is passed then use the routing algorithm
|
||||
let request_connector = request
|
||||
.connector
|
||||
.as_ref()
|
||||
.and_then(|connector| connector.first().map(|c| c.to_string()));
|
||||
helpers::get_connector_default(
|
||||
state,
|
||||
request_connector.as_ref().or(previously_used_connector),
|
||||
)
|
||||
.await
|
||||
helpers::get_connector_default(state, request.routing.clone()).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ use crate::{
|
||||
storage::{
|
||||
self,
|
||||
enums::{self, IntentStatus},
|
||||
PaymentAttemptExt,
|
||||
},
|
||||
transformers::ForeignInto,
|
||||
},
|
||||
@ -137,7 +138,8 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
})?;
|
||||
connector_response = db
|
||||
.insert_connector_response(
|
||||
Self::make_connector_response(&payment_attempt),
|
||||
Self::make_connector_response(&payment_attempt)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?,
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
@ -276,13 +278,8 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for PaymentCreate {
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsRequest,
|
||||
_previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
let request_connector = request
|
||||
.connector
|
||||
.as_ref()
|
||||
.and_then(|connector| connector.first().map(|c| c.to_string()));
|
||||
helpers::get_connector_default(state, request_connector.as_ref()).await
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, request.routing.clone()).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,12 +448,6 @@ impl PaymentCreate {
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to encode additional pm data")?;
|
||||
|
||||
let connector = request.connector.as_ref().and_then(|connector_vec| {
|
||||
connector_vec
|
||||
.first()
|
||||
.map(|first_connector| first_connector.to_string())
|
||||
});
|
||||
|
||||
Ok(storage::PaymentAttemptNew {
|
||||
payment_id: payment_id.to_string(),
|
||||
merchant_id: merchant_id.to_string(),
|
||||
@ -476,7 +467,6 @@ impl PaymentCreate {
|
||||
payment_experience: request.payment_experience.map(ForeignInto::foreign_into),
|
||||
payment_method_type: request.payment_method_type.map(ForeignInto::foreign_into),
|
||||
payment_method_data: additional_pm_data,
|
||||
connector,
|
||||
..storage::PaymentAttemptNew::default()
|
||||
})
|
||||
}
|
||||
@ -529,18 +519,18 @@ impl PaymentCreate {
|
||||
#[instrument(skip_all)]
|
||||
pub fn make_connector_response(
|
||||
payment_attempt: &storage::PaymentAttempt,
|
||||
) -> storage::ConnectorResponseNew {
|
||||
storage::ConnectorResponseNew {
|
||||
) -> CustomResult<storage::ConnectorResponseNew, errors::ParsingError> {
|
||||
Ok(storage::ConnectorResponseNew {
|
||||
payment_id: payment_attempt.payment_id.clone(),
|
||||
merchant_id: payment_attempt.merchant_id.clone(),
|
||||
attempt_id: payment_attempt.attempt_id.clone(),
|
||||
created_at: payment_attempt.created_at,
|
||||
modified_at: payment_attempt.modified_at,
|
||||
connector_name: payment_attempt.connector.clone(),
|
||||
connector_name: payment_attempt.get_routed_through_connector()?,
|
||||
connector_transaction_id: None,
|
||||
authentication_data: None,
|
||||
encoded_data: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -119,7 +119,8 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::VerifyRequest> for Paym
|
||||
|
||||
connector_response = match db
|
||||
.insert_connector_response(
|
||||
PaymentCreate::make_connector_response(&payment_attempt),
|
||||
PaymentCreate::make_connector_response(&payment_attempt)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?,
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
@ -273,9 +274,8 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::VerifyRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, previously_used_connector).await
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -293,7 +293,7 @@ async fn payment_response_update_tracker<F: Clone, T>(
|
||||
let (payment_attempt_update, connector_response_update) = match router_data.response.clone() {
|
||||
Err(err) => (
|
||||
Some(storage::PaymentAttemptUpdate::ErrorUpdate {
|
||||
connector: Some(router_data.connector.clone()),
|
||||
connector: None,
|
||||
status: storage::enums::AttemptStatus::Failure,
|
||||
error_message: Some(err.message),
|
||||
error_code: Some(err.code),
|
||||
@ -316,7 +316,7 @@ async fn payment_response_update_tracker<F: Clone, T>(
|
||||
};
|
||||
|
||||
let encoded_data = payment_data.connector_response.encoded_data.clone();
|
||||
let connector_name = payment_data.payment_attempt.connector.clone();
|
||||
let connector_name = router_data.connector.clone();
|
||||
|
||||
let authentication_data = redirection_data
|
||||
.map(|data| utils::Encode::<RedirectForm>::encode_to_value(&data))
|
||||
@ -330,7 +330,7 @@ async fn payment_response_update_tracker<F: Clone, T>(
|
||||
|
||||
let payment_attempt_update = storage::PaymentAttemptUpdate::ResponseUpdate {
|
||||
status: router_data.status,
|
||||
connector: Some(router_data.connector),
|
||||
connector: None,
|
||||
connector_transaction_id: connector_transaction_id.clone(),
|
||||
authentication_type: None,
|
||||
payment_method_id: Some(router_data.payment_method_id),
|
||||
@ -345,7 +345,7 @@ async fn payment_response_update_tracker<F: Clone, T>(
|
||||
connector_transaction_id,
|
||||
authentication_data,
|
||||
encoded_data,
|
||||
connector_name,
|
||||
connector_name: Some(connector_name),
|
||||
};
|
||||
|
||||
(
|
||||
|
||||
@ -293,8 +293,7 @@ where
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsSessionRequest,
|
||||
_previously_used_connector: Option<&String>,
|
||||
) -> RouterResult<api::ConnectorCallType> {
|
||||
) -> RouterResult<api::ConnectorChoice> {
|
||||
let connectors = &state.conf.connectors;
|
||||
let db = &state.store;
|
||||
|
||||
@ -434,6 +433,6 @@ where
|
||||
connectors_data
|
||||
};
|
||||
|
||||
Ok(api::ConnectorCallType::Multiple(connectors_data))
|
||||
Ok(api::ConnectorChoice::SessionMultiple(connectors_data))
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,8 +250,7 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsStartRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, previously_used_connector).await
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,10 +101,9 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for PaymentStatus {
|
||||
&'a self,
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, previously_used_connector).await
|
||||
request: &api::PaymentsRequest,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, request.routing.clone()).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -316,10 +316,9 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for PaymentUpdate {
|
||||
&'a self,
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsRequest,
|
||||
previously_used_connector: Option<&String>,
|
||||
) -> CustomResult<api::ConnectorCallType, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, previously_used_connector).await
|
||||
request: &api::PaymentsRequest,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, request.routing.clone()).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,8 +14,8 @@ use crate::{
|
||||
services::{self, RedirectForm},
|
||||
types::{
|
||||
self, api,
|
||||
storage::{self, enums},
|
||||
transformers::{ForeignFrom, ForeignInto},
|
||||
storage::{self, enums, PaymentAttemptExt},
|
||||
transformers::{ForeignInto, ForeignTryFrom},
|
||||
},
|
||||
utils::{OptionExt, ValueExt},
|
||||
};
|
||||
@ -275,6 +275,9 @@ where
|
||||
})
|
||||
}
|
||||
let mut response: api::PaymentsResponse = Default::default();
|
||||
let routed_through = payment_attempt
|
||||
.get_routed_through_connector()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
services::ApplicationResponse::Json(
|
||||
response
|
||||
.set_payment_id(Some(payment_attempt.payment_id))
|
||||
@ -283,7 +286,7 @@ where
|
||||
.set_amount(payment_attempt.amount)
|
||||
.set_amount_capturable(None)
|
||||
.set_amount_received(payment_intent.amount_captured)
|
||||
.set_connector(payment_attempt.connector)
|
||||
.set_connector(routed_through)
|
||||
.set_client_secret(payment_intent.client_secret.map(masking::Secret::new))
|
||||
.set_created(Some(payment_intent.created_at))
|
||||
.set_currency(currency)
|
||||
@ -398,11 +401,15 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
||||
fn foreign_from(item: (storage::PaymentIntent, storage::PaymentAttempt)) -> Self {
|
||||
impl ForeignTryFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
|
||||
fn foreign_try_from(
|
||||
item: (storage::PaymentIntent, storage::PaymentAttempt),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let pi = item.0;
|
||||
let pa = item.1;
|
||||
Self {
|
||||
Ok(Self {
|
||||
payment_id: Some(pi.payment_id),
|
||||
merchant_id: Some(pi.merchant_id),
|
||||
status: pi.status.foreign_into(),
|
||||
@ -414,11 +421,11 @@ impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::Pay
|
||||
description: pi.description,
|
||||
metadata: pi.metadata,
|
||||
customer_id: pi.customer_id,
|
||||
connector: pa.connector,
|
||||
connector: pa.get_routed_through_connector()?,
|
||||
payment_method: pa.payment_method.map(ForeignInto::foreign_into),
|
||||
payment_method_type: pa.payment_method_type.map(ForeignInto::foreign_into),
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ use crate::{
|
||||
types::{
|
||||
self,
|
||||
api::{self, refunds},
|
||||
storage::{self, enums, ProcessTrackerExt},
|
||||
storage::{self, enums, PaymentAttemptExt, ProcessTrackerExt},
|
||||
transformers::{ForeignFrom, ForeignInto},
|
||||
},
|
||||
utils::{self, OptionExt},
|
||||
@ -113,24 +113,25 @@ pub async fn trigger_refund_to_gateway(
|
||||
payment_intent: &storage::PaymentIntent,
|
||||
creds_identifier: Option<String>,
|
||||
) -> RouterResult<storage::Refund> {
|
||||
let connector = payment_attempt
|
||||
.connector
|
||||
.clone()
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)?;
|
||||
let connector_id = connector.to_string();
|
||||
let routed_through = payment_attempt
|
||||
.get_routed_through_connector()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||
.into_report()
|
||||
.attach_printable("Failed to retrieve connector from payment attempt")?;
|
||||
|
||||
metrics::REFUND_COUNT.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&[metrics::request::add_attributes(
|
||||
"connector",
|
||||
connector.to_string(),
|
||||
routed_through.clone(),
|
||||
)],
|
||||
);
|
||||
|
||||
let connector: api::ConnectorData = api::ConnectorData::get_connector_by_name(
|
||||
&state.conf.connectors,
|
||||
&connector_id,
|
||||
&routed_through,
|
||||
api::GetToken::Connector,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -143,11 +144,11 @@ pub async fn trigger_refund_to_gateway(
|
||||
.attach_printable("Transaction in invalid")
|
||||
})?;
|
||||
|
||||
validator::validate_for_valid_refunds(payment_attempt)?;
|
||||
validator::validate_for_valid_refunds(payment_attempt, connector.connector_name)?;
|
||||
|
||||
let mut router_data = core_utils::construct_refund_router_data(
|
||||
state,
|
||||
&connector_id,
|
||||
&routed_through,
|
||||
merchant_account,
|
||||
(payment_attempt.amount, currency),
|
||||
payment_intent,
|
||||
@ -544,10 +545,12 @@ pub async fn validate_and_create_refund(
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::MaximumRefundCount)?;
|
||||
|
||||
let connector = payment_attempt.connector.clone().ok_or_else(|| {
|
||||
report!(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("connector not populated in payment attempt.")
|
||||
})?;
|
||||
let connector = payment_attempt
|
||||
.get_routed_through_connector()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||
.into_report()
|
||||
.attach_printable("No connector populated in payment attempt")?;
|
||||
|
||||
refund_create_req = storage::RefundNew::default()
|
||||
.set_refund_id(refund_id.to_string())
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use common_utils::ext_traits::StringExt;
|
||||
use error_stack::{report, IntoReport, ResultExt};
|
||||
use error_stack::{report, IntoReport};
|
||||
use router_env::{instrument, tracing};
|
||||
use time::PrimitiveDateTime;
|
||||
|
||||
@ -141,14 +140,8 @@ pub fn validate_refund_list(limit: Option<i64>) -> CustomResult<i64, errors::Api
|
||||
|
||||
pub fn validate_for_valid_refunds(
|
||||
payment_attempt: &storage_models::payment_attempt::PaymentAttempt,
|
||||
connector: api_models::enums::Connector,
|
||||
) -> RouterResult<()> {
|
||||
let connector: api_models::enums::Connector = payment_attempt
|
||||
.connector
|
||||
.clone()
|
||||
.get_required_value("connector")?
|
||||
.parse_enum("connector")
|
||||
.change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)?;
|
||||
|
||||
let payment_method = payment_attempt
|
||||
.payment_method
|
||||
.as_ref()
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::{
|
||||
scheduler::{consumer, process_data, utils},
|
||||
types::{
|
||||
api,
|
||||
storage::{self, enums, ProcessTrackerExt},
|
||||
storage::{self, enums, PaymentAttemptExt, ProcessTrackerExt},
|
||||
},
|
||||
utils::{OptionExt, ValueExt},
|
||||
};
|
||||
@ -64,9 +64,10 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow {
|
||||
_ => {
|
||||
let connector = payment_data
|
||||
.payment_attempt
|
||||
.connector
|
||||
.clone()
|
||||
.get_routed_through_connector()
|
||||
.map_err(errors::ProcessTrackerError::EParsingError)?
|
||||
.ok_or(errors::ProcessTrackerError::MissingRequiredField)?;
|
||||
|
||||
retry_sync_task(
|
||||
db,
|
||||
connector,
|
||||
|
||||
@ -124,8 +124,13 @@ pub struct ConnectorData {
|
||||
pub get_token: GetToken,
|
||||
}
|
||||
|
||||
pub enum ConnectorChoice {
|
||||
SessionMultiple(Vec<ConnectorData>),
|
||||
StraightThrough(serde_json::Value),
|
||||
Decide,
|
||||
}
|
||||
|
||||
pub enum ConnectorCallType {
|
||||
Routing,
|
||||
Multiple(Vec<ConnectorData>),
|
||||
Single(ConnectorData),
|
||||
}
|
||||
|
||||
@ -1,7 +1,43 @@
|
||||
use error_stack::ResultExt;
|
||||
pub use storage_models::payment_attempt::{
|
||||
PaymentAttempt, PaymentAttemptNew, PaymentAttemptUpdate, PaymentAttemptUpdateInternal,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
core::errors::{self, CustomResult},
|
||||
utils::ValueExt,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RoutingData {
|
||||
pub routed_through: Option<String>,
|
||||
pub algorithm: Option<api_models::admin::RoutingAlgorithm>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RoutedThroughData {
|
||||
pub routed_through: Option<String>,
|
||||
}
|
||||
|
||||
pub trait PaymentAttemptExt {
|
||||
fn get_routed_through_connector(&self) -> CustomResult<Option<String>, errors::ParsingError>;
|
||||
}
|
||||
|
||||
impl PaymentAttemptExt for PaymentAttempt {
|
||||
fn get_routed_through_connector(&self) -> CustomResult<Option<String>, errors::ParsingError> {
|
||||
if let Some(ref val) = self.connector {
|
||||
let data: RoutedThroughData = val
|
||||
.clone()
|
||||
.parse_value("RoutedThroughData")
|
||||
.attach_printable("Failed to read routed_through connector from payment attempt")?;
|
||||
|
||||
Ok(data.routed_through)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kv_store")]
|
||||
impl crate::utils::storage_partitioning::KvStorePartition for PaymentAttempt {}
|
||||
|
||||
@ -28,9 +64,12 @@ mod tests {
|
||||
|
||||
let payment_id = Uuid::new_v4().to_string();
|
||||
let current_time = common_utils::date_time::now();
|
||||
let connector = types::Connector::Dummy.to_string();
|
||||
let payment_attempt = PaymentAttemptNew {
|
||||
payment_id: payment_id.clone(),
|
||||
connector: Some(types::Connector::Dummy.to_string()),
|
||||
connector: Some(serde_json::json!({
|
||||
"routed_through": connector,
|
||||
})),
|
||||
created_at: current_time.into(),
|
||||
modified_at: current_time.into(),
|
||||
..PaymentAttemptNew::default()
|
||||
@ -57,11 +96,14 @@ mod tests {
|
||||
let current_time = common_utils::date_time::now();
|
||||
let payment_id = Uuid::new_v4().to_string();
|
||||
let merchant_id = Uuid::new_v4().to_string();
|
||||
let connector = types::Connector::Dummy.to_string();
|
||||
|
||||
let payment_attempt = PaymentAttemptNew {
|
||||
payment_id: payment_id.clone(),
|
||||
merchant_id: merchant_id.clone(),
|
||||
connector: Some(types::Connector::Dummy.to_string()),
|
||||
connector: Some(serde_json::json!({
|
||||
"routed_through": connector,
|
||||
})),
|
||||
created_at: current_time.into(),
|
||||
modified_at: current_time.into(),
|
||||
..PaymentAttemptNew::default()
|
||||
@ -96,11 +138,14 @@ mod tests {
|
||||
let uuid = uuid::Uuid::new_v4().to_string();
|
||||
let state = routes::AppState::with_storage(conf, StorageImpl::PostgresqlTest).await;
|
||||
let current_time = common_utils::date_time::now();
|
||||
let connector = types::Connector::Dummy.to_string();
|
||||
|
||||
let payment_attempt = PaymentAttemptNew {
|
||||
payment_id: uuid.clone(),
|
||||
merchant_id: "1".to_string(),
|
||||
connector: Some(types::Connector::Dummy.to_string()),
|
||||
connector: Some(serde_json::json!({
|
||||
"routed_through": connector,
|
||||
})),
|
||||
created_at: current_time.into(),
|
||||
modified_at: current_time.into(),
|
||||
// Adding a mandate_id
|
||||
|
||||
@ -288,7 +288,6 @@ async fn payments_create_core() {
|
||||
)),
|
||||
merchant_id: Some("jarnura".to_string()),
|
||||
amount: Some(6540.into()),
|
||||
connector: None,
|
||||
currency: Some(api_enums::Currency::USD),
|
||||
capture_method: Some(api_enums::CaptureMethod::Automatic),
|
||||
amount_to_capture: Some(6540),
|
||||
@ -438,7 +437,6 @@ async fn payments_create_core_adyen_no_redirect() {
|
||||
payment_id: Some(api::PaymentIdType::PaymentIntentId(payment_id.clone())),
|
||||
merchant_id: Some(merchant_id.clone()),
|
||||
amount: Some(6540.into()),
|
||||
connector: None,
|
||||
currency: Some(api_enums::Currency::USD),
|
||||
capture_method: Some(api_enums::CaptureMethod::Automatic),
|
||||
amount_to_capture: Some(6540),
|
||||
|
||||
@ -203,7 +203,6 @@ async fn payments_create_core_adyen_no_redirect() {
|
||||
payment_id: Some(api::PaymentIdType::PaymentIntentId(payment_id.clone())),
|
||||
merchant_id: Some(merchant_id.clone()),
|
||||
amount: Some(6540.into()),
|
||||
connector: None,
|
||||
currency: Some(api_enums::Currency::USD),
|
||||
capture_method: Some(api_enums::CaptureMethod::Automatic),
|
||||
amount_to_capture: Some(6540),
|
||||
|
||||
@ -15,7 +15,7 @@ pub struct PaymentAttempt {
|
||||
pub amount: i64,
|
||||
pub currency: Option<storage_enums::Currency>,
|
||||
pub save_to_locker: Option<bool>,
|
||||
pub connector: Option<String>,
|
||||
pub connector: Option<serde_json::Value>,
|
||||
pub error_message: Option<String>,
|
||||
pub offer_amount: Option<i64>,
|
||||
pub surcharge_amount: Option<i64>,
|
||||
@ -59,7 +59,7 @@ pub struct PaymentAttemptNew {
|
||||
pub currency: Option<storage_enums::Currency>,
|
||||
// pub auto_capture: Option<bool>,
|
||||
pub save_to_locker: Option<bool>,
|
||||
pub connector: Option<String>,
|
||||
pub connector: Option<serde_json::Value>,
|
||||
pub error_message: Option<String>,
|
||||
pub offer_amount: Option<i64>,
|
||||
pub surcharge_amount: Option<i64>,
|
||||
@ -105,7 +105,7 @@ pub enum PaymentAttemptUpdate {
|
||||
},
|
||||
UpdateTrackers {
|
||||
payment_token: Option<String>,
|
||||
connector: Option<String>,
|
||||
connector: Option<serde_json::Value>,
|
||||
},
|
||||
AuthenticationTypeUpdate {
|
||||
authentication_type: storage_enums::AuthenticationType,
|
||||
@ -117,7 +117,7 @@ pub enum PaymentAttemptUpdate {
|
||||
authentication_type: Option<storage_enums::AuthenticationType>,
|
||||
payment_method: Option<storage_enums::PaymentMethod>,
|
||||
browser_info: Option<serde_json::Value>,
|
||||
connector: Option<String>,
|
||||
connector: Option<serde_json::Value>,
|
||||
payment_token: Option<String>,
|
||||
payment_method_data: Option<serde_json::Value>,
|
||||
payment_method_type: Option<storage_enums::PaymentMethodType>,
|
||||
@ -129,7 +129,7 @@ pub enum PaymentAttemptUpdate {
|
||||
},
|
||||
ResponseUpdate {
|
||||
status: storage_enums::AttemptStatus,
|
||||
connector: Option<String>,
|
||||
connector: Option<serde_json::Value>,
|
||||
connector_transaction_id: Option<String>,
|
||||
authentication_type: Option<storage_enums::AuthenticationType>,
|
||||
payment_method_id: Option<Option<String>>,
|
||||
@ -140,7 +140,7 @@ pub enum PaymentAttemptUpdate {
|
||||
status: storage_enums::AttemptStatus,
|
||||
},
|
||||
ErrorUpdate {
|
||||
connector: Option<String>,
|
||||
connector: Option<serde_json::Value>,
|
||||
status: storage_enums::AttemptStatus,
|
||||
error_code: Option<String>,
|
||||
error_message: Option<String>,
|
||||
@ -154,7 +154,7 @@ pub struct PaymentAttemptUpdateInternal {
|
||||
currency: Option<storage_enums::Currency>,
|
||||
status: Option<storage_enums::AttemptStatus>,
|
||||
connector_transaction_id: Option<String>,
|
||||
connector: Option<String>,
|
||||
connector: Option<serde_json::Value>,
|
||||
authentication_type: Option<storage_enums::AuthenticationType>,
|
||||
payment_method: Option<storage_enums::PaymentMethod>,
|
||||
error_message: Option<String>,
|
||||
|
||||
@ -211,7 +211,7 @@ diesel::table! {
|
||||
amount -> Int8,
|
||||
currency -> Nullable<Currency>,
|
||||
save_to_locker -> Nullable<Bool>,
|
||||
connector -> Nullable<Varchar>,
|
||||
connector -> Nullable<Jsonb>,
|
||||
error_message -> Nullable<Text>,
|
||||
offer_amount -> Nullable<Int8>,
|
||||
surcharge_amount -> Nullable<Int8>,
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
-- Alter column type to varchar(64) and extract and set the connector
|
||||
-- name field from the json.
|
||||
ALTER TABLE payment_attempt
|
||||
ALTER COLUMN connector TYPE VARCHAR(64)
|
||||
USING connector->>'routed_through';
|
||||
@ -0,0 +1,8 @@
|
||||
-- Alter column type to json
|
||||
-- as well as the connector.
|
||||
ALTER TABLE payment_attempt
|
||||
ALTER COLUMN connector TYPE JSONB
|
||||
USING jsonb_build_object(
|
||||
'routed_through', connector,
|
||||
'algorithm', NULL
|
||||
);
|
||||
Reference in New Issue
Block a user