refactor(connector): [Paypal] Add support for passing shipping_cost in Payment request (#6423)

This commit is contained in:
Swangi Kumari
2024-10-25 18:28:57 +05:30
committed by GitHub
parent 4647a2f6ae
commit b0d5c96b99
7 changed files with 115 additions and 24 deletions

View File

@ -71,17 +71,22 @@ pub struct PaymentsAuthorizeData {
/// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference. /// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.
pub merchant_order_reference_id: Option<String>, pub merchant_order_reference_id: Option<String>,
pub integrity_object: Option<AuthoriseIntegrityObject>, pub integrity_object: Option<AuthoriseIntegrityObject>,
pub shipping_cost: Option<MinorUnit>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PaymentsPostSessionTokensData { pub struct PaymentsPostSessionTokensData {
// amount here would include amount, surcharge_amount and shipping_cost
pub amount: MinorUnit, pub amount: MinorUnit,
/// original amount sent by the merchant
pub order_amount: MinorUnit,
pub currency: storage_enums::Currency, pub currency: storage_enums::Currency,
pub capture_method: Option<storage_enums::CaptureMethod>, pub capture_method: Option<storage_enums::CaptureMethod>,
/// Merchant's identifier for the payment/invoice. This will be sent to the connector /// Merchant's identifier for the payment/invoice. This will be sent to the connector
/// if the connector provides support to accept multiple reference ids. /// if the connector provides support to accept multiple reference ids.
/// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference. /// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.
pub merchant_order_reference_id: Option<String>, pub merchant_order_reference_id: Option<String>,
pub shipping_cost: Option<MinorUnit>,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -833,10 +838,13 @@ pub struct PaymentsTaxCalculationData {
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct SdkPaymentsSessionUpdateData { pub struct SdkPaymentsSessionUpdateData {
pub order_tax_amount: MinorUnit, pub order_tax_amount: MinorUnit,
pub net_amount: MinorUnit, // amount here would include amount, surcharge_amount, order_tax_amount and shipping_cost
pub amount: MinorUnit, pub amount: MinorUnit,
/// original amount sent by the merchant
pub order_amount: MinorUnit,
pub currency: storage_enums::Currency, pub currency: storage_enums::Currency,
pub session_id: Option<String>, pub session_id: Option<String>,
pub shipping_cost: Option<MinorUnit>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -862,4 +870,5 @@ pub struct SetupMandateRequestData {
// MinorUnit for amount framework // MinorUnit for amount framework
pub minor_amount: Option<MinorUnit>, pub minor_amount: Option<MinorUnit>,
pub shipping_cost: Option<MinorUnit>,
} }

View File

@ -5,7 +5,7 @@ use base64::Engine;
use common_utils::{ use common_utils::{
ext_traits::ByteSliceExt, ext_traits::ByteSliceExt,
request::RequestContent, request::RequestContent,
types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector}, types::{AmountConvertor, MinorUnit, StringMajorUnit, StringMajorUnitForConnector},
}; };
use diesel_models::enums; use diesel_models::enums;
use error_stack::ResultExt; use error_stack::ResultExt;
@ -484,7 +484,8 @@ impl ConnectorIntegration<api::PoFulfill, types::PayoutsData, types::PayoutsResp
req.request.minor_amount, req.request.minor_amount,
req.request.destination_currency, req.request.destination_currency,
)?; )?;
let connector_router_data = paypal::PaypalRouterData::try_from((amount, None, None, req))?; let connector_router_data =
paypal::PaypalRouterData::try_from((amount, None, None, None, req))?;
let connector_req = paypal::PaypalFulfillRequest::try_from(&connector_router_data)?; let connector_req = paypal::PaypalFulfillRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -710,7 +711,23 @@ impl
req.request.amount, req.request.amount,
req.request.currency, req.request.currency,
)?; )?;
let connector_router_data = paypal::PaypalRouterData::try_from((amount, None, None, req))?; let shipping_cost = connector_utils::convert_amount(
self.amount_converter,
req.request.shipping_cost.unwrap_or(MinorUnit::zero()),
req.request.currency,
)?;
let order_amount = connector_utils::convert_amount(
self.amount_converter,
req.request.order_amount,
req.request.currency,
)?;
let connector_router_data = paypal::PaypalRouterData::try_from((
amount,
Some(shipping_cost),
None,
Some(order_amount),
req,
))?;
let connector_req = paypal::PaypalPaymentsRequest::try_from(&connector_router_data)?; let connector_req = paypal::PaypalPaymentsRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -810,12 +827,12 @@ impl
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let order_amount = connector_utils::convert_amount( let order_amount = connector_utils::convert_amount(
self.amount_converter, self.amount_converter,
req.request.amount, req.request.order_amount,
req.request.currency, req.request.currency,
)?; )?;
let amount = connector_utils::convert_amount( let amount = connector_utils::convert_amount(
self.amount_converter, self.amount_converter,
req.request.net_amount, req.request.amount,
req.request.currency, req.request.currency,
)?; )?;
let order_tax_amount = connector_utils::convert_amount( let order_tax_amount = connector_utils::convert_amount(
@ -823,8 +840,14 @@ impl
req.request.order_tax_amount, req.request.order_tax_amount,
req.request.currency, req.request.currency,
)?; )?;
let shipping_cost = connector_utils::convert_amount(
self.amount_converter,
req.request.shipping_cost.unwrap_or(MinorUnit::zero()),
req.request.currency,
)?;
let connector_router_data = paypal::PaypalRouterData::try_from(( let connector_router_data = paypal::PaypalRouterData::try_from((
amount, amount,
Some(shipping_cost),
Some(order_tax_amount), Some(order_tax_amount),
Some(order_amount), Some(order_amount),
req, req,
@ -915,7 +938,13 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
req.request.minor_amount, req.request.minor_amount,
req.request.currency, req.request.currency,
)?; )?;
let connector_router_data = paypal::PaypalRouterData::try_from((amount, None, None, req))?; let shipping_cost = connector_utils::convert_amount(
self.amount_converter,
req.request.shipping_cost.unwrap_or(MinorUnit::zero()),
req.request.currency,
)?;
let connector_router_data =
paypal::PaypalRouterData::try_from((amount, Some(shipping_cost), None, None, req))?;
let connector_req = paypal::PaypalPaymentsRequest::try_from(&connector_router_data)?; let connector_req = paypal::PaypalPaymentsRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -1432,7 +1461,7 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
req.request.currency, req.request.currency,
)?; )?;
let connector_router_data = let connector_router_data =
paypal::PaypalRouterData::try_from((amount_to_capture, None, None, req))?; paypal::PaypalRouterData::try_from((amount_to_capture, None, None, None, req))?;
let connector_req = paypal::PaypalPaymentsCaptureRequest::try_from(&connector_router_data)?; let connector_req = paypal::PaypalPaymentsCaptureRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -1599,7 +1628,8 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
req.request.minor_refund_amount, req.request.minor_refund_amount,
req.request.currency, req.request.currency,
)?; )?;
let connector_router_data = paypal::PaypalRouterData::try_from((amount, None, None, req))?; let connector_router_data =
paypal::PaypalRouterData::try_from((amount, None, None, None, req))?;
let connector_req = paypal::PaypalRefundRequest::try_from(&connector_router_data)?; let connector_req = paypal::PaypalRefundRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }

View File

@ -28,6 +28,7 @@ use crate::{
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct PaypalRouterData<T> { pub struct PaypalRouterData<T> {
pub amount: StringMajorUnit, pub amount: StringMajorUnit,
pub shipping_cost: Option<StringMajorUnit>,
pub order_tax_amount: Option<StringMajorUnit>, pub order_tax_amount: Option<StringMajorUnit>,
pub order_amount: Option<StringMajorUnit>, pub order_amount: Option<StringMajorUnit>,
pub router_data: T, pub router_data: T,
@ -38,20 +39,23 @@ impl<T>
StringMajorUnit, StringMajorUnit,
Option<StringMajorUnit>, Option<StringMajorUnit>,
Option<StringMajorUnit>, Option<StringMajorUnit>,
Option<StringMajorUnit>,
T, T,
)> for PaypalRouterData<T> )> for PaypalRouterData<T>
{ {
type Error = error_stack::Report<errors::ConnectorError>; type Error = error_stack::Report<errors::ConnectorError>;
fn try_from( fn try_from(
(amount, order_tax_amount, order_amount, item): ( (amount, shipping_cost, order_tax_amount, order_amount, item): (
StringMajorUnit, StringMajorUnit,
Option<StringMajorUnit>, Option<StringMajorUnit>,
Option<StringMajorUnit>, Option<StringMajorUnit>,
Option<StringMajorUnit>,
T, T,
), ),
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
Ok(Self { Ok(Self {
amount, amount,
shipping_cost,
order_tax_amount, order_tax_amount,
order_amount, order_amount,
router_data: item, router_data: item,
@ -107,24 +111,47 @@ impl From<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for OrderReque
value: item.amount.clone(), value: item.amount.clone(),
}, },
tax_total: None, tax_total: None,
shipping: Some(OrderAmount {
currency_code: item.router_data.request.currency,
value: item
.shipping_cost
.clone()
.unwrap_or(StringMajorUnit::zero()),
}),
}, },
} }
} }
} }
impl From<&PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>> for OrderRequestAmount { impl TryFrom<&PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>>
fn from(item: &PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>) -> Self { for OrderRequestAmount
Self { {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: &PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>,
) -> Result<Self, Self::Error> {
Ok(Self {
currency_code: item.router_data.request.currency, currency_code: item.router_data.request.currency,
value: item.amount.clone(), value: item.amount.clone(),
breakdown: AmountBreakdown { breakdown: AmountBreakdown {
item_total: OrderAmount { item_total: OrderAmount {
currency_code: item.router_data.request.currency, currency_code: item.router_data.request.currency,
value: item.amount.clone(), value: item.order_amount.clone().ok_or(
errors::ConnectorError::MissingRequiredField {
field_name: "order_amount",
},
)?,
}, },
tax_total: None, tax_total: None,
shipping: Some(OrderAmount {
currency_code: item.router_data.request.currency,
value: item
.shipping_cost
.clone()
.unwrap_or(StringMajorUnit::zero()),
}),
}, },
} })
} }
} }
@ -153,6 +180,13 @@ impl TryFrom<&PaypalRouterData<&types::SdkSessionUpdateRouterData>> for OrderReq
}, },
)?, )?,
}), }),
shipping: Some(OrderAmount {
currency_code: item.router_data.request.currency,
value: item
.shipping_cost
.clone()
.unwrap_or(StringMajorUnit::zero()),
}),
}, },
}) })
} }
@ -162,6 +196,7 @@ impl TryFrom<&PaypalRouterData<&types::SdkSessionUpdateRouterData>> for OrderReq
pub struct AmountBreakdown { pub struct AmountBreakdown {
item_total: OrderAmount, item_total: OrderAmount,
tax_total: Option<OrderAmount>, tax_total: Option<OrderAmount>,
shipping: Option<OrderAmount>,
} }
#[derive(Default, Debug, Serialize, Eq, PartialEq)] #[derive(Default, Debug, Serialize, Eq, PartialEq)]
@ -206,9 +241,12 @@ impl From<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for ItemDetail
} }
} }
impl From<&PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>> for ItemDetails { impl TryFrom<&PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>> for ItemDetails {
fn from(item: &PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>) -> Self { type Error = error_stack::Report<errors::ConnectorError>;
Self { fn try_from(
item: &PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>,
) -> Result<Self, Self::Error> {
Ok(Self {
name: format!( name: format!(
"Payment for invoice {}", "Payment for invoice {}",
item.router_data.connector_request_reference_id item.router_data.connector_request_reference_id
@ -216,10 +254,14 @@ impl From<&PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>> for It
quantity: ORDER_QUANTITY, quantity: ORDER_QUANTITY,
unit_amount: OrderAmount { unit_amount: OrderAmount {
currency_code: item.router_data.request.currency, currency_code: item.router_data.request.currency,
value: item.amount.clone(), value: item.order_amount.clone().ok_or(
errors::ConnectorError::MissingRequiredField {
field_name: "order_amount",
},
)?,
}, },
tax: None, tax: None,
} })
} }
} }
@ -549,12 +591,12 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsPostSessionTokensRouterData>>
PaypalAuthType::try_from(&item.router_data.connector_auth_type)?; PaypalAuthType::try_from(&item.router_data.connector_auth_type)?;
let payee = get_payee(&paypal_auth); let payee = get_payee(&paypal_auth);
let amount = OrderRequestAmount::from(item); let amount = OrderRequestAmount::try_from(item)?;
let connector_request_reference_id = let connector_request_reference_id =
item.router_data.connector_request_reference_id.clone(); item.router_data.connector_request_reference_id.clone();
let shipping_address = ShippingAddress::from(item); let shipping_address = ShippingAddress::from(item);
let item_details = vec![ItemDetails::from(item)]; let item_details = vec![ItemDetails::try_from(item)?];
let purchase_units = vec![PurchaseUnitRequest { let purchase_units = vec![PurchaseUnitRequest {
reference_id: Some(connector_request_reference_id.clone()), reference_id: Some(connector_request_reference_id.clone()),

View File

@ -278,6 +278,7 @@ pub async fn construct_payment_router_data_for_authorize<'a>(
charges: None, charges: None,
merchant_order_reference_id: None, merchant_order_reference_id: None,
integrity_object: None, integrity_object: None,
shipping_cost: payment_data.payment_intent.amount_details.shipping_cost,
}; };
// TODO: evaluate the fields in router data, if they are required or not // TODO: evaluate the fields in router data, if they are required or not
@ -2150,6 +2151,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
.payment_intent .payment_intent
.merchant_order_reference_id .merchant_order_reference_id
.clone(); .clone();
let shipping_cost = payment_data.payment_intent.shipping_cost;
Ok(Self { Ok(Self {
payment_method_data: (payment_method_data.get_required_value("payment_method_data")?), payment_method_data: (payment_method_data.get_required_value("payment_method_data")?),
@ -2196,6 +2198,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
charges, charges,
merchant_order_reference_id, merchant_order_reference_id,
integrity_object: None, integrity_object: None,
shipping_cost,
}) })
} }
} }
@ -2534,11 +2537,12 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::SdkPaymentsSessi
+ shipping_cost + shipping_cost
+ surcharge_amount; + surcharge_amount;
Ok(Self { Ok(Self {
net_amount, amount: net_amount,
order_tax_amount, order_tax_amount,
currency: payment_data.currency, currency: payment_data.currency,
amount: payment_data.payment_intent.amount, order_amount: payment_data.payment_intent.amount,
session_id: payment_data.session_id, session_id: payment_data.session_id,
shipping_cost: payment_data.payment_intent.shipping_cost,
}) })
} }
} }
@ -2575,9 +2579,11 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsPostSess
.clone(); .clone();
Ok(Self { Ok(Self {
amount, //need to change after we move to connector module amount, //need to change after we move to connector module
order_amount: payment_data.payment_intent.amount,
currency: payment_data.currency, currency: payment_data.currency,
merchant_order_reference_id, merchant_order_reference_id,
capture_method: payment_data.payment_attempt.capture_method, capture_method: payment_data.payment_attempt.capture_method,
shipping_cost: payment_data.payment_intent.shipping_cost,
}) })
} }
} }
@ -2770,6 +2776,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::SetupMandateRequ
| Some(RequestIncrementalAuthorization::Default) | Some(RequestIncrementalAuthorization::Default)
), ),
metadata: payment_data.payment_intent.metadata.clone().map(Into::into), metadata: payment_data.payment_intent.metadata.clone().map(Into::into),
shipping_cost: payment_data.payment_intent.shipping_cost,
}) })
} }
} }

View File

@ -879,6 +879,7 @@ impl ForeignFrom<&SetupMandateRouterData> for PaymentsAuthorizeData {
charges: None, // TODO: allow charges on mandates? charges: None, // TODO: allow charges on mandates?
merchant_order_reference_id: None, merchant_order_reference_id: None,
integrity_object: None, integrity_object: None,
shipping_cost: data.request.shipping_cost,
} }
} }
} }

View File

@ -57,6 +57,7 @@ impl VerifyConnectorData {
charges: None, charges: None,
merchant_order_reference_id: None, merchant_order_reference_id: None,
integrity_object: None, integrity_object: None,
shipping_cost: None,
} }
} }

View File

@ -947,6 +947,7 @@ impl Default for PaymentAuthorizeType {
charges: None, charges: None,
integrity_object: None, integrity_object: None,
merchant_order_reference_id: None, merchant_order_reference_id: None,
shipping_cost: None,
}; };
Self(data) Self(data)
} }