Skip to content

Commit

Permalink
feat(process_tracker): make long standing payments failed (#2380)
Browse files Browse the repository at this point in the history
Co-authored-by: Arun Raj M <[email protected]>
  • Loading branch information
Narayanbhat166 and jarnura authored Oct 9, 2023
1 parent 17393f5 commit 73dfc31
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 83 deletions.
2 changes: 2 additions & 0 deletions crates/router/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub(crate) const ALPHABETS: [char; 62] = [
pub const REQUEST_TIME_OUT: u64 = 30;
pub const REQUEST_TIMEOUT_ERROR_CODE: &str = "TIMEOUT";
pub const REQUEST_TIMEOUT_ERROR_MESSAGE: &str = "Connector did not respond in specified time";
pub const REQUEST_TIMEOUT_ERROR_MESSAGE_FROM_PSYNC: &str =
"This Payment has been moved to failed as there is no response from the connector";

///Payment intent fulfillment default timeout (in seconds)
pub const DEFAULT_FULFILLMENT_TIME: i64 = 15 * 60;
Expand Down
129 changes: 67 additions & 62 deletions crates/router/src/core/payments/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ use std::{fmt::Debug, marker::PhantomData, str::FromStr};

use api_models::payments::FrmMessage;
use common_utils::fp_utils;
use data_models::mandates::MandateData;
use diesel_models::ephemeral_key;
use error_stack::{IntoReport, ResultExt};
use router_env::{instrument, tracing};

use super::{flows::Feature, PaymentAddress, PaymentData};
use super::{flows::Feature, PaymentData};
use crate::{
configs::settings::{ConnectorRequestReferenceIdConfig, Server},
connector::Nexinets,
Expand Down Expand Up @@ -202,40 +201,32 @@ where
connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig,
connector_http_status_code: Option<u16>,
) -> RouterResponse<Self> {
let captures = payment_data
.multiple_capture_data
.and_then(|multiple_capture_data| {
multiple_capture_data
.expand_captures
.and_then(|should_expand| {
should_expand.then_some(
multiple_capture_data
.get_all_captures()
.into_iter()
.cloned()
.collect(),
)
})
});
let captures =
payment_data
.multiple_capture_data
.clone()
.and_then(|multiple_capture_data| {
multiple_capture_data
.expand_captures
.and_then(|should_expand| {
should_expand.then_some(
multiple_capture_data
.get_all_captures()
.into_iter()
.cloned()
.collect(),
)
})
});

payments_to_payments_response(
req,
payment_data.payment_attempt,
payment_data.payment_intent,
payment_data.refunds,
payment_data.disputes,
payment_data.attempts,
payment_data,
captures,
payment_data.payment_method_data,
customer,
auth_flow,
payment_data.address,
server,
payment_data.connector_response.authentication_data,
&operation,
payment_data.ephemeral_key,
payment_data.sessions_token,
payment_data.frm_message,
payment_data.setup_mandate,
connector_request_reference_id_config,
connector_http_status_code,
)
Expand Down Expand Up @@ -333,31 +324,23 @@ where
// try to use router data here so that already validated things , we don't want to repeat the validations.
// Add internal value not found and external value not found so that we can give 500 / Internal server error for internal value not found
#[allow(clippy::too_many_arguments)]
pub fn payments_to_payments_response<R, Op>(
pub fn payments_to_payments_response<R, Op, F: Clone>(
payment_request: Option<R>,
payment_attempt: storage::PaymentAttempt,
payment_intent: storage::PaymentIntent,
refunds: Vec<storage::Refund>,
disputes: Vec<storage::Dispute>,
option_attempts: Option<Vec<storage::PaymentAttempt>>,
payment_data: PaymentData<F>,
captures: Option<Vec<storage::Capture>>,
payment_method_data: Option<api::PaymentMethodData>,
customer: Option<domain::Customer>,
auth_flow: services::AuthFlow,
address: PaymentAddress,
server: &Server,
redirection_data: Option<serde_json::Value>,
operation: &Op,
ephemeral_key_option: Option<ephemeral_key::EphemeralKey>,
session_tokens: Vec<api::SessionToken>,
fraud_check: Option<payments::FraudCheck>,
mandate_data: Option<MandateData>,
connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig,
connector_http_status_code: Option<u16>,
) -> RouterResponse<api::PaymentsResponse>
where
Op: Debug,
{
let payment_attempt = payment_data.payment_attempt;
let payment_intent = payment_data.payment_intent;

let currency = payment_attempt
.currency
.as_ref()
Expand All @@ -369,22 +352,31 @@ where
field_name: "amount",
})?;
let mandate_id = payment_attempt.mandate_id.clone();
let refunds_response = if refunds.is_empty() {
let refunds_response = if payment_data.refunds.is_empty() {
None
} else {
Some(refunds.into_iter().map(ForeignInto::foreign_into).collect())
Some(
payment_data
.refunds
.into_iter()
.map(ForeignInto::foreign_into)
.collect(),
)
};
let disputes_response = if disputes.is_empty() {

let disputes_response = if payment_data.disputes.is_empty() {
None
} else {
Some(
disputes
payment_data
.disputes
.into_iter()
.map(ForeignInto::foreign_into)
.collect(),
)
};
let attempts_response = option_attempts.map(|attempts| {

let attempts_response = payment_data.attempts.map(|attempts| {
attempts
.into_iter()
.map(ForeignInto::foreign_into)
Expand Down Expand Up @@ -419,7 +411,7 @@ where
field_name: "payment_method_data",
})?;
let merchant_decision = payment_intent.merchant_decision.to_owned();
let frm_message = fraud_check.map(FrmMessage::foreign_from);
let frm_message = payment_data.frm_message.map(FrmMessage::foreign_from);

let payment_method_data_response =
additional_payment_method_data.map(api::PaymentMethodDataResponse::from);
Expand All @@ -441,13 +433,23 @@ where

let output = Ok(match payment_request {
Some(_request) => {
if payments::is_start_pay(&operation) && redirection_data.is_some() {
let redirection_data = redirection_data.get_required_value("redirection_data")?;
if payments::is_start_pay(&operation)
&& payment_data
.connector_response
.authentication_data
.is_some()
{
let redirection_data = payment_data
.connector_response
.authentication_data
.get_required_value("redirection_data")?;

let form: RedirectForm = serde_json::from_value(redirection_data)
.map_err(|_| errors::ApiErrorResponse::InternalServerError)?;

services::ApplicationResponse::Form(Box::new(services::RedirectionFormData {
redirect_form: form,
payment_method_data,
payment_method_data: payment_data.payment_method_data,
amount,
currency: currency.to_string(),
}))
Expand Down Expand Up @@ -494,22 +496,23 @@ where
display_to_timestamp: wait_screen_data.display_to_timestamp,
}
}))
.or(redirection_data.map(|_| {
api_models::payments::NextActionData::RedirectToUrl {
.or(payment_data
.connector_response
.authentication_data
.map(|_| api_models::payments::NextActionData::RedirectToUrl {
redirect_to_url: helpers::create_startpay_url(
server,
&payment_attempt,
&payment_intent,
),
}
}));
}));
};

// next action check for third party sdk session (for ex: Apple pay through trustpay has third party sdk session response)
if third_party_sdk_session_next_action(&payment_attempt, operation) {
next_action_response = Some(
api_models::payments::NextActionData::ThirdPartySdkSessionToken {
session_token: session_tokens.get(0).cloned(),
session_token: payment_data.sessions_token.get(0).cloned(),
},
)
}
Expand Down Expand Up @@ -555,7 +558,7 @@ where
)
.set_mandate_id(mandate_id)
.set_mandate_data(
mandate_data.map(|d| api::MandateData {
payment_data.setup_mandate.map(|d| api::MandateData {
customer_acceptance: d.customer_acceptance.map(|d| {
api::CustomerAcceptance {
acceptance_type: match d.acceptance_type {
Expand Down Expand Up @@ -621,8 +624,8 @@ where
.or(payment_attempt.error_message),
)
.set_error_code(payment_attempt.error_code)
.set_shipping(address.shipping)
.set_billing(address.billing)
.set_shipping(payment_data.address.shipping)
.set_billing(payment_data.address.billing)
.set_next_action(next_action_response)
.set_return_url(payment_intent.return_url)
.set_cancellation_reason(payment_attempt.cancellation_reason)
Expand All @@ -642,7 +645,9 @@ where
.set_allowed_payment_method_types(
payment_intent.allowed_payment_method_types,
)
.set_ephemeral_key(ephemeral_key_option.map(ForeignFrom::foreign_from))
.set_ephemeral_key(
payment_data.ephemeral_key.map(ForeignFrom::foreign_from),
)
.set_frm_message(frm_message)
.set_merchant_decision(merchant_decision)
.set_manual_retry_allowed(helpers::is_manual_retry_allowed(
Expand Down Expand Up @@ -696,8 +701,8 @@ where
.as_ref()
.and_then(|cus| cus.phone.as_ref().map(|s| s.to_owned())),
mandate_id,
shipping: address.shipping,
billing: address.billing,
shipping: payment_data.address.shipping,
billing: payment_data.address.billing,
cancellation_reason: payment_attempt.cancellation_reason,
payment_token: payment_attempt.payment_token,
metadata: payment_intent.metadata,
Expand Down
4 changes: 2 additions & 2 deletions crates/router/src/core/refunds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,13 +861,13 @@ pub async fn sync_refund_with_gateway_workflow(
.await?
}
_ => {
payment_sync::retry_sync_task(
_ = payment_sync::retry_sync_task(
&*state.store,
response.connector,
response.merchant_id,
refund_tracker.to_owned(),
)
.await?
.await?;
}
}

Expand Down
9 changes: 3 additions & 6 deletions crates/router/src/core/webhooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,11 +695,6 @@ pub async fn create_event_and_trigger_outgoing_webhook<W: types::OutgoingWebhook
}?;

if state.conf.webhooks.outgoing_enabled {
let arbiter = actix::Arbiter::try_current()
.ok_or(errors::ApiErrorResponse::WebhookProcessingFailure)
.into_report()
.attach_printable("arbiter retrieval failure")?;

let outgoing_webhook = api::OutgoingWebhook {
merchant_id: merchant_account.merchant_id.clone(),
event_id: event.event_id,
Expand All @@ -708,7 +703,9 @@ pub async fn create_event_and_trigger_outgoing_webhook<W: types::OutgoingWebhook
timestamp: event.created_at,
};

arbiter.spawn(async move {
// Using a tokio spawn here and not arbiter because not all caller of this function
// may have an actix arbiter
tokio::spawn(async move {
let result =
trigger_webhook_to_merchant::<W>(merchant_account, outgoing_webhook, &state).await;

Expand Down
Loading

0 comments on commit 73dfc31

Please sign in to comment.