feat(compatibility): add mandates support in stripe compatibility (#897)

Co-authored-by: Sahkal Poddar <sahkal.poddar@juspay.in>
Co-authored-by: Sarthak Soni <sarthak.soni@juspay.in>
Co-authored-by: Sarthak Soni <76486416+Sarthak1799@users.noreply.github.com>
Co-authored-by: Abhishek Marrivagu <abhi.codes10@gmail.com>
This commit is contained in:
Sahkal Poddar
2023-05-08 14:41:27 +05:30
committed by GitHub
parent 982c27fce7
commit 2ba186b7d1
13 changed files with 329 additions and 76 deletions

View File

@ -47,6 +47,7 @@ locker_decryption_key1 = ""
locker_decryption_key2 = "" locker_decryption_key2 = ""
vault_encryption_key = "" vault_encryption_key = ""
vault_private_key = "" vault_private_key = ""
tunnel_private_key = ""
[connectors.supported] [connectors.supported]
wallets = ["klarna", "braintree", "applepay"] wallets = ["klarna", "braintree", "applepay"]

View File

@ -147,17 +147,17 @@ where
/// ///
/// Extending functionalities of `bytes::Bytes` /// Extending functionalities of `bytes::Bytes`
/// ///
pub trait BytesExt<T> { pub trait BytesExt {
/// ///
/// Convert `bytes::Bytes` into type `<T>` using `serde::Deserialize` /// Convert `bytes::Bytes` into type `<T>` using `serde::Deserialize`
/// ///
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_struct<'de, T>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
where where
T: Deserialize<'de>; T: Deserialize<'de>;
} }
impl<T> BytesExt<T> for bytes::Bytes { impl BytesExt for bytes::Bytes {
fn parse_struct<'de>(&'de self, _type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_struct<'de, T>(&'de self, _type_name: &str) -> CustomResult<T, errors::ParsingError>
where where
T: Deserialize<'de>, T: Deserialize<'de>,
{ {

View File

@ -20,6 +20,7 @@ impl StripeApis {
.service(app::PaymentIntents::server(state.clone())) .service(app::PaymentIntents::server(state.clone()))
.service(app::Refunds::server(state.clone())) .service(app::Refunds::server(state.clone()))
.service(app::Customers::server(state.clone())) .service(app::Customers::server(state.clone()))
.service(app::Webhooks::server(state)) .service(app::Webhooks::server(state.clone()))
.service(app::Mandates::server(state))
} }
} }

View File

@ -1,7 +1,7 @@
use actix_web::{web, Scope}; use actix_web::{web, Scope};
use super::{customers::*, payment_intents::*, refunds::*, setup_intents::*, webhooks::*}; use super::{customers::*, payment_intents::*, refunds::*, setup_intents::*, webhooks::*};
use crate::routes::{self, webhooks}; use crate::routes::{self, mandates, webhooks};
pub struct PaymentIntents; pub struct PaymentIntents;
@ -111,3 +111,13 @@ impl Webhooks {
) )
} }
} }
pub struct Mandates;
impl Mandates {
pub fn server(config: routes::AppState) -> Scope {
web::scope("/payment_methods")
.app_data(web::Data::new(config))
.service(web::resource("/{id}/detach").route(web::post().to(mandates::revoke_mandate)))
}
}

View File

