mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 05:17:02 +08:00
feat(core): added multiple payment_attempt support for payment_intent (#439)
Co-authored-by: Nishant Joshi <nishant.joshi@juspay.in>
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -4369,9 +4369,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.19.7"
|
version = "0.19.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274"
|
checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
@ -5024,9 +5024,9 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.3.6"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966"
|
checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -193,7 +193,7 @@ async fn drainer(
|
|||||||
}
|
}
|
||||||
kv::Updateable::PaymentAttemptUpdate(a) => {
|
kv::Updateable::PaymentAttemptUpdate(a) => {
|
||||||
macro_util::handle_resp!(
|
macro_util::handle_resp!(
|
||||||
a.orig.update(&conn, a.update_data).await,
|
a.orig.update_with_attempt_id(&conn, a.update_data).await,
|
||||||
update_op,
|
update_op,
|
||||||
payment_attempt
|
payment_attempt
|
||||||
)
|
)
|
||||||
|
|||||||
@ -880,7 +880,7 @@ impl<F, T>
|
|||||||
.transaction_id
|
.transaction_id
|
||||||
.map_or(response.order_id, Some) // For paypal there will be no transaction_id, only order_id will be present
|
.map_or(response.order_id, Some) // For paypal there will be no transaction_id, only order_id will be present
|
||||||
.map(types::ResponseId::ConnectorTransactionId)
|
.map(types::ResponseId::ConnectorTransactionId)
|
||||||
.ok_or_else(|| errors::ConnectorError::MissingConnectorTransactionID)?,
|
.ok_or(errors::ConnectorError::MissingConnectorTransactionID)?,
|
||||||
redirection_data,
|
redirection_data,
|
||||||
mandate_reference: None,
|
mandate_reference: None,
|
||||||
// we don't need to save session token for capture, void flow so ignoring if it is not present
|
// we don't need to save session token for capture, void flow so ignoring if it is not present
|
||||||
|
|||||||
@ -817,9 +817,10 @@ pub async fn list_payment_methods(
|
|||||||
let payment_attempt = payment_intent
|
let payment_attempt = payment_intent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.async_map(|pi| async {
|
.async_map(|pi| async {
|
||||||
db.find_payment_attempt_by_payment_id_merchant_id(
|
db.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&pi.payment_id,
|
&pi.payment_id,
|
||||||
&pi.merchant_id,
|
&pi.merchant_id,
|
||||||
|
&pi.active_attempt_id,
|
||||||
merchant_account.storage_scheme,
|
merchant_account.storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -6,7 +6,6 @@ pub mod transformers;
|
|||||||
|
|
||||||
use std::{fmt::Debug, marker::PhantomData, time::Instant};
|
use std::{fmt::Debug, marker::PhantomData, time::Instant};
|
||||||
|
|
||||||
use common_utils::ext_traits::AsyncExt;
|
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
@ -127,17 +126,29 @@ where
|
|||||||
|
|
||||||
payment_data = match connector_details {
|
payment_data = match connector_details {
|
||||||
api::ConnectorCallType::Single(connector) => {
|
api::ConnectorCallType::Single(connector) => {
|
||||||
call_connector_service(
|
let router_data = call_connector_service(
|
||||||
state,
|
state,
|
||||||
&merchant_account,
|
&merchant_account,
|
||||||
&validate_result.payment_id,
|
|
||||||
connector,
|
connector,
|
||||||
&operation,
|
&operation,
|
||||||
payment_data,
|
&payment_data,
|
||||||
&customer,
|
&customer,
|
||||||
call_connector_action,
|
call_connector_action,
|
||||||
)
|
)
|
||||||
.await?
|
.await?;
|
||||||
|
|
||||||
|
let operation = Box::new(PaymentResponse);
|
||||||
|
let db = &*state.store;
|
||||||
|
operation
|
||||||
|
.to_post_update_tracker()?
|
||||||
|
.update_tracker(
|
||||||
|
db,
|
||||||
|
&validate_result.payment_id,
|
||||||
|
payment_data,
|
||||||
|
router_data,
|
||||||
|
merchant_account.storage_scheme,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
}
|
}
|
||||||
|
|
||||||
api::ConnectorCallType::Multiple(connectors) => {
|
api::ConnectorCallType::Multiple(connectors) => {
|
||||||
@ -367,13 +378,12 @@ impl PaymentRedirectFlow for PaymentRedirectSync {
|
|||||||
pub async fn call_connector_service<F, Op, Req>(
|
pub async fn call_connector_service<F, Op, Req>(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
merchant_account: &storage::MerchantAccount,
|
merchant_account: &storage::MerchantAccount,
|
||||||
payment_id: &api::PaymentIdType,
|
|
||||||
connector: api::ConnectorData,
|
connector: api::ConnectorData,
|
||||||
_operation: &Op,
|
_operation: &Op,
|
||||||
payment_data: PaymentData<F>,
|
payment_data: &PaymentData<F>,
|
||||||
customer: &Option<storage::Customer>,
|
customer: &Option<storage::Customer>,
|
||||||
call_connector_action: CallConnectorAction,
|
call_connector_action: CallConnectorAction,
|
||||||
) -> RouterResult<PaymentData<F>>
|
) -> RouterResult<types::RouterData<F, Req, types::PaymentsResponseData>>
|
||||||
where
|
where
|
||||||
Op: Debug + Sync,
|
Op: Debug + Sync,
|
||||||
F: Send + Clone,
|
F: Send + Clone,
|
||||||
@ -388,8 +398,6 @@ where
|
|||||||
// To perform router related operation for PaymentResponse
|
// To perform router related operation for PaymentResponse
|
||||||
PaymentResponse: Operation<F, Req>,
|
PaymentResponse: Operation<F, Req>,
|
||||||
{
|
{
|
||||||
let db = &*state.store;
|
|
||||||
|
|
||||||
let stime_connector = Instant::now();
|
let stime_connector = Instant::now();
|
||||||
|
|
||||||
let mut router_data = payment_data
|
let mut router_data = payment_data
|
||||||
@ -420,28 +428,11 @@ where
|
|||||||
Ok(router_data)
|
Ok(router_data)
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = router_data_res
|
|
||||||
.async_and_then(|response| async {
|
|
||||||
let operation = helpers::response_operation::<F, Req>();
|
|
||||||
let payment_data = operation
|
|
||||||
.to_post_update_tracker()?
|
|
||||||
.update_tracker(
|
|
||||||
db,
|
|
||||||
payment_id,
|
|
||||||
payment_data,
|
|
||||||
response,
|
|
||||||
merchant_account.storage_scheme,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(payment_data)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let etime_connector = Instant::now();
|
let etime_connector = Instant::now();
|
||||||
let duration_connector = etime_connector.saturating_duration_since(stime_connector);
|
let duration_connector = etime_connector.saturating_duration_since(stime_connector);
|
||||||
tracing::info!(duration = format!("Duration taken: {}", duration_connector.as_millis()));
|
tracing::info!(duration = format!("Duration taken: {}", duration_connector.as_millis()));
|
||||||
|
|
||||||
Ok(response)
|
router_data_res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn call_multiple_connectors_service<F, Op, Req>(
|
pub async fn call_multiple_connectors_service<F, Op, Req>(
|
||||||
@ -668,9 +659,10 @@ pub async fn list_payments(
|
|||||||
let pi = futures::stream::iter(payment_intents)
|
let pi = futures::stream::iter(payment_intents)
|
||||||
.filter_map(|pi| async {
|
.filter_map(|pi| async {
|
||||||
let pa = db
|
let pa = db
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&pi.payment_id,
|
&pi.payment_id,
|
||||||
merchant_id,
|
merchant_id,
|
||||||
|
&pi.active_attempt_id,
|
||||||
// since OLAP doesn't have KV. Force to get the data from PSQL.
|
// since OLAP doesn't have KV. Force to get the data from PSQL.
|
||||||
storage_enums::MerchantStorageScheme::PostgresOnly,
|
storage_enums::MerchantStorageScheme::PostgresOnly,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -56,9 +56,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsCancelRequest>
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut payment_attempt = db
|
let mut payment_attempt = db
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_id,
|
payment_intent.payment_id.as_str(),
|
||||||
merchant_id,
|
merchant_id,
|
||||||
|
payment_intent.active_attempt_id.as_str(),
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -175,7 +176,7 @@ impl<F: Clone> UpdateTracker<F, PaymentData<F>, api::PaymentsCancelRequest> for
|
|||||||
{
|
{
|
||||||
let cancellation_reason = payment_data.payment_attempt.cancellation_reason.clone();
|
let cancellation_reason = payment_data.payment_attempt.cancellation_reason.clone();
|
||||||
payment_data.payment_attempt = db
|
payment_data.payment_attempt = db
|
||||||
.update_payment_attempt(
|
.update_payment_attempt_with_attempt_id(
|
||||||
payment_data.payment_attempt,
|
payment_data.payment_attempt,
|
||||||
storage::PaymentAttemptUpdate::VoidUpdate {
|
storage::PaymentAttemptUpdate::VoidUpdate {
|
||||||
status: enums::AttemptStatus::VoidInitiated,
|
status: enums::AttemptStatus::VoidInitiated,
|
||||||
|
|||||||
@ -63,9 +63,10 @@ impl<F: Send + Clone> GetTracker<F, payments::PaymentData<F>, api::PaymentsCaptu
|
|||||||
helpers::validate_amount_to_capture(payment_intent.amount, request.amount_to_capture)?;
|
helpers::validate_amount_to_capture(payment_intent.amount, request.amount_to_capture)?;
|
||||||
|
|
||||||
payment_attempt = db
|
payment_attempt = db
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_id,
|
payment_intent.payment_id.as_str(),
|
||||||
merchant_id,
|
merchant_id,
|
||||||
|
payment_intent.active_attempt_id.as_str(),
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -94,9 +94,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Co
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
payment_attempt = db
|
payment_attempt = db
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_id,
|
&payment_intent.payment_id,
|
||||||
merchant_id,
|
merchant_id,
|
||||||
|
&payment_intent.active_attempt_id,
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -96,9 +96,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
payment_attempt = db
|
payment_attempt = db
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_id,
|
payment_intent.payment_id.as_str(),
|
||||||
merchant_id,
|
merchant_id,
|
||||||
|
payment_intent.active_attempt_id.as_str(),
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -338,7 +339,7 @@ impl<F: Clone> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for Paymen
|
|||||||
.attach_printable("Failed to encode additional pm data")?;
|
.attach_printable("Failed to encode additional pm data")?;
|
||||||
|
|
||||||
payment_data.payment_attempt = db
|
payment_data.payment_attempt = db
|
||||||
.update_payment_attempt(
|
.update_payment_attempt_with_attempt_id(
|
||||||
payment_data.payment_attempt,
|
payment_data.payment_attempt,
|
||||||
storage::PaymentAttemptUpdate::ConfirmUpdate {
|
storage::PaymentAttemptUpdate::ConfirmUpdate {
|
||||||
amount: payment_data.amount.into(),
|
amount: payment_data.amount.into(),
|
||||||
|
|||||||
@ -127,6 +127,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
|||||||
request,
|
request,
|
||||||
shipping_address.clone().map(|x| x.address_id),
|
shipping_address.clone().map(|x| x.address_id),
|
||||||
billing_address.clone().map(|x| x.address_id),
|
billing_address.clone().map(|x| x.address_id),
|
||||||
|
payment_attempt.attempt_id.to_owned(),
|
||||||
)?,
|
)?,
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
@ -316,7 +317,7 @@ impl<F: Clone> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for Paymen
|
|||||||
let connector = payment_data.payment_attempt.connector.clone();
|
let connector = payment_data.payment_attempt.connector.clone();
|
||||||
|
|
||||||
payment_data.payment_attempt = db
|
payment_data.payment_attempt = db
|
||||||
.update_payment_attempt(
|
.update_payment_attempt_with_attempt_id(
|
||||||
payment_data.payment_attempt,
|
payment_data.payment_attempt,
|
||||||
storage::PaymentAttemptUpdate::UpdateTrackers {
|
storage::PaymentAttemptUpdate::UpdateTrackers {
|
||||||
payment_token,
|
payment_token,
|
||||||
@ -479,6 +480,7 @@ impl PaymentCreate {
|
|||||||
request: &api::PaymentsRequest,
|
request: &api::PaymentsRequest,
|
||||||
shipping_address_id: Option<String>,
|
shipping_address_id: Option<String>,
|
||||||
billing_address_id: Option<String>,
|
billing_address_id: Option<String>,
|
||||||
|
active_attempt_id: String,
|
||||||
) -> RouterResult<storage::PaymentIntentNew> {
|
) -> RouterResult<storage::PaymentIntentNew> {
|
||||||
let created_at @ modified_at @ last_synced = Some(common_utils::date_time::now());
|
let created_at @ modified_at @ last_synced = Some(common_utils::date_time::now());
|
||||||
let status =
|
let status =
|
||||||
@ -512,6 +514,7 @@ impl PaymentCreate {
|
|||||||
statement_descriptor_name: request.statement_descriptor_name.clone(),
|
statement_descriptor_name: request.statement_descriptor_name.clone(),
|
||||||
statement_descriptor_suffix: request.statement_descriptor_suffix.clone(),
|
statement_descriptor_suffix: request.statement_descriptor_suffix.clone(),
|
||||||
metadata: metadata.map(masking::Secret::new),
|
metadata: metadata.map(masking::Secret::new),
|
||||||
|
active_attempt_id,
|
||||||
..storage::PaymentIntentNew::default()
|
..storage::PaymentIntentNew::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,7 +106,12 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::VerifyRequest> for Paym
|
|||||||
|
|
||||||
payment_intent = match db
|
payment_intent = match db
|
||||||
.insert_payment_intent(
|
.insert_payment_intent(
|
||||||
Self::make_payment_intent(&payment_id, merchant_id, request),
|
Self::make_payment_intent(
|
||||||
|
&payment_id,
|
||||||
|
merchant_id,
|
||||||
|
request,
|
||||||
|
payment_attempt.attempt_id.to_owned(),
|
||||||
|
),
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -312,6 +317,7 @@ impl PaymentMethodValidate {
|
|||||||
payment_id: &str,
|
payment_id: &str,
|
||||||
merchant_id: &str,
|
merchant_id: &str,
|
||||||
request: &api::VerifyRequest,
|
request: &api::VerifyRequest,
|
||||||
|
active_attempt_id: String,
|
||||||
) -> storage::PaymentIntentNew {
|
) -> storage::PaymentIntentNew {
|
||||||
let created_at @ modified_at @ last_synced = Some(date_time::now());
|
let created_at @ modified_at @ last_synced = Some(date_time::now());
|
||||||
let status = helpers::payment_intent_status_fsm(&request.payment_method_data, Some(true));
|
let status = helpers::payment_intent_status_fsm(&request.payment_method_data, Some(true));
|
||||||
@ -331,6 +337,7 @@ impl PaymentMethodValidate {
|
|||||||
client_secret: Some(client_secret),
|
client_secret: Some(client_secret),
|
||||||
setup_future_usage: request.setup_future_usage.map(ForeignInto::foreign_into),
|
setup_future_usage: request.setup_future_usage.map(ForeignInto::foreign_into),
|
||||||
off_session: request.off_session,
|
off_session: request.off_session,
|
||||||
|
active_attempt_id,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -361,7 +361,7 @@ async fn payment_response_update_tracker<F: Clone, T>(
|
|||||||
|
|
||||||
payment_data.payment_attempt = match payment_attempt_update {
|
payment_data.payment_attempt = match payment_attempt_update {
|
||||||
Some(payment_attempt_update) => db
|
Some(payment_attempt_update) => db
|
||||||
.update_payment_attempt(
|
.update_payment_attempt_with_attempt_id(
|
||||||
payment_data.payment_attempt,
|
payment_data.payment_attempt,
|
||||||
payment_attempt_update,
|
payment_attempt_update,
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
|
|||||||
@ -71,9 +71,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsSessionRequest>
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut payment_attempt = db
|
let mut payment_attempt = db
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_id,
|
payment_intent.payment_id.as_str(),
|
||||||
merchant_id,
|
merchant_id,
|
||||||
|
payment_intent.active_attempt_id.as_str(),
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -68,9 +68,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsStartRequest> f
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
payment_attempt = db
|
payment_attempt = db
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_id,
|
payment_intent.payment_id.as_str(),
|
||||||
merchant_id,
|
merchant_id,
|
||||||
|
payment_intent.active_attempt_id.as_str(),
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -191,27 +191,11 @@ async fn get_tracker_for_sync<
|
|||||||
)> {
|
)> {
|
||||||
let (payment_intent, payment_attempt, currency, amount);
|
let (payment_intent, payment_attempt, currency, amount);
|
||||||
|
|
||||||
payment_attempt = match payment_id {
|
(payment_intent, payment_attempt) =
|
||||||
api::PaymentIdType::PaymentIntentId(ref id) => {
|
get_payment_intent_payment_attempt(db, payment_id, merchant_id, storage_scheme).await?;
|
||||||
db.find_payment_attempt_by_payment_id_merchant_id(id, merchant_id, storage_scheme)
|
|
||||||
}
|
|
||||||
api::PaymentIdType::ConnectorTransactionId(ref id) => {
|
|
||||||
db.find_payment_attempt_by_merchant_id_connector_txn_id(merchant_id, id, storage_scheme)
|
|
||||||
}
|
|
||||||
api::PaymentIdType::PaymentAttemptId(ref id) => {
|
|
||||||
db.find_payment_attempt_by_merchant_id_attempt_id(merchant_id, id, storage_scheme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.await
|
|
||||||
.map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound))?;
|
|
||||||
|
|
||||||
let payment_id_str = payment_attempt.payment_id.clone();
|
let payment_id_str = payment_attempt.payment_id.clone();
|
||||||
|
|
||||||
payment_intent = db
|
|
||||||
.find_payment_intent_by_payment_id_merchant_id(&payment_id_str, merchant_id, storage_scheme)
|
|
||||||
.await
|
|
||||||
.map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound))?;
|
|
||||||
|
|
||||||
let mut connector_response = db
|
let mut connector_response = db
|
||||||
.find_connector_response_by_payment_id_merchant_id_attempt_id(
|
.find_connector_response_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_intent.payment_id,
|
&payment_intent.payment_id,
|
||||||
@ -319,3 +303,63 @@ impl<F: Send + Clone> ValidateRequest<F, api::PaymentsRetrieveRequest> for Payme
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub async fn get_payment_intent_payment_attempt(
|
||||||
|
db: &dyn StorageInterface,
|
||||||
|
payment_id: &api::PaymentIdType,
|
||||||
|
merchant_id: &str,
|
||||||
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> RouterResult<(storage::PaymentIntent, storage::PaymentAttempt)> {
|
||||||
|
(|| async {
|
||||||
|
let (pi, pa);
|
||||||
|
match payment_id {
|
||||||
|
api_models::payments::PaymentIdType::PaymentIntentId(ref id) => {
|
||||||
|
pi = db
|
||||||
|
.find_payment_intent_by_payment_id_merchant_id(id, merchant_id, storage_scheme)
|
||||||
|
.await?;
|
||||||
|
pa = db
|
||||||
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
|
pi.payment_id.as_str(),
|
||||||
|
merchant_id,
|
||||||
|
pi.active_attempt_id.as_str(),
|
||||||
|
storage_scheme,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
api_models::payments::PaymentIdType::ConnectorTransactionId(ref id) => {
|
||||||
|
pa = db
|
||||||
|
.find_payment_attempt_by_merchant_id_connector_txn_id(
|
||||||
|
merchant_id,
|
||||||
|
id,
|
||||||
|
storage_scheme,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
pi = db
|
||||||
|
.find_payment_intent_by_payment_id_merchant_id(
|
||||||
|
pa.payment_id.as_str(),
|
||||||
|
merchant_id,
|
||||||
|
storage_scheme,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
api_models::payments::PaymentIdType::PaymentAttemptId(ref id) => {
|
||||||
|
pa = db
|
||||||
|
.find_payment_attempt_by_attempt_id_merchant_id(id, merchant_id, storage_scheme)
|
||||||
|
.await?;
|
||||||
|
pi = db
|
||||||
|
.find_payment_intent_by_payment_id_merchant_id(
|
||||||
|
pa.payment_id.as_str(),
|
||||||
|
merchant_id,
|
||||||
|
storage_scheme,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((pi, pa))
|
||||||
|
})()
|
||||||
|
.await
|
||||||
|
.map_err(|error: error_stack::Report<errors::StorageError>| {
|
||||||
|
error.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -87,10 +87,18 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
payment_intent = db
|
||||||
|
.find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme)
|
||||||
|
.await
|
||||||
|
.map_err(|error| {
|
||||||
|
error.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
|
||||||
|
})?;
|
||||||
|
|
||||||
payment_attempt = db
|
payment_attempt = db
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_id,
|
payment_intent.payment_id.as_str(),
|
||||||
merchant_id,
|
merchant_id,
|
||||||
|
payment_intent.active_attempt_id.as_str(),
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -364,7 +372,7 @@ impl<F: Clone> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for Paymen
|
|||||||
let payment_method_type = payment_data.payment_attempt.payment_method_type.clone();
|
let payment_method_type = payment_data.payment_attempt.payment_method_type.clone();
|
||||||
let payment_experience = payment_data.payment_attempt.payment_experience.clone();
|
let payment_experience = payment_data.payment_attempt.payment_experience.clone();
|
||||||
payment_data.payment_attempt = db
|
payment_data.payment_attempt = db
|
||||||
.update_payment_attempt(
|
.update_payment_attempt_with_attempt_id(
|
||||||
payment_data.payment_attempt,
|
payment_data.payment_attempt,
|
||||||
storage::PaymentAttemptUpdate::Update {
|
storage::PaymentAttemptUpdate::Update {
|
||||||
amount: payment_data.amount.into(),
|
amount: payment_data.amount.into(),
|
||||||
|
|||||||
@ -37,26 +37,6 @@ pub async fn refund_create_core(
|
|||||||
|
|
||||||
merchant_id = &merchant_account.merchant_id;
|
merchant_id = &merchant_account.merchant_id;
|
||||||
|
|
||||||
payment_attempt = db
|
|
||||||
.find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id(
|
|
||||||
&req.payment_id,
|
|
||||||
merchant_id,
|
|
||||||
merchant_account.storage_scheme,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::SuccessfulPaymentNotFound)?;
|
|
||||||
|
|
||||||
// Amount is not passed in request refer from payment attempt.
|
|
||||||
amount = req.amount.unwrap_or(payment_attempt.amount); // [#298]: Need to that capture amount
|
|
||||||
//[#299]: Can we change the flow based on some workflow idea
|
|
||||||
utils::when(amount <= 0, || {
|
|
||||||
Err(report!(errors::ApiErrorResponse::InvalidDataFormat {
|
|
||||||
field_name: "amount".to_string(),
|
|
||||||
expected_format: "positive integer".to_string()
|
|
||||||
})
|
|
||||||
.attach_printable("amount less than zero"))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
payment_intent = db
|
payment_intent = db
|
||||||
.find_payment_intent_by_payment_id_merchant_id(
|
.find_payment_intent_by_payment_id_merchant_id(
|
||||||
&req.payment_id,
|
&req.payment_id,
|
||||||
@ -74,6 +54,32 @@ pub async fn refund_create_core(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Amount is not passed in request refer from payment attempt.
|
||||||
|
amount = req.amount.unwrap_or(
|
||||||
|
payment_intent
|
||||||
|
.amount_captured
|
||||||
|
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.into_report()
|
||||||
|
.attach_printable("amount captured is none in a successful payment")?,
|
||||||
|
);
|
||||||
|
//[#299]: Can we change the flow based on some workflow idea
|
||||||
|
utils::when(amount <= 0, || {
|
||||||
|
Err(report!(errors::ApiErrorResponse::InvalidDataFormat {
|
||||||
|
field_name: "amount".to_string(),
|
||||||
|
expected_format: "positive integer".to_string()
|
||||||
|
})
|
||||||
|
.attach_printable("amount less than zero"))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
payment_attempt = db
|
||||||
|
.find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id(
|
||||||
|
&req.payment_id,
|
||||||
|
merchant_id,
|
||||||
|
merchant_account.storage_scheme,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::SuccessfulPaymentNotFound)?;
|
||||||
|
|
||||||
let creds_identifier = req
|
let creds_identifier = req
|
||||||
.merchant_connector_details
|
.merchant_connector_details
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|||||||
@ -214,9 +214,9 @@ async fn get_payment_attempt_from_object_reference_id(
|
|||||||
.await
|
.await
|
||||||
.change_context(errors::WebhooksFlowError::ResourceNotFound),
|
.change_context(errors::WebhooksFlowError::ResourceNotFound),
|
||||||
api::ObjectReferenceId::PaymentId(api::PaymentIdType::PaymentAttemptId(ref id)) => db
|
api::ObjectReferenceId::PaymentId(api::PaymentIdType::PaymentAttemptId(ref id)) => db
|
||||||
.find_payment_attempt_by_merchant_id_attempt_id(
|
.find_payment_attempt_by_attempt_id_merchant_id(
|
||||||
&merchant_account.merchant_id,
|
|
||||||
id,
|
id,
|
||||||
|
&merchant_account.merchant_id,
|
||||||
merchant_account.storage_scheme,
|
merchant_account.storage_scheme,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -12,20 +12,13 @@ pub trait PaymentAttemptInterface {
|
|||||||
storage_scheme: enums::MerchantStorageScheme,
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
||||||
|
|
||||||
async fn update_payment_attempt(
|
async fn update_payment_attempt_with_attempt_id(
|
||||||
&self,
|
&self,
|
||||||
this: types::PaymentAttempt,
|
this: types::PaymentAttempt,
|
||||||
payment_attempt: types::PaymentAttemptUpdate,
|
payment_attempt: types::PaymentAttemptUpdate,
|
||||||
storage_scheme: enums::MerchantStorageScheme,
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
||||||
|
|
||||||
async fn find_payment_attempt_by_payment_id_merchant_id(
|
|
||||||
&self,
|
|
||||||
payment_id: &str,
|
|
||||||
merchant_id: &str,
|
|
||||||
storage_scheme: enums::MerchantStorageScheme,
|
|
||||||
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
|
||||||
|
|
||||||
async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
|
async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
|
||||||
&self,
|
&self,
|
||||||
connector_transaction_id: &str,
|
connector_transaction_id: &str,
|
||||||
@ -48,12 +41,20 @@ pub trait PaymentAttemptInterface {
|
|||||||
storage_scheme: enums::MerchantStorageScheme,
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
||||||
|
|
||||||
async fn find_payment_attempt_by_merchant_id_attempt_id(
|
async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&self,
|
&self,
|
||||||
|
payment_id: &str,
|
||||||
merchant_id: &str,
|
merchant_id: &str,
|
||||||
attempt_id: &str,
|
attempt_id: &str,
|
||||||
storage_scheme: enums::MerchantStorageScheme,
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
||||||
|
|
||||||
|
async fn find_payment_attempt_by_attempt_id_merchant_id(
|
||||||
|
&self,
|
||||||
|
attempt_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "kv_store"))]
|
#[cfg(not(feature = "kv_store"))]
|
||||||
@ -83,27 +84,14 @@ mod storage {
|
|||||||
.into_report()
|
.into_report()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_payment_attempt(
|
async fn update_payment_attempt_with_attempt_id(
|
||||||
&self,
|
&self,
|
||||||
this: PaymentAttempt,
|
this: PaymentAttempt,
|
||||||
payment_attempt: PaymentAttemptUpdate,
|
payment_attempt: PaymentAttemptUpdate,
|
||||||
_storage_scheme: enums::MerchantStorageScheme,
|
_storage_scheme: enums::MerchantStorageScheme,
|
||||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||||
let conn = connection::pg_connection_write(self).await?;
|
let conn = connection::pg_connection_write(&self).await?;
|
||||||
this.update(&conn, payment_attempt)
|
this.update_with_attempt_id(&conn, payment_attempt)
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
.into_report()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn find_payment_attempt_by_payment_id_merchant_id(
|
|
||||||
&self,
|
|
||||||
payment_id: &str,
|
|
||||||
merchant_id: &str,
|
|
||||||
_storage_scheme: enums::MerchantStorageScheme,
|
|
||||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
|
||||||
let conn = connection::pg_connection_read(self).await?;
|
|
||||||
PaymentAttempt::find_by_payment_id_merchant_id(&conn, payment_id, merchant_id)
|
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.into_report()
|
.into_report()
|
||||||
@ -162,7 +150,26 @@ mod storage {
|
|||||||
.into_report()
|
.into_report()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_payment_attempt_by_merchant_id_attempt_id(
|
async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
|
&self,
|
||||||
|
payment_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
attempt_id: &str,
|
||||||
|
_storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||||
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
|
|
||||||
|
PaymentAttempt::find_by_payment_id_merchant_id_attempt_id(
|
||||||
|
&conn,
|
||||||
|
payment_id,
|
||||||
|
merchant_id,
|
||||||
|
attempt_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
.into_report()
|
||||||
|
}
|
||||||
|
async fn find_payment_attempt_by_attempt_id_merchant_id(
|
||||||
&self,
|
&self,
|
||||||
merchant_id: &str,
|
merchant_id: &str,
|
||||||
attempt_id: &str,
|
attempt_id: &str,
|
||||||
@ -180,8 +187,9 @@ mod storage {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl PaymentAttemptInterface for MockDb {
|
impl PaymentAttemptInterface for MockDb {
|
||||||
async fn find_payment_attempt_by_merchant_id_attempt_id(
|
async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&self,
|
&self,
|
||||||
|
_payment_id: &str,
|
||||||
_merchant_id: &str,
|
_merchant_id: &str,
|
||||||
_attempt_id: &str,
|
_attempt_id: &str,
|
||||||
_storage_scheme: enums::MerchantStorageScheme,
|
_storage_scheme: enums::MerchantStorageScheme,
|
||||||
@ -190,6 +198,16 @@ impl PaymentAttemptInterface for MockDb {
|
|||||||
Err(errors::StorageError::MockDbError)?
|
Err(errors::StorageError::MockDbError)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn find_payment_attempt_by_attempt_id_merchant_id(
|
||||||
|
&self,
|
||||||
|
_attempt_id: &str,
|
||||||
|
_merchant_id: &str,
|
||||||
|
_storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> CustomResult<types::PaymentAttempt, errors::StorageError> {
|
||||||
|
// [#172]: Implement function for `MockDb`
|
||||||
|
Err(errors::StorageError::MockDbError)?
|
||||||
|
}
|
||||||
|
|
||||||
async fn find_payment_attempt_by_merchant_id_connector_txn_id(
|
async fn find_payment_attempt_by_merchant_id_connector_txn_id(
|
||||||
&self,
|
&self,
|
||||||
_merchant_id: &str,
|
_merchant_id: &str,
|
||||||
@ -252,7 +270,7 @@ impl PaymentAttemptInterface for MockDb {
|
|||||||
|
|
||||||
// safety: only used for testing
|
// safety: only used for testing
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
async fn update_payment_attempt(
|
async fn update_payment_attempt_with_attempt_id(
|
||||||
&self,
|
&self,
|
||||||
this: types::PaymentAttempt,
|
this: types::PaymentAttempt,
|
||||||
payment_attempt: types::PaymentAttemptUpdate,
|
payment_attempt: types::PaymentAttemptUpdate,
|
||||||
@ -262,7 +280,7 @@ impl PaymentAttemptInterface for MockDb {
|
|||||||
|
|
||||||
let item = payment_attempts
|
let item = payment_attempts
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|item| item.id == this.id)
|
.find(|item| item.attempt_id == this.attempt_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
*item = payment_attempt.apply_changeset(this);
|
*item = payment_attempt.apply_changeset(this);
|
||||||
@ -270,16 +288,6 @@ impl PaymentAttemptInterface for MockDb {
|
|||||||
Ok(item.clone())
|
Ok(item.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_payment_attempt_by_payment_id_merchant_id(
|
|
||||||
&self,
|
|
||||||
_payment_id: &str,
|
|
||||||
_merchant_id: &str,
|
|
||||||
_storage_scheme: enums::MerchantStorageScheme,
|
|
||||||
) -> CustomResult<types::PaymentAttempt, errors::StorageError> {
|
|
||||||
// [#172]: Implement function for `MockDb`
|
|
||||||
Err(errors::StorageError::MockDbError)?
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
|
async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
|
||||||
&self,
|
&self,
|
||||||
_connector_transaction_id: &str,
|
_connector_transaction_id: &str,
|
||||||
@ -317,6 +325,7 @@ mod storage {
|
|||||||
use common_utils::date_time;
|
use common_utils::date_time;
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use redis_interface::HsetnxReply;
|
use redis_interface::HsetnxReply;
|
||||||
|
use storage_models::reverse_lookup::ReverseLookup;
|
||||||
|
|
||||||
use super::PaymentAttemptInterface;
|
use super::PaymentAttemptInterface;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -406,9 +415,7 @@ mod storage {
|
|||||||
ReverseLookupNew {
|
ReverseLookupNew {
|
||||||
lookup_id: format!(
|
lookup_id: format!(
|
||||||
"{}_{}",
|
"{}_{}",
|
||||||
&created_attempt.merchant_id,
|
&created_attempt.merchant_id, &created_attempt.attempt_id,
|
||||||
// [#439]: Change this to `attempt_id`
|
|
||||||
&created_attempt.payment_id,
|
|
||||||
),
|
),
|
||||||
pk_id: key,
|
pk_id: key,
|
||||||
sk_id: field,
|
sk_id: field,
|
||||||
@ -440,7 +447,7 @@ mod storage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_payment_attempt(
|
async fn update_payment_attempt_with_attempt_id(
|
||||||
&self,
|
&self,
|
||||||
this: PaymentAttempt,
|
this: PaymentAttempt,
|
||||||
payment_attempt: PaymentAttemptUpdate,
|
payment_attempt: PaymentAttemptUpdate,
|
||||||
@ -449,7 +456,7 @@ mod storage {
|
|||||||
match storage_scheme {
|
match storage_scheme {
|
||||||
enums::MerchantStorageScheme::PostgresOnly => {
|
enums::MerchantStorageScheme::PostgresOnly => {
|
||||||
let conn = connection::pg_connection_write(self).await?;
|
let conn = connection::pg_connection_write(self).await?;
|
||||||
this.update(&conn, payment_attempt)
|
this.update_with_attempt_id(&conn, payment_attempt)
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.into_report()
|
.into_report()
|
||||||
@ -465,33 +472,39 @@ mod storage {
|
|||||||
.change_context(errors::StorageError::KVError)?;
|
.change_context(errors::StorageError::KVError)?;
|
||||||
let field = format!("pa_{}", updated_attempt.attempt_id);
|
let field = format!("pa_{}", updated_attempt.attempt_id);
|
||||||
let updated_attempt = self
|
let updated_attempt = self
|
||||||
.redis_conn()
|
.redis_conn
|
||||||
.map_err(Into::<errors::StorageError>::into)?
|
|
||||||
.set_hash_fields(&key, (&field, &redis_value))
|
.set_hash_fields(&key, (&field, &redis_value))
|
||||||
.await
|
.await
|
||||||
.map(|_| updated_attempt)
|
.map(|_| updated_attempt)
|
||||||
.change_context(errors::StorageError::KVError)?;
|
.change_context(errors::StorageError::KVError)?;
|
||||||
|
|
||||||
let conn = connection::pg_connection_write(self).await?;
|
match (
|
||||||
// Reverse lookup for connector_transaction_id
|
|
||||||
if let (None, Some(connector_transaction_id)) = (
|
|
||||||
old_connector_transaction_id,
|
old_connector_transaction_id,
|
||||||
&updated_attempt.connector_transaction_id,
|
&updated_attempt.connector_transaction_id,
|
||||||
) {
|
) {
|
||||||
let field = format!("pa_{}", updated_attempt.attempt_id);
|
(None, Some(connector_transaction_id)) => {
|
||||||
ReverseLookupNew {
|
add_connector_txn_id_to_reverse_lookup(
|
||||||
lookup_id: format!(
|
self,
|
||||||
"{}_{}",
|
key.as_str(),
|
||||||
&updated_attempt.merchant_id, connector_transaction_id
|
this.merchant_id.as_str(),
|
||||||
),
|
updated_attempt.attempt_id.as_str(),
|
||||||
pk_id: key.clone(),
|
connector_transaction_id.as_str(),
|
||||||
sk_id: field.clone(),
|
)
|
||||||
source: "payment_attempt".to_string(),
|
.await?;
|
||||||
}
|
}
|
||||||
.insert(&conn)
|
(Some(old_connector_transaction_id), Some(connector_transaction_id)) => {
|
||||||
.await
|
if old_connector_transaction_id.ne(connector_transaction_id) {
|
||||||
.map_err(Into::<errors::StorageError>::into)
|
add_connector_txn_id_to_reverse_lookup(
|
||||||
.into_report()?;
|
self,
|
||||||
|
key.as_str(),
|
||||||
|
this.merchant_id.as_str(),
|
||||||
|
updated_attempt.attempt_id.as_str(),
|
||||||
|
connector_transaction_id.as_str(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(_, _) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let redis_entry = kv::TypedSql {
|
let redis_entry = kv::TypedSql {
|
||||||
@ -517,41 +530,6 @@ mod storage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_payment_attempt_by_payment_id_merchant_id(
|
|
||||||
&self,
|
|
||||||
payment_id: &str,
|
|
||||||
merchant_id: &str,
|
|
||||||
storage_scheme: enums::MerchantStorageScheme,
|
|
||||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
|
||||||
let database_call = || async {
|
|
||||||
let conn = connection::pg_connection_read(self).await?;
|
|
||||||
PaymentAttempt::find_by_payment_id_merchant_id(&conn, payment_id, merchant_id)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
.into_report()
|
|
||||||
};
|
|
||||||
match storage_scheme {
|
|
||||||
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
|
||||||
enums::MerchantStorageScheme::RedisKv => {
|
|
||||||
// [#439]: get the attempt_id from payment_intent
|
|
||||||
let key = format!("{merchant_id}_{payment_id}");
|
|
||||||
let lookup = self.get_lookup_by_lookup_id(&key).await?;
|
|
||||||
|
|
||||||
db_utils::try_redis_get_else_try_database_get(
|
|
||||||
self.redis_conn()
|
|
||||||
.map_err(Into::<errors::StorageError>::into)?
|
|
||||||
.get_hash_field_and_deserialize(
|
|
||||||
&lookup.pk_id,
|
|
||||||
&lookup.sk_id,
|
|
||||||
"PaymentAttempt",
|
|
||||||
),
|
|
||||||
database_call,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
|
async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
|
||||||
&self,
|
&self,
|
||||||
connector_transaction_id: &str,
|
connector_transaction_id: &str,
|
||||||
@ -594,21 +572,17 @@ mod storage {
|
|||||||
&self,
|
&self,
|
||||||
payment_id: &str,
|
payment_id: &str,
|
||||||
merchant_id: &str,
|
merchant_id: &str,
|
||||||
storage_scheme: enums::MerchantStorageScheme,
|
_storage_scheme: enums::MerchantStorageScheme,
|
||||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||||
self.find_payment_attempt_by_payment_id_merchant_id(
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
|
PaymentAttempt::find_last_successful_attempt_by_payment_id_merchant_id(
|
||||||
|
&conn,
|
||||||
payment_id,
|
payment_id,
|
||||||
merchant_id,
|
merchant_id,
|
||||||
storage_scheme,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.and_then(|attempt| match attempt.status {
|
.map_err(Into::into)
|
||||||
enums::AttemptStatus::Charged => Ok(attempt),
|
.into_report()
|
||||||
_ => Err(errors::StorageError::ValueNotFound(format!(
|
|
||||||
"Successful payment attempt does not exist for {payment_id}_{merchant_id}"
|
|
||||||
)))
|
|
||||||
.into_report(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_payment_attempt_by_merchant_id_connector_txn_id(
|
async fn find_payment_attempt_by_merchant_id_connector_txn_id(
|
||||||
@ -647,10 +621,10 @@ mod storage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_payment_attempt_by_merchant_id_attempt_id(
|
async fn find_payment_attempt_by_attempt_id_merchant_id(
|
||||||
&self,
|
&self,
|
||||||
merchant_id: &str,
|
|
||||||
attempt_id: &str,
|
attempt_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
storage_scheme: enums::MerchantStorageScheme,
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||||
let database_call = || async {
|
let database_call = || async {
|
||||||
@ -677,5 +651,64 @@ mod storage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
|
&self,
|
||||||
|
payment_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
attempt_id: &str,
|
||||||
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||||
|
let database_call = || async {
|
||||||
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
|
PaymentAttempt::find_by_payment_id_merchant_id_attempt_id(
|
||||||
|
&conn,
|
||||||
|
payment_id,
|
||||||
|
merchant_id,
|
||||||
|
attempt_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
.into_report()
|
||||||
|
};
|
||||||
|
match storage_scheme {
|
||||||
|
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
||||||
|
|
||||||
|
enums::MerchantStorageScheme::RedisKv => {
|
||||||
|
let lookup_id = format!("{merchant_id}_{attempt_id}");
|
||||||
|
let lookup = self.get_lookup_by_lookup_id(&lookup_id).await?;
|
||||||
|
let key = &lookup.pk_id;
|
||||||
|
db_utils::try_redis_get_else_try_database_get(
|
||||||
|
self.redis_conn()
|
||||||
|
.map_err(Into::<errors::StorageError>::into)?
|
||||||
|
.get_hash_field_and_deserialize(key, &lookup.sk_id, "PaymentAttempt"),
|
||||||
|
database_call,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
async fn add_connector_txn_id_to_reverse_lookup(
|
||||||
|
store: &Store,
|
||||||
|
key: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
updated_attempt_attempt_id: &str,
|
||||||
|
connector_transaction_id: &str,
|
||||||
|
) -> CustomResult<ReverseLookup, errors::StorageError> {
|
||||||
|
let conn = connection::pg_connection_write(store).await?;
|
||||||
|
let field = format!("pa_{}", updated_attempt_attempt_id);
|
||||||
|
ReverseLookupNew {
|
||||||
|
lookup_id: format!("{}_{}", merchant_id, connector_transaction_id),
|
||||||
|
pk_id: key.to_owned(),
|
||||||
|
sk_id: field.clone(),
|
||||||
|
source: "payment_attempt".to_string(),
|
||||||
|
}
|
||||||
|
.insert(&conn)
|
||||||
|
.await
|
||||||
|
.map_err(Into::<errors::StorageError>::into)
|
||||||
|
.into_report()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,6 +92,7 @@ mod storage {
|
|||||||
setup_future_usage: new.setup_future_usage,
|
setup_future_usage: new.setup_future_usage,
|
||||||
off_session: new.off_session,
|
off_session: new.off_session,
|
||||||
client_secret: new.client_secret.clone(),
|
client_secret: new.client_secret.clone(),
|
||||||
|
active_attempt_id: new.active_attempt_id.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match self
|
match self
|
||||||
@ -347,6 +348,7 @@ impl PaymentIntentInterface for MockDb {
|
|||||||
setup_future_usage: new.setup_future_usage,
|
setup_future_usage: new.setup_future_usage,
|
||||||
off_session: new.off_session,
|
off_session: new.off_session,
|
||||||
client_secret: new.client_secret,
|
client_secret: new.client_secret,
|
||||||
|
active_attempt_id: new.active_attempt_id.to_owned(),
|
||||||
};
|
};
|
||||||
payment_intents.push(payment_intent.clone());
|
payment_intents.push(payment_intent.clone());
|
||||||
Ok(payment_intent)
|
Ok(payment_intent)
|
||||||
|
|||||||
@ -1,279 +0,0 @@
|
|||||||
use error_stack::{FutureExt, IntoReport, ResultExt};
|
|
||||||
use futures::{
|
|
||||||
future::{join_all, try_join, try_join_all},
|
|
||||||
join,
|
|
||||||
};
|
|
||||||
use router_derive::Setter;
|
|
||||||
use storage_models::enums::MerchantStorageScheme;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
core::errors::{self, RouterResult, StorageErrorExt},
|
|
||||||
db::StorageInterface,
|
|
||||||
types::storage::{self as storage_types},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub enum PaymentAttemptDbCall {
|
|
||||||
Query {
|
|
||||||
merchant_id: String,
|
|
||||||
payment_id: String,
|
|
||||||
},
|
|
||||||
Insert(storage_types::PaymentAttemptNew),
|
|
||||||
Update {
|
|
||||||
current_payment_attempt: storage_types::PaymentAttempt,
|
|
||||||
updated_payment_attempt: storage_types::PaymentAttemptUpdate,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PaymentAttemptDbCall {
|
|
||||||
async fn get_db_call(
|
|
||||||
self,
|
|
||||||
db: &dyn StorageInterface,
|
|
||||||
storage_scheme: MerchantStorageScheme,
|
|
||||||
) -> Result<storage_types::PaymentAttempt, error_stack::Report<errors::ApiErrorResponse>> {
|
|
||||||
match self {
|
|
||||||
PaymentAttemptDbCall::Query {
|
|
||||||
merchant_id,
|
|
||||||
payment_id,
|
|
||||||
} => db
|
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
|
||||||
&payment_id,
|
|
||||||
&merchant_id,
|
|
||||||
storage_scheme,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::PaymentNotFound),
|
|
||||||
PaymentAttemptDbCall::Insert(payment_attempt_new) => {
|
|
||||||
let payment_id = payment_attempt_new.payment_id.clone();
|
|
||||||
db.insert_payment_attempt(payment_attempt_new, storage_scheme)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::DuplicatePayment { payment_id })
|
|
||||||
}
|
|
||||||
PaymentAttemptDbCall::Update {
|
|
||||||
current_payment_attempt,
|
|
||||||
updated_payment_attempt,
|
|
||||||
} => db
|
|
||||||
.update_payment_attempt(
|
|
||||||
current_payment_attempt,
|
|
||||||
updated_payment_attempt,
|
|
||||||
storage_scheme,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
|
||||||
.attach_printable("Failed to update payment attempt"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum PaymentIntentDbCall {
|
|
||||||
Insert(storage_types::PaymentIntentNew),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PaymentIntentDbCall {
|
|
||||||
async fn get_db_call(
|
|
||||||
self,
|
|
||||||
db: &dyn StorageInterface,
|
|
||||||
storage_scheme: MerchantStorageScheme,
|
|
||||||
) -> Result<storage_types::PaymentIntent, error_stack::Report<errors::ApiErrorResponse>> {
|
|
||||||
match self {
|
|
||||||
PaymentIntentDbCall::Insert(payment_intent_new) => {
|
|
||||||
let payment_id = payment_intent_new.payment_id.clone();
|
|
||||||
db.insert_payment_intent(payment_intent_new, storage_scheme)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::DuplicatePayment { payment_id })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ConnectorResponseDbCall {
|
|
||||||
Query {
|
|
||||||
merchant_id: String,
|
|
||||||
payment_id: String,
|
|
||||||
attempt_id: String,
|
|
||||||
},
|
|
||||||
Insert(storage_types::ConnectorResponseNew),
|
|
||||||
Update(storage_types::ConnectorResponseUpdate),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum AddressDbCall {
|
|
||||||
Query { address_id: String },
|
|
||||||
Insert(storage_types::AddressNew),
|
|
||||||
Update(storage_types::AddressUpdate),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DbCall {
|
|
||||||
payment_intent: PaymentIntentDbCall,
|
|
||||||
payment_attempt: PaymentAttemptDbCall,
|
|
||||||
connector_response: ConnectorResponseDbCall,
|
|
||||||
shipping_address: Option<AddressDbCall>,
|
|
||||||
billing_address: Option<AddressDbCall>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum EntityRequest {
|
|
||||||
PaymentIntent {},
|
|
||||||
PaymentAttempt {
|
|
||||||
payment_id: String,
|
|
||||||
merchant_id: String,
|
|
||||||
},
|
|
||||||
Address {
|
|
||||||
address_id: String,
|
|
||||||
},
|
|
||||||
ConnectorResponse {
|
|
||||||
payment_id: String,
|
|
||||||
attempt_id: String,
|
|
||||||
merchant_id: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Entity {
|
|
||||||
PaymentIntent(
|
|
||||||
Result<storage_types::PaymentIntent, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
),
|
|
||||||
PaymentAttempt(
|
|
||||||
Result<storage_types::PaymentAttempt, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
),
|
|
||||||
Address(Result<storage_types::Address, error_stack::Report<errors::ApiErrorResponse>>),
|
|
||||||
ConnectorResponse(
|
|
||||||
Result<storage_types::ConnectorResponse, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
),
|
|
||||||
None, //FIXME: for testing purposes only
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Setter)]
|
|
||||||
pub struct EntityResult {
|
|
||||||
pub payment_intent:
|
|
||||||
Result<storage_types::PaymentIntent, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
pub payment_attempt:
|
|
||||||
Result<storage_types::PaymentAttempt, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
pub connector_response:
|
|
||||||
Result<storage_types::ConnectorResponse, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
pub billing_address:
|
|
||||||
Result<Option<storage_types::Address>, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
pub shipping_address:
|
|
||||||
Result<Option<storage_types::Address>, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[derive(Setter)]
|
|
||||||
// pub struct DbCallRequest<T, F: Fn() -> Result<T, errors::ApiErrorResponse>> {
|
|
||||||
// pub payment_attempt: F,
|
|
||||||
// pub connector_response:
|
|
||||||
// Result<storage_types::ConnectorResponse, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
// pub billing_address:
|
|
||||||
// Result<Option<storage_types::Address>, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
// pub shipping_address:
|
|
||||||
// Result<Option<storage_types::Address>, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
// pub mandate:
|
|
||||||
// Result<Option<storage_types::Mandate>, error_stack::Report<errors::ApiErrorResponse>>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl EntityResult {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
payment_intent: Err(error_stack::report!(
|
|
||||||
errors::ApiErrorResponse::PaymentNotFound
|
|
||||||
)),
|
|
||||||
payment_attempt: Err(error_stack::report!(
|
|
||||||
errors::ApiErrorResponse::PaymentNotFound
|
|
||||||
)),
|
|
||||||
connector_response: Err(error_stack::report!(
|
|
||||||
errors::ApiErrorResponse::PaymentNotFound
|
|
||||||
)),
|
|
||||||
billing_address: Ok(None),
|
|
||||||
shipping_address: Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for EntityResult {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
pub trait QueryEntity {
|
|
||||||
async fn query_entity(
|
|
||||||
&self,
|
|
||||||
db: &dyn StorageInterface,
|
|
||||||
storage_scheme: MerchantStorageScheme,
|
|
||||||
) -> Entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl QueryEntity for EntityRequest {
|
|
||||||
async fn query_entity(
|
|
||||||
&self,
|
|
||||||
db: &dyn StorageInterface,
|
|
||||||
storage_scheme: MerchantStorageScheme,
|
|
||||||
) -> Entity {
|
|
||||||
match self {
|
|
||||||
EntityRequest::PaymentIntent {
|
|
||||||
payment_id,
|
|
||||||
merchant_id,
|
|
||||||
} => Entity::PaymentIntent(
|
|
||||||
db.find_payment_intent_by_payment_id_merchant_id(
|
|
||||||
payment_id,
|
|
||||||
merchant_id,
|
|
||||||
storage_scheme,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::PaymentNotFound),
|
|
||||||
),
|
|
||||||
EntityRequest::PaymentAttempt {
|
|
||||||
payment_id,
|
|
||||||
merchant_id,
|
|
||||||
} => Entity::PaymentAttempt(
|
|
||||||
db.find_payment_attempt_by_payment_id_merchant_id(
|
|
||||||
payment_id,
|
|
||||||
merchant_id,
|
|
||||||
storage_scheme,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::PaymentNotFound),
|
|
||||||
),
|
|
||||||
EntityRequest::Address { address_id } => Entity::Address(
|
|
||||||
db.find_address(address_id)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::AddressNotFound), //FIXME: do not change context
|
|
||||||
),
|
|
||||||
EntityRequest::ConnectorResponse {
|
|
||||||
payment_id,
|
|
||||||
attempt_id,
|
|
||||||
merchant_id,
|
|
||||||
} => Entity::ConnectorResponse(
|
|
||||||
db.find_connector_response_by_payment_id_merchant_id_attempt_id(
|
|
||||||
&payment_id,
|
|
||||||
&merchant_id,
|
|
||||||
&attempt_id,
|
|
||||||
storage_scheme,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::PaymentNotFound),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn make_parallel_db_call(
|
|
||||||
db: &dyn StorageInterface,
|
|
||||||
db_calls: DbCall,
|
|
||||||
storage_scheme: MerchantStorageScheme,
|
|
||||||
) -> EntityResult {
|
|
||||||
let (payment_intent_res) = join!(
|
|
||||||
db_calls.payment_attempt.get_db_call(db, storage_scheme),
|
|
||||||
db_calls.payment_intent.get_db_call(db, storage_scheme)
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut entities_result = EntityResult::new();
|
|
||||||
|
|
||||||
for entity in combined_res {
|
|
||||||
match entity {
|
|
||||||
Entity::PaymentIntent(pi_res) => entities_result.set_payment_intent(pi_res),
|
|
||||||
Entity::PaymentAttempt(pa_res) => entities_result.set_payment_attempt(pa_res),
|
|
||||||
_ => &mut entities_result,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
entities_result
|
|
||||||
}
|
|
||||||
@ -95,6 +95,7 @@ mod tests {
|
|||||||
|
|
||||||
let current_time = common_utils::date_time::now();
|
let current_time = common_utils::date_time::now();
|
||||||
let payment_id = Uuid::new_v4().to_string();
|
let payment_id = Uuid::new_v4().to_string();
|
||||||
|
let attempt_id = Uuid::new_v4().to_string();
|
||||||
let merchant_id = Uuid::new_v4().to_string();
|
let merchant_id = Uuid::new_v4().to_string();
|
||||||
let connector = types::Connector::Dummy.to_string();
|
let connector = types::Connector::Dummy.to_string();
|
||||||
|
|
||||||
@ -106,6 +107,7 @@ mod tests {
|
|||||||
})),
|
})),
|
||||||
created_at: current_time.into(),
|
created_at: current_time.into(),
|
||||||
modified_at: current_time.into(),
|
modified_at: current_time.into(),
|
||||||
|
attempt_id: attempt_id.clone(),
|
||||||
..PaymentAttemptNew::default()
|
..PaymentAttemptNew::default()
|
||||||
};
|
};
|
||||||
state
|
state
|
||||||
@ -116,9 +118,10 @@ mod tests {
|
|||||||
|
|
||||||
let response = state
|
let response = state
|
||||||
.store
|
.store
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&payment_id,
|
&payment_id,
|
||||||
&merchant_id,
|
&merchant_id,
|
||||||
|
&attempt_id,
|
||||||
enums::MerchantStorageScheme::PostgresOnly,
|
enums::MerchantStorageScheme::PostgresOnly,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -150,6 +153,7 @@ mod tests {
|
|||||||
modified_at: current_time.into(),
|
modified_at: current_time.into(),
|
||||||
// Adding a mandate_id
|
// Adding a mandate_id
|
||||||
mandate_id: Some("man_121212".to_string()),
|
mandate_id: Some("man_121212".to_string()),
|
||||||
|
attempt_id: uuid.clone(),
|
||||||
..PaymentAttemptNew::default()
|
..PaymentAttemptNew::default()
|
||||||
};
|
};
|
||||||
state
|
state
|
||||||
@ -160,9 +164,10 @@ mod tests {
|
|||||||
|
|
||||||
let response = state
|
let response = state
|
||||||
.store
|
.store
|
||||||
.find_payment_attempt_by_payment_id_merchant_id(
|
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
|
||||||
&uuid,
|
&uuid,
|
||||||
"1",
|
"1",
|
||||||
|
&uuid,
|
||||||
enums::MerchantStorageScheme::PostgresOnly,
|
enums::MerchantStorageScheme::PostgresOnly,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -33,6 +33,7 @@ pub struct PaymentIntent {
|
|||||||
pub setup_future_usage: Option<storage_enums::FutureUsage>,
|
pub setup_future_usage: Option<storage_enums::FutureUsage>,
|
||||||
pub off_session: Option<bool>,
|
pub off_session: Option<bool>,
|
||||||
pub client_secret: Option<String>,
|
pub client_secret: Option<String>,
|
||||||
|
pub active_attempt_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
@ -72,6 +73,7 @@ pub struct PaymentIntentNew {
|
|||||||
pub client_secret: Option<String>,
|
pub client_secret: Option<String>,
|
||||||
pub setup_future_usage: Option<storage_enums::FutureUsage>,
|
pub setup_future_usage: Option<storage_enums::FutureUsage>,
|
||||||
pub off_session: Option<bool>,
|
pub off_session: Option<bool>,
|
||||||
|
pub active_attempt_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -109,6 +111,9 @@ pub enum PaymentIntentUpdate {
|
|||||||
billing_address_id: Option<String>,
|
billing_address_id: Option<String>,
|
||||||
return_url: Option<String>,
|
return_url: Option<String>,
|
||||||
},
|
},
|
||||||
|
PaymentAttemptUpdate {
|
||||||
|
active_attempt_id: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
|
#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
|
||||||
@ -128,6 +133,7 @@ pub struct PaymentIntentUpdateInternal {
|
|||||||
pub billing_address_id: Option<String>,
|
pub billing_address_id: Option<String>,
|
||||||
pub shipping_address_id: Option<String>,
|
pub shipping_address_id: Option<String>,
|
||||||
pub modified_at: Option<PrimitiveDateTime>,
|
pub modified_at: Option<PrimitiveDateTime>,
|
||||||
|
pub active_attempt_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaymentIntentUpdate {
|
impl PaymentIntentUpdate {
|
||||||
@ -242,6 +248,10 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
|
|||||||
modified_at: Some(common_utils::date_time::now()),
|
modified_at: Some(common_utils::date_time::now()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
PaymentIntentUpdate::PaymentAttemptUpdate { active_attempt_id } => Self {
|
||||||
|
active_attempt_id: Some(active_attempt_id),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,15 +21,20 @@ impl PaymentAttemptNew {
|
|||||||
|
|
||||||
impl PaymentAttempt {
|
impl PaymentAttempt {
|
||||||
#[instrument(skip(conn))]
|
#[instrument(skip(conn))]
|
||||||
pub async fn update(
|
pub async fn update_with_attempt_id(
|
||||||
self,
|
self,
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
payment_attempt: PaymentAttemptUpdate,
|
payment_attempt: PaymentAttemptUpdate,
|
||||||
) -> StorageResult<Self> {
|
) -> StorageResult<Self> {
|
||||||
match generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
|
match generics::generic_update_with_unique_predicate_get_result::<
|
||||||
|
<Self as HasTable>::Table,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
>(
|
||||||
conn,
|
conn,
|
||||||
dsl::payment_id
|
dsl::attempt_id
|
||||||
.eq(self.payment_id.to_owned())
|
.eq(self.attempt_id.to_owned())
|
||||||
.and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
|
.and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
|
||||||
PaymentAttemptUpdateInternal::from(payment_attempt),
|
PaymentAttemptUpdateInternal::from(payment_attempt),
|
||||||
)
|
)
|
||||||
@ -39,27 +44,10 @@ impl PaymentAttempt {
|
|||||||
errors::DatabaseError::NoFieldsToUpdate => Ok(self),
|
errors::DatabaseError::NoFieldsToUpdate => Ok(self),
|
||||||
_ => Err(error),
|
_ => Err(error),
|
||||||
},
|
},
|
||||||
Ok(mut payment_attempts) => payment_attempts
|
result => result,
|
||||||
.pop()
|
|
||||||
.ok_or(error_stack::report!(errors::DatabaseError::NotFound)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(conn))]
|
|
||||||
pub async fn find_by_payment_id_merchant_id(
|
|
||||||
conn: &PgPooledConn,
|
|
||||||
payment_id: &str,
|
|
||||||
merchant_id: &str,
|
|
||||||
) -> StorageResult<Self> {
|
|
||||||
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
|
||||||
conn,
|
|
||||||
dsl::merchant_id
|
|
||||||
.eq(merchant_id.to_owned())
|
|
||||||
.and(dsl::payment_id.eq(payment_id.to_owned())),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(conn))]
|
#[instrument(skip(conn))]
|
||||||
pub async fn find_optional_by_payment_id_merchant_id(
|
pub async fn find_optional_by_payment_id_merchant_id(
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
@ -118,7 +106,7 @@ impl PaymentAttempt {
|
|||||||
.fold(
|
.fold(
|
||||||
Err(errors::DatabaseError::NotFound).into_report(),
|
Err(errors::DatabaseError::NotFound).into_report(),
|
||||||
|acc, cur| match acc {
|
|acc, cur| match acc {
|
||||||
Ok(value) if value.created_at > cur.created_at => Ok(value),
|
Ok(value) if value.modified_at > cur.modified_at => Ok(value),
|
||||||
_ => Ok(cur),
|
_ => Ok(cur),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -153,4 +141,22 @@ impl PaymentAttempt {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(conn))]
|
||||||
|
pub async fn find_by_payment_id_merchant_id_attempt_id(
|
||||||
|
conn: &PgPooledConn,
|
||||||
|
payment_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
attempt_id: &str,
|
||||||
|
) -> StorageResult<Self> {
|
||||||
|
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
||||||
|
conn,
|
||||||
|
dsl::payment_id.eq(payment_id.to_owned()).and(
|
||||||
|
dsl::merchant_id
|
||||||
|
.eq(merchant_id.to_owned())
|
||||||
|
.and(dsl::attempt_id.eq(attempt_id.to_owned())),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,7 @@ diesel::table! {
|
|||||||
setup_future_usage -> Nullable<FutureUsage>,
|
setup_future_usage -> Nullable<FutureUsage>,
|
||||||
off_session -> Nullable<Bool>,
|
off_session -> Nullable<Bool>,
|
||||||
client_secret -> Nullable<Varchar>,
|
client_secret -> Nullable<Varchar>,
|
||||||
|
active_attempt_id -> Varchar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP INDEX payment_attempt_payment_id_merchant_id_index;
|
||||||
|
CREATE UNIQUE INDEX payment_attempt_payment_id_merchant_id_index ON payment_attempt (payment_id, merchant_id);
|
||||||
|
DROP INDEX payment_attempt_payment_id_merchant_id_attempt_id_index;
|
||||||
|
ALTER TABLE PAYMENT_INTENT DROP COLUMN attempt_id;
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
ALTER TABLE payment_intent ADD COLUMN active_attempt_id VARCHAR(64) NOT NULL DEFAULT 'xxx';
|
||||||
|
|
||||||
|
UPDATE payment_intent SET active_attempt_id = payment_attempt.attempt_id from payment_attempt where payment_intent.active_attempt_id = payment_attempt.payment_id;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX payment_attempt_payment_id_merchant_id_attempt_id_index ON payment_attempt (payment_id, merchant_id, attempt_id);
|
||||||
|
|
||||||
|
-- Because payment_attempt table can have rows with same payment_id and merchant_id, this index is dropped.
|
||||||
|
DROP index payment_attempt_payment_id_merchant_id_index;
|
||||||
|
|
||||||
|
CREATE INDEX payment_attempt_payment_id_merchant_id_index ON payment_attempt (payment_id, merchant_id);
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP INDEX payment_attempt_connector_transaction_id_merchant_id_index;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
CREATE INDEX payment_attempt_connector_transaction_id_merchant_id_index ON payment_attempt (connector_transaction_id, merchant_id);
|
||||||
Reference in New Issue
Block a user