feat(core): [CYBERSOURCE] Add original authorized amount in router data (#3417)

Co-authored-by: Samraat Bansal <samraat.bansal@juspay.in>
Co-authored-by: SamraatBansal <55536657+SamraatBansal@users.noreply.github.com>
This commit is contained in:
DEEPANSHU BANSAL
2024-01-25 12:43:48 +05:30
committed by GitHub
parent fc6e68f7f0
commit 47fbe486ce
5 changed files with 106 additions and 40 deletions

View File

@ -442,7 +442,7 @@ pub struct ClientRiskInformationRules {
#[serde(rename_all = "camelCase")]
pub struct Avs {
code: String,
code_raw: String,
code_raw: Option<String>,
}
impl

View File

@ -10,7 +10,7 @@ use crate::{
connector::utils::{
self, AddressDetailsData, ApplePayDecrypt, CardData, PaymentsAuthorizeRequestData,
PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingData,
PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RouterData,
PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData,
},
consts,
core::errors,
@ -47,6 +47,7 @@ impl<T>
T,
),
) -> Result<Self, Self::Error> {
// This conversion function is used at different places in the file, if updating this, keep a check for those
let amount = utils::get_amount_as_string(currency_unit, amount, currency)?;
Ok(Self {
amount,
@ -81,11 +82,11 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest {
Some(vec![CybersourceActionsList::TokenCreate]),
Some(vec![CybersourceActionsTokenType::PaymentInstrument]),
Some(CybersourceAuthorizationOptions {
initiator: CybersourcePaymentInitiator {
initiator: Some(CybersourcePaymentInitiator {
initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer),
credential_stored_on_file: Some(true),
stored_credential_used: None,
},
}),
merchant_intitiated_transaction: None,
}),
);
@ -272,14 +273,16 @@ pub enum CybersourceActionsTokenType {
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CybersourceAuthorizationOptions {
initiator: CybersourcePaymentInitiator,
initiator: Option<CybersourcePaymentInitiator>,
merchant_intitiated_transaction: Option<MerchantInitiatedTransaction>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MerchantInitiatedTransaction {
reason: String,
reason: Option<String>,
//Required for recurring mandates payment
original_authorized_amount: Option<String>,
}
#[derive(Debug, Serialize)]
@ -470,35 +473,60 @@ impl From<&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>>
}
impl
From<(
TryFrom<(
&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>,
Option<PaymentSolution>,
)> for ProcessingInformation
{
fn from(
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
(item, solution): (
&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>,
Option<PaymentSolution>,
),
) -> Self {
) -> Result<Self, Self::Error> {
let (action_list, action_token_types, authorization_options) =
if item.router_data.request.setup_mandate_details.is_some() {
(
Some(vec![CybersourceActionsList::TokenCreate]),
Some(vec![CybersourceActionsTokenType::PaymentInstrument]),
Some(CybersourceAuthorizationOptions {
initiator: CybersourcePaymentInitiator {
initiator: Some(CybersourcePaymentInitiator {
initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer),
credential_stored_on_file: Some(true),
stored_credential_used: None,
},
}),
merchant_intitiated_transaction: None,
}),
)
} else if item.router_data.request.connector_mandate_id().is_some() {
let original_amount = item
.router_data
.get_recurring_mandate_payment_data()?
.get_original_payment_amount()?;
let original_currency = item
.router_data
.get_recurring_mandate_payment_data()?
.get_original_payment_currency()?;
(
None,
None,
Some(CybersourceAuthorizationOptions {
initiator: None,
merchant_intitiated_transaction: Some(MerchantInitiatedTransaction {
reason: None,
original_authorized_amount: Some(utils::get_amount_as_string(
&types::api::CurrencyUnit::Base,
original_amount,
original_currency,
)?),
}),
}),
)
} else {
(None, None, None)
};
Self {
Ok(Self {
capture: Some(matches!(
item.router_data.request.capture_method,
Some(enums::CaptureMethod::Automatic) | None
@ -509,7 +537,7 @@ impl
authorization_options,
capture_options: None,
commerce_indicator: String::from("internet"),
}
})
}
}
@ -533,11 +561,11 @@ impl
Some(vec![CybersourceActionsList::TokenCreate]),
Some(vec![CybersourceActionsTokenType::PaymentInstrument]),
Some(CybersourceAuthorizationOptions {
initiator: CybersourcePaymentInitiator {
initiator: Some(CybersourcePaymentInitiator {
initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer),
credential_stored_on_file: Some(true),
stored_credential_used: None,
},
}),
merchant_intitiated_transaction: None,
}),
)
@ -680,7 +708,7 @@ impl
},
});
let processing_information = ProcessingInformation::from((item, None));
let processing_information = ProcessingInformation::try_from((item, None))?;
let client_reference_information = ClientReferenceInformation::from(item);
let merchant_defined_information =
item.router_data.request.metadata.clone().map(|metadata| {
@ -792,7 +820,7 @@ impl
let bill_to = build_bill_to(item.router_data.get_billing()?, email)?;
let order_information = OrderInformationWithBill::from((item, bill_to));
let processing_information =
ProcessingInformation::from((item, Some(PaymentSolution::ApplePay)));
ProcessingInformation::try_from((item, Some(PaymentSolution::ApplePay)))?;
let client_reference_information = ClientReferenceInformation::from(item);
let expiration_month = apple_pay_data.get_expiry_month()?;
let expiration_year = apple_pay_data.get_four_digit_expiry_year()?;
@ -846,7 +874,7 @@ impl
},
});
let processing_information =
ProcessingInformation::from((item, Some(PaymentSolution::GooglePay)));
ProcessingInformation::try_from((item, Some(PaymentSolution::GooglePay)))?;
let client_reference_information = ClientReferenceInformation::from(item);
let merchant_defined_information =
item.router_data.request.metadata.clone().map(|metadata| {
@ -893,10 +921,9 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
build_bill_to(item.router_data.get_billing()?, email)?;
let order_information =
OrderInformationWithBill::from((item, bill_to));
let processing_information = ProcessingInformation::from((
item,
Some(PaymentSolution::ApplePay),
));
let processing_information = ProcessingInformation::try_from(
(item, Some(PaymentSolution::ApplePay)),
)?;
let client_reference_information =
ClientReferenceInformation::from(item);
let payment_information = PaymentInformation::ApplePayToken(
@ -1008,7 +1035,7 @@ impl
String,
),
) -> Result<Self, Self::Error> {
let processing_information = ProcessingInformation::from((item, None));
let processing_information = ProcessingInformation::try_from((item, None))?;
let payment_instrument = CybersoucrePaymentInstrument {
id: connector_mandate_id,
};
@ -1159,13 +1186,14 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsIncrementalAuthorizationRout
action_list: None,
action_token_types: None,
authorization_options: Some(CybersourceAuthorizationOptions {
initiator: CybersourcePaymentInitiator {
initiator: Some(CybersourcePaymentInitiator {
initiator_type: None,
credential_stored_on_file: None,
stored_credential_used: Some(true),
},
}),
merchant_intitiated_transaction: Some(MerchantInitiatedTransaction {
reason: "5".to_owned(),
reason: Some("5".to_owned()),
original_authorized_amount: None,
}),
}),
commerce_indicator: String::from("internet"),
@ -1339,18 +1367,6 @@ impl From<CybersourceIncrementalAuthorizationStatus> for common_enums::Authoriza
}
}
impl From<CybersourcePaymentStatus> for enums::RefundStatus {
fn from(item: CybersourcePaymentStatus) -> Self {
match item {
CybersourcePaymentStatus::Succeeded | CybersourcePaymentStatus::Transmitted => {
Self::Success
}
CybersourcePaymentStatus::Failed => Self::Failure,
_ => Self::Pending,
}
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum CybersourcePaymentsResponse {
@ -1430,7 +1446,7 @@ pub struct ClientProcessorInformation {
#[serde(rename_all = "camelCase")]
pub struct Avs {
code: String,
code_raw: String,
code_raw: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]

View File

@ -25,7 +25,7 @@ use crate::{
consts,
core::{
errors::{self, ApiErrorResponse, CustomResult},
payments::PaymentData,
payments::{PaymentData, RecurringMandatePaymentData},
},
pii::PeekInterface,
types::{
@ -81,6 +81,7 @@ pub trait RouterData {
fn get_customer_id(&self) -> Result<String, Error>;
fn get_connector_customer_id(&self) -> Result<String, Error>;
fn get_preprocessing_id(&self) -> Result<String, Error>;
fn get_recurring_mandate_payment_data(&self) -> Result<RecurringMandatePaymentData, Error>;
#[cfg(feature = "payouts")]
fn get_payout_method_data(&self) -> Result<api::PayoutMethodData, Error>;
#[cfg(feature = "payouts")]
@ -250,6 +251,12 @@ impl<Flow, Request, Response> RouterData for types::RouterData<Flow, Request, Re
.to_owned()
.ok_or_else(missing_field_err("preprocessing_id"))
}
fn get_recurring_mandate_payment_data(&self) -> Result<RecurringMandatePaymentData, Error> {
self.recurring_mandate_payment_data
.to_owned()
.ok_or_else(missing_field_err("recurring_mandate_payment_data"))
}
#[cfg(feature = "payouts")]
fn get_payout_method_data(&self) -> Result<api::PayoutMethodData, Error> {
self.payout_method_data
@ -1133,6 +1140,22 @@ impl MandateData for payments::MandateAmountData {
}
}
pub trait RecurringMandateData {
fn get_original_payment_amount(&self) -> Result<i64, Error>;
fn get_original_payment_currency(&self) -> Result<diesel_models::enums::Currency, Error>;
}
impl RecurringMandateData for RecurringMandatePaymentData {
fn get_original_payment_amount(&self) -> Result<i64, Error> {
self.original_payment_authorized_amount
.ok_or_else(missing_field_err("original_payment_authorized_amount"))
}
fn get_original_payment_currency(&self) -> Result<diesel_models::enums::Currency, Error> {
self.original_payment_authorized_currency
.ok_or_else(missing_field_err("original_payment_authorized_currency"))
}
}
pub trait MandateReferenceData {
fn get_connector_mandate_id(&self) -> Result<String, Error>;
}

View File

@ -2039,6 +2039,8 @@ pub struct IncrementalAuthorizationDetails {
#[derive(Debug, Default, Clone)]
pub struct RecurringMandatePaymentData {
pub payment_method_type: Option<storage_enums::PaymentMethodType>, //required for making recurring payment using saved payment method through stripe
pub original_payment_authorized_amount: Option<i64>,
pub original_payment_authorized_currency: Option<storage_enums::Currency>,
}
#[derive(Debug, Default, Clone)]

View File

@ -479,6 +479,27 @@ pub async fn get_token_for_recurring_mandate(
.await
.to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
let original_payment_intent = mandate
.original_payment_id
.as_ref()
.async_map(|payment_id| async {
db.find_payment_intent_by_payment_id_merchant_id(
payment_id,
&mandate.merchant_id,
merchant_account.storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
.map_err(|err| logger::error!(mandate_original_payment_not_found=?err))
.ok()
})
.await
.flatten();
let original_payment_authorized_amount = original_payment_intent.clone().map(|pi| pi.amount);
let original_payment_authorized_currency =
original_payment_intent.clone().and_then(|pi| pi.currency);
let customer = req.customer_id.clone().get_required_value("customer_id")?;
let payment_method_id = {
@ -540,6 +561,8 @@ pub async fn get_token_for_recurring_mandate(
Some(payment_method.payment_method),
Some(payments::RecurringMandatePaymentData {
payment_method_type,
original_payment_authorized_amount,
original_payment_authorized_currency,
}),
payment_method.payment_method_type,
Some(mandate_connector_details),
@ -550,6 +573,8 @@ pub async fn get_token_for_recurring_mandate(
Some(payment_method.payment_method),
Some(payments::RecurringMandatePaymentData {
payment_method_type,
original_payment_authorized_amount,
original_payment_authorized_currency,
}),
payment_method.payment_method_type,
Some(mandate_connector_details),