@ -1,6 +1,6 @@
use api_models::payments; use api_models::payments;
use common_utils::{date_time, ext_traits::StringExt, pii as secret}; use common_utils::{date_time, ext_traits::StringExt, pii as secret};
use error_stack::ResultExt; use error_stack::{IntoReport, ResultExt};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -10,7 +10,7 @@ use crate::{
pii::{self, Email, PeekInterface}, pii::{self, Email, PeekInterface},
types::{ types::{
api::{admin, enums as api_enums}, api::{admin, enums as api_enums},
transformers::{ForeignFrom, ForeignInto}, transformers::{ForeignFrom, ForeignTryFrom},
}, },
}; };
@ -59,6 +59,7 @@ impl From<StripePaymentMethodType> for api_enums::PaymentMethod {
} }
} }
} }
#[derive(Default, PartialEq, Eq, Deserialize, Clone)] #[derive(Default, PartialEq, Eq, Deserialize, Clone)]
pub struct StripePaymentMethodData { pub struct StripePaymentMethodData {
#[serde(rename = "type")] #[serde(rename = "type")]
@ -143,12 +144,32 @@ pub struct StripePaymentIntentRequest {
pub client_secret: Option<pii::Secret<String>>, pub client_secret: Option<pii::Secret<String>>,
pub payment_method_options: Option<StripePaymentMethodOptions>, pub payment_method_options: Option<StripePaymentMethodOptions>,
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>, pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
pub mandate_id: Option<String>,
pub off_session: Option<bool>,
} }
impl TryFrom<StripePaymentIntentRequest> for payments::PaymentsRequest { impl TryFrom<StripePaymentIntentRequest> for payments::PaymentsRequest {
type Error = error_stack::Report<errors::ApiErrorResponse>; type Error = error_stack::Report<errors::ApiErrorResponse>;
fn try_from(item: StripePaymentIntentRequest) -> errors::RouterResult<Self> { fn try_from(item: StripePaymentIntentRequest) -> errors::RouterResult<Self> {
Ok(Self { let (mandate_options, authentication_type) = match item.payment_method_options {
Some(pmo) => {
let StripePaymentMethodOptions::Card {
request_three_d_secure,
mandate_options,
}: StripePaymentMethodOptions = pmo;
(
Option::<payments::MandateData>::foreign_try_from((
mandate_options,
item.currency.to_owned(),
))?,
Some(api_enums::AuthenticationType::foreign_from(
request_three_d_secure,
)),
)
}
None => (None, None),
};
let request = Ok(Self {
payment_id: item.id.map(payments::PaymentIdType::PaymentIntentId), payment_id: item.id.map(payments::PaymentIdType::PaymentIntentId),
amount: item.amount.map(|amount| amount.into()), amount: item.amount.map(|amount| amount.into()),
connector: item.connector, connector: item.connector,
@ -188,16 +209,15 @@ impl TryFrom<StripePaymentIntentRequest> for payments::PaymentsRequest {
statement_descriptor_suffix: item.statement_descriptor_suffix, statement_descriptor_suffix: item.statement_descriptor_suffix,
metadata: item.metadata, metadata: item.metadata,
client_secret: item.client_secret.map(|s| s.peek().clone()), client_secret: item.client_secret.map(|s| s.peek().clone()),
authentication_type: item.payment_method_options.map(|pmo| { authentication_type,
let StripePaymentMethodOptions::Card { mandate_data: mandate_options,
request_three_d_secure,
} = pmo;
request_three_d_secure.foreign_into()
}),
merchant_connector_details: item.merchant_connector_details, merchant_connector_details: item.merchant_connector_details,
setup_future_usage: item.setup_future_usage,
mandate_id: item.mandate_id,
off_session: item.off_session,
..Self::default() ..Self::default()
}) });
request
} }
} }
@ -374,7 +394,7 @@ impl From<payments::PaymentsResponse> for StripePaymentIntentResponse {
created: u64::try_from(date_time::now().assume_utc().unix_timestamp()) created: u64::try_from(date_time::now().assume_utc().unix_timestamp())
.unwrap_or_default(), .unwrap_or_default(),
method_type: "card".to_string(), method_type: "card".to_string(),
live_mode: false, livemode: false,
}, },
error_type: code, error_type: code,
}), }),
@ -391,7 +411,7 @@ pub struct StripePaymentMethod {
created: u64, created: u64,
#[serde(rename = "type")] #[serde(rename = "type")]
method_type: String, method_type: String,
live_mode: bool, livemode: bool,
} }
#[derive(Default, Eq, PartialEq, Serialize)] #[derive(Default, Eq, PartialEq, Serialize)]
@ -404,7 +424,7 @@ pub struct Charges {
} }
impl Charges { impl Charges {
fn new() -> Self { pub fn new() -> Self {
Self { Self {
object: "list", object: "list",
data: vec![], data: vec![],
@ -491,15 +511,83 @@ impl From<payments::PaymentListResponse> for StripePaymentIntentListResponse {
} }
} }
#[derive(PartialEq, Eq, Deserialize, Clone)] #[derive(PartialEq, Eq, Deserialize, Clone, Debug)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodOptions { pub enum StripePaymentMethodOptions {
Card { Card {
request_three_d_secure: Option<Request3DS>, request_three_d_secure: Option<Request3DS>,
mandate_options: Option<MandateOption>,
}, },
} }
#[derive(Default, Eq, PartialEq, Serialize, Deserialize, Clone)] #[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripeMandateType {
SingleUse,
MultiUse,
}
#[derive(PartialEq, Eq, Clone, Default, Deserialize, Serialize, Debug)]
pub struct MandateOption {
#[serde(default, with = "common_utils::custom_serde::timestamp::option")]
pub accepted_at: Option<time::PrimitiveDateTime>,
pub user_agent: Option<String>,
pub ip_address: Option<pii::Secret<String, common_utils::pii::IpAddress>>,
pub mandate_type: Option<StripeMandateType>,
pub amount: Option<i64>,
#[serde(default, with = "common_utils::custom_serde::timestamp::option")]
pub start_date: Option<time::PrimitiveDateTime>,
#[serde(default, with = "common_utils::custom_serde::timestamp::option")]
pub end_date: Option<time::PrimitiveDateTime>,
}
impl ForeignTryFrom<(Option<MandateOption>, Option<String>)> for Option<payments::MandateData> {
type Error = error_stack::Report<errors::ApiErrorResponse>;
fn foreign_try_from(
(mandate_options, currency): (Option<MandateOption>, Option<String>),
) -> errors::RouterResult<Self> {
let currency = currency
.ok_or(errors::ApiErrorResponse::MissingRequiredField {
field_name: "currency",
})
.into_report()
.and_then(|c| {
c.to_uppercase().parse_enum("currency").change_context(
errors::ApiErrorResponse::InvalidDataValue {
field_name: "currency",
},
)
})?;
let mandate_data = mandate_options.map(|mandate| payments::MandateData {
mandate_type: match mandate.mandate_type {
Some(item) => match item {
StripeMandateType::SingleUse => {
payments::MandateType::SingleUse(payments::MandateAmountData {
amount: mandate.amount.unwrap_or_default(),
currency,
start_date: mandate.start_date,
end_date: mandate.end_date,
metadata: None,
})
}
StripeMandateType::MultiUse => payments::MandateType::MultiUse(None),
},
None => api_models::payments::MandateType::MultiUse(None),
},
customer_acceptance: payments::CustomerAcceptance {
acceptance_type: payments::AcceptanceType::Online,
accepted_at: mandate.accepted_at,
online: Some(payments::OnlineMandate {
ip_address: mandate.ip_address.unwrap_or_default(),
user_agent: mandate.user_agent.unwrap_or_default(),
}),
},
});
Ok(mandate_data)
}
}
#[derive(Default, Eq, PartialEq, Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum Request3DS { pub enum Request3DS {
#[default] #[default]

View File

@ -28,7 +28,11 @@ pub async fn setup_intents_create(
} }
}; };
let create_payment_req: payment_types::PaymentsRequest = payload.into(); let create_payment_req: payment_types::PaymentsRequest =
match payment_types::PaymentsRequest::try_from(payload) {
Ok(req) => req,
Err(err) => return api::log_and_return_error_response(err),
};
wrap::compatibility_api_wrap::< wrap::compatibility_api_wrap::<
_, _,
@ -124,7 +128,11 @@ pub async fn setup_intents_update(
} }
}; };
let mut payload: payment_types::PaymentsRequest = stripe_payload.into(); let mut payload: payment_types::PaymentsRequest =
match payment_types::PaymentsRequest::try_from(stripe_payload) {
Ok(req) => req,
Err(err) => return api::log_and_return_error_response(err),
};
payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(setup_id)); payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(setup_id));
let (auth_type, auth_flow) = let (auth_type, auth_flow) =
@ -179,7 +187,11 @@ pub async fn setup_intents_confirm(
} }
}; };
let mut payload: payment_types::PaymentsRequest = stripe_payload.into(); let mut payload: payment_types::PaymentsRequest =
match payment_types::PaymentsRequest::try_from(stripe_payload) {
Ok(req) => req,
Err(err) => return api::log_and_return_error_response(err),
};
payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(setup_id)); payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(setup_id));
payload.confirm = Some(true); payload.confirm = Some(true);

View File

@ -1,12 +1,21 @@
use api_models::{payments, refunds}; use api_models::payments;
use common_utils::{date_time, ext_traits::StringExt};
use error_stack::ResultExt;
use router_env::logger; use router_env::logger;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use crate::{ use crate::{
compatibility::stripe::{
payment_intents::types as payment_intent, refunds::types as stripe_refunds,
},
consts,
core::errors, core::errors,
pii::{self, PeekInterface}, pii::{self, PeekInterface},
types::api::{self as api_types, enums as api_enums}, types::{
api::{self as api_types, admin, enums as api_enums},
transformers::{ForeignFrom, ForeignTryFrom},
},
}; };
#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)] #[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
@ -108,11 +117,14 @@ impl From<Shipping> for payments::Address {
} }
} }
} }
#[derive(Default, PartialEq, Eq, Deserialize, Clone)] #[derive(Default, PartialEq, Eq, Deserialize, Clone)]
pub struct StripeSetupIntentRequest { pub struct StripeSetupIntentRequest {
pub confirm: Option<bool>, pub confirm: Option<bool>,
pub customer: Option<String>, pub customer: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub currency: Option<String>,
pub payment_method_data: Option<StripePaymentMethodData>, pub payment_method_data: Option<StripePaymentMethodData>,
pub receipt_email: Option<pii::Email>, pub receipt_email: Option<pii::Email>,
pub return_url: Option<url::Url>, pub return_url: Option<url::Url>,
@ -123,17 +135,46 @@ pub struct StripeSetupIntentRequest {
pub statement_descriptor_suffix: Option<String>, pub statement_descriptor_suffix: Option<String>,
pub metadata: Option<api_models::payments::Metadata>, pub metadata: Option<api_models::payments::Metadata>,
pub client_secret: Option<pii::Secret<String>>, pub client_secret: Option<pii::Secret<String>>,
pub payment_method_options: Option<payment_intent::StripePaymentMethodOptions>,
pub payment_method: Option<String>,
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
} }
impl From<StripeSetupIntentRequest> for payments::PaymentsRequest { impl TryFrom<StripeSetupIntentRequest> for payments::PaymentsRequest {
fn from(item: StripeSetupIntentRequest) -> Self { type Error = error_stack::Report<errors::ApiErrorResponse>;
Self { fn try_from(item: StripeSetupIntentRequest) -> errors::RouterResult<Self> {
let (mandate_options, authentication_type) = match item.payment_method_options {
Some(pmo) => {
let payment_intent::StripePaymentMethodOptions::Card {
request_three_d_secure,
mandate_options,
}: payment_intent::StripePaymentMethodOptions = pmo;
(
Option::<payments::MandateData>::foreign_try_from((
mandate_options,
item.currency.to_owned(),
))?,
Some(api_enums::AuthenticationType::foreign_from(
request_three_d_secure,
)),
)
}
None => (None, None),
};
let request = Ok(Self {
amount: Some(api_types::Amount::Zero), amount: Some(api_types::Amount::Zero),
currency: Some(api_enums::Currency::default()),
capture_method: None, capture_method: None,
amount_to_capture: None, amount_to_capture: None,
confirm: item.confirm, confirm: item.confirm,
customer_id: item.customer, customer_id: item.customer,
currency: item
.currency
.as_ref()
.map(|c| c.to_uppercase().parse_enum("currency"))
.transpose()
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "currency",
})?,
email: item.receipt_email, email: item.receipt_email,
name: item name: item
.billing_details .billing_details
@ -163,8 +204,13 @@ impl From<StripeSetupIntentRequest> for payments::PaymentsRequest {
statement_descriptor_suffix: item.statement_descriptor_suffix, statement_descriptor_suffix: item.statement_descriptor_suffix,
metadata: item.metadata, metadata: item.metadata,
client_secret: item.client_secret.map(|s| s.peek().clone()), client_secret: item.client_secret.map(|s| s.peek().clone()),
setup_future_usage: item.setup_future_usage,
merchant_connector_details: item.merchant_connector_details,
authentication_type,
mandate_data: mandate_options,
..Default::default() ..Default::default()
} });
request
} }
} }
@ -232,6 +278,32 @@ impl From<StripePaymentCancelRequest> for payments::PaymentsCancelRequest {
} }
} }
} }
#[derive(Default, Eq, PartialEq, Serialize)]
pub struct RedirectUrl {
pub return_url: Option<String>,
pub url: Option<String>,
}
#[derive(Eq, PartialEq, Serialize)]
pub struct StripeNextAction {
#[serde(rename = "type")]
stype: payments::NextActionType,
redirect_to_url: RedirectUrl,
}
pub(crate) fn into_stripe_next_action(
next_action: Option<payments::NextAction>,
return_url: Option<String>,
) -> Option<StripeNextAction> {
next_action.map(|n| StripeNextAction {
stype: n.next_action_type,
redirect_to_url: RedirectUrl {
return_url,
url: n.redirect_to_url,
},
})
}
#[derive(Default, Eq, PartialEq, Serialize)] #[derive(Default, Eq, PartialEq, Serialize)]
pub struct StripeSetupIntentResponse { pub struct StripeSetupIntentResponse {
pub id: Option<String>, pub id: Option<String>,
@ -241,8 +313,35 @@ pub struct StripeSetupIntentResponse {
#[serde(with = "common_utils::custom_serde::iso8601::option")] #[serde(with = "common_utils::custom_serde::iso8601::option")]
pub created: Option<time::PrimitiveDateTime>, pub created: Option<time::PrimitiveDateTime>,
pub customer: Option<String>, pub customer: Option<String>,
pub refunds: Option<Vec<refunds::RefundResponse>>, pub refunds: Option<Vec<stripe_refunds::StripeRefundResponse>>,
pub mandate_id: Option<String>, pub mandate_id: Option<String>,
pub next_action: Option<StripeNextAction>,
pub last_payment_error: Option<LastPaymentError>,
pub charges: payment_intent::Charges,
}
#[derive(Default, Eq, PartialEq, Serialize)]
pub struct LastPaymentError {
charge: Option<String>,
code: Option<String>,
decline_code: Option<String>,
message: String,
param: Option<String>,
payment_method: StripePaymentMethod,
#[serde(rename = "type")]
error_type: String,
}
#[derive(Default, Eq, PartialEq, Serialize)]
pub struct StripePaymentMethod {
#[serde(rename = "id")]
payment_method_id: String,
object: &'static str,
card: Option<StripeCard>,
created: u64,
#[serde(rename = "type")]
method_type: String,
livemode: bool,
} }
impl From<payments::PaymentsResponse> for StripeSetupIntentResponse { impl From<payments::PaymentsResponse> for StripeSetupIntentResponse {
@ -251,11 +350,36 @@ impl From<payments::PaymentsResponse> for StripeSetupIntentResponse {
object: "setup_intent".to_owned(), object: "setup_intent".to_owned(),
status: StripeSetupStatus::from(resp.status), status: StripeSetupStatus::from(resp.status),
client_secret: resp.client_secret, client_secret: resp.client_secret,
charges: payment_intent::Charges::new(),
created: resp.created, created: resp.created,
customer: resp.customer_id, customer: resp.customer_id,
id: resp.payment_id, id: resp.payment_id,
refunds: resp.refunds, refunds: resp
.refunds
.map(|a| a.into_iter().map(Into::into).collect()),
mandate_id: resp.mandate_id, mandate_id: resp.mandate_id,
next_action: into_stripe_next_action(resp.next_action, resp.return_url),
last_payment_error: resp.error_code.map(|code| -> LastPaymentError {
LastPaymentError {
charge: None,
code: Some(code.to_owned()),
decline_code: None,
message: resp
.error_message
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
param: None,
payment_method: StripePaymentMethod {
payment_method_id: "place_holder_id".to_string(),
object: "payment_method",
card: None,
created: u64::try_from(date_time::now().assume_utc().unix_timestamp())
.unwrap_or_default(),
method_type: "card".to_string(),
livemode: false,
},
error_type: code,
}
}),
} }
} }
} }

View File

@ -266,6 +266,7 @@ pub struct Jwekey {
pub locker_decryption_key2: String, pub locker_decryption_key2: String,
pub vault_encryption_key: String, pub vault_encryption_key: String,
pub vault_private_key: String, pub vault_private_key: String,
pub tunnel_private_key: String,
} }
#[derive(Debug, Deserialize, Clone, Default)] #[derive(Debug, Deserialize, Clone, Default)]

View File

@ -489,20 +489,22 @@ impl
types::PaymentsResponseData: Clone, types::PaymentsResponseData: Clone,
{ {
let id = data.request.connector_transaction_id.clone(); let id = data.request.connector_transaction_id.clone();
let response: transformers::PaymentIntentSyncResponse = let response: transformers::PaymentIntentSyncResponse = match id
match id.get_connector_transaction_id() { .get_connector_transaction_id()
Ok(x) if x.starts_with("set") => res {
.response Ok(x) if x.starts_with("set") => res
.parse_struct("SetupIntentSyncResponse") .response
.change_context(errors::ConnectorError::ResponseDeserializationFailed), .parse_struct::<transformers::SetupIntentSyncResponse>("SetupIntentSyncResponse")
Ok(_) => res .change_context(errors::ConnectorError::ResponseDeserializationFailed)
.response .map(Into::into),
.parse_struct("PaymentIntentSyncResponse") Ok(_) => res
.change_context(errors::ConnectorError::ResponseDeserializationFailed), .response
Err(err) => { .parse_struct("PaymentIntentSyncResponse")
Err(err).change_context(errors::ConnectorError::MissingConnectorTransactionID) .change_context(errors::ConnectorError::ResponseDeserializationFailed),
} Err(err) => {
}?; Err(err).change_context(errors::ConnectorError::MissingConnectorTransactionID)
}
}?;
types::RouterData::try_from(types::ResponseRouterData { types::RouterData::try_from(types::ResponseRouterData {
response, response,

View File

@ -1,12 +1,14 @@
use std::borrow::Cow; use std::borrow::Cow;
use base64::Engine;
use common_utils::{ use common_utils::{
ext_traits::{AsyncExt, ByteSliceExt, ValueExt}, ext_traits::{AsyncExt, ByteSliceExt, ValueExt},
fp_utils, fp_utils,
}; };
// TODO : Evaluate all the helper functions () // TODO : Evaluate all the helper functions ()
use error_stack::{report, IntoReport, ResultExt}; use error_stack::{report, IntoReport, ResultExt};
#[cfg(feature = "kms")]
use external_services::kms;
use josekit::jwe;
use masking::{ExposeOptionInterface, PeekInterface}; use masking::{ExposeOptionInterface, PeekInterface};
use router_env::{instrument, tracing}; use router_env::{instrument, tracing};
use storage_models::{enums, merchant_account, payment_intent}; use storage_models::{enums, merchant_account, payment_intent};
@ -1546,11 +1548,12 @@ impl MerchantConnectorAccountType {
} }
pub async fn get_merchant_connector_account( pub async fn get_merchant_connector_account(
db: &dyn StorageInterface, state: &AppState,
merchant_id: &str, merchant_id: &str,
connector_label: &str, connector_label: &str,
creds_identifier: Option<String>, creds_identifier: Option<String>,
) -> RouterResult<MerchantConnectorAccountType> { ) -> RouterResult<MerchantConnectorAccountType> {
let db = &*state.store;
match creds_identifier { match creds_identifier {
Some(creds_identifier) => { Some(creds_identifier) => {
let mca_config = db let mca_config = db
@ -1560,20 +1563,35 @@ pub async fn get_merchant_connector_account(
errors::ApiErrorResponse::MerchantConnectorAccountNotFound, errors::ApiErrorResponse::MerchantConnectorAccountNotFound,
)?; )?;
let cached_mca = consts::BASE64_ENGINE #[cfg(feature = "kms")]
.decode(mca_config.config.as_bytes()) let kms_config = &state.conf.kms;
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"Failed to decode merchant_connector_details sent in request and then put in cache",
)?
.parse_struct("MerchantConnectorDetails")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"Failed to parse merchant_connector_details sent in request and then put in cache",
)?;
Ok(MerchantConnectorAccountType::CacheVal(cached_mca)) #[cfg(feature = "kms")]
let private_key = kms::get_kms_client(kms_config)
.await
.decrypt(state.conf.jwekey.tunnel_private_key.to_owned())
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error getting tunnel private key")?;
#[cfg(not(feature = "kms"))]
let private_key = state.conf.jwekey.tunnel_private_key.to_owned();
let decrypted_mca = services::decrypt_jwe(mca_config.config.as_str(), services::KeyIdCheck::SkipKeyIdCheck, private_key, jwe::RSA_OAEP_256)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"Failed to decrypt merchant_connector_details sent in request and then put in cache",
)?;
let res = String::into_bytes(decrypted_mca)
.parse_struct("MerchantConnectorDetails")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"Failed to parse merchant_connector_details sent in request and then put in cache",
)?;
Ok(MerchantConnectorAccountType::CacheVal(res))
} }
None => db None => db
.find_merchant_connector_account_by_merchant_id_connector_label( .find_merchant_connector_account_by_merchant_id_connector_label(

View File

@ -44,9 +44,8 @@ where
connector_id, connector_id,
); );
let db = &*state.store;
merchant_connector_account = helpers::get_merchant_connector_account( merchant_connector_account = helpers::get_merchant_connector_account(
db, state,
merchant_account.merchant_id.as_str(), merchant_account.merchant_id.as_str(),
&connector_label, &connector_label,
payment_data.creds_identifier.to_owned(), payment_data.creds_identifier.to_owned(),
@ -695,6 +694,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::VerifyRequestDat
off_session: payment_data.mandate_id.as_ref().map(|_| true), off_session: payment_data.mandate_id.as_ref().map(|_| true),
mandate_id: payment_data.mandate_id.clone(), mandate_id: payment_data.mandate_id.clone(),
setup_mandate_details: payment_data.setup_mandate, setup_mandate_details: payment_data.setup_mandate,
return_url: payment_data.payment_intent.return_url,
}) })
} }
} }

View File

@ -30,8 +30,6 @@ pub async fn construct_refund_router_data<'a, F>(
refund: &'a storage::Refund, refund: &'a storage::Refund,
creds_identifier: Option<String>, creds_identifier: Option<String>,
) -> RouterResult<types::RefundsRouterData<F>> { ) -> RouterResult<types::RefundsRouterData<F>> {
let db = &*state.store;
let connector_label = helpers::get_connector_label( let connector_label = helpers::get_connector_label(
payment_intent.business_country, payment_intent.business_country,
&payment_intent.business_label, &payment_intent.business_label,
@ -40,7 +38,7 @@ pub async fn construct_refund_router_data<'a, F>(
); );
let merchant_connector_account = helpers::get_merchant_connector_account( let merchant_connector_account = helpers::get_merchant_connector_account(
db, state,
merchant_account.merchant_id.as_str(), merchant_account.merchant_id.as_str(),
&connector_label, &connector_label,
creds_identifier, creds_identifier,
@ -234,7 +232,6 @@ pub async fn construct_accept_dispute_router_data<'a>(
merchant_account: &storage::MerchantAccount, merchant_account: &storage::MerchantAccount,
dispute: &storage::Dispute, dispute: &storage::Dispute,
) -> RouterResult<types::AcceptDisputeRouterData> { ) -> RouterResult<types::AcceptDisputeRouterData> {
let db = &*state.store;
let connector_id = &dispute.connector; let connector_id = &dispute.connector;
let connector_label = helpers::get_connector_label( let connector_label = helpers::get_connector_label(
payment_intent.business_country, payment_intent.business_country,
@ -243,7 +240,7 @@ pub async fn construct_accept_dispute_router_data<'a>(
connector_id, connector_id,
); );
let merchant_connector_account = helpers::get_merchant_connector_account( let merchant_connector_account = helpers::get_merchant_connector_account(
db, state,
merchant_account.merchant_id.as_str(), merchant_account.merchant_id.as_str(),
&connector_label, &connector_label,
None, None,
@ -296,7 +293,6 @@ pub async fn construct_submit_evidence_router_data<'a>(
dispute: &storage::Dispute, dispute: &storage::Dispute,
submit_evidence_request_data: types::SubmitEvidenceRequestData, submit_evidence_request_data: types::SubmitEvidenceRequestData,
) -> RouterResult<types::SubmitEvidenceRouterData> { ) -> RouterResult<types::SubmitEvidenceRouterData> {
let db = &*state.store;
let connector_id = &dispute.connector; let connector_id = &dispute.connector;
let connector_label = helpers::get_connector_label( let connector_label = helpers::get_connector_label(
payment_intent.business_country, payment_intent.business_country,
@ -305,7 +301,7 @@ pub async fn construct_submit_evidence_router_data<'a>(
connector_id, connector_id,
); );
let merchant_connector_account = helpers::get_merchant_connector_account( let merchant_connector_account = helpers::get_merchant_connector_account(
db, state,
merchant_account.merchant_id.as_str(), merchant_account.merchant_id.as_str(),
&connector_label, &connector_label,
None, None,
@ -356,7 +352,6 @@ pub async fn construct_upload_file_router_data<'a>(
connector_id: &str, connector_id: &str,
file_key: String, file_key: String,
) -> RouterResult<types::UploadFileRouterData> { ) -> RouterResult<types::UploadFileRouterData> {
let db = &*state.store;
let connector_label = helpers::get_connector_label( let connector_label = helpers::get_connector_label(
payment_intent.business_country, payment_intent.business_country,
&payment_intent.business_label, &payment_intent.business_label,
@ -364,7 +359,7 @@ pub async fn construct_upload_file_router_data<'a>(
connector_id, connector_id,
); );
let merchant_connector_account = helpers::get_merchant_connector_account( let merchant_connector_account = helpers::get_merchant_connector_account(
db, state,
merchant_account.merchant_id.as_str(), merchant_account.merchant_id.as_str(),
&connector_label, &connector_label,
None, None,
@ -418,7 +413,7 @@ pub async fn construct_defend_dispute_router_data<'a>(
merchant_account: &storage::MerchantAccount, merchant_account: &storage::MerchantAccount,
dispute: &storage::Dispute, dispute: &storage::Dispute,
) -> RouterResult<types::DefendDisputeRouterData> { ) -> RouterResult<types::DefendDisputeRouterData> {
let db = &*state.store; let _db = &*state.store;
let connector_id = &dispute.connector; let connector_id = &dispute.connector;
let connector_label = helpers::get_connector_label( let connector_label = helpers::get_connector_label(
payment_intent.business_country, payment_intent.business_country,
@ -427,7 +422,7 @@ pub async fn construct_defend_dispute_router_data<'a>(
connector_id, connector_id,
); );
let merchant_connector_account = helpers::get_merchant_connector_account( let merchant_connector_account = helpers::get_merchant_connector_account(
db, state,
merchant_account.merchant_id.as_str(), merchant_account.merchant_id.as_str(),
&connector_label, &connector_label,
None, None,

View File

@ -299,6 +299,7 @@ pub struct VerifyRequestData {
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 setup_mandate_details: Option<payments::MandateData>, pub setup_mandate_details: Option<payments::MandateData>,
pub return_url: Option<String>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]