mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +08:00
fix: introduce net_amount field in payment response (#3115)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -2037,6 +2037,11 @@ pub struct PaymentsResponse {
|
|||||||
#[schema(example = 100)]
|
#[schema(example = 100)]
|
||||||
pub amount: i64,
|
pub amount: i64,
|
||||||
|
|
||||||
|
/// The payment net amount. net_amount = amount + surcharge_details.surcharge_amount + surcharge_details.tax_amount,
|
||||||
|
/// If no surcharge_details, net_amount = amount
|
||||||
|
#[schema(example = 110)]
|
||||||
|
pub net_amount: i64,
|
||||||
|
|
||||||
/// The maximum amount that could be captured from the payment
|
/// The maximum amount that could be captured from the payment
|
||||||
#[schema(minimum = 100, example = 6540)]
|
#[schema(minimum = 100, example = 6540)]
|
||||||
pub amount_capturable: Option<i64>,
|
pub amount_capturable: Option<i64>,
|
||||||
|
|||||||
@ -107,6 +107,7 @@ pub struct PaymentAttempt {
|
|||||||
pub attempt_id: String,
|
pub attempt_id: String,
|
||||||
pub status: storage_enums::AttemptStatus,
|
pub status: storage_enums::AttemptStatus,
|
||||||
pub amount: i64,
|
pub amount: i64,
|
||||||
|
pub net_amount: i64,
|
||||||
pub currency: Option<storage_enums::Currency>,
|
pub currency: Option<storage_enums::Currency>,
|
||||||
pub save_to_locker: Option<bool>,
|
pub save_to_locker: Option<bool>,
|
||||||
pub connector: Option<String>,
|
pub connector: Option<String>,
|
||||||
@ -183,6 +184,9 @@ pub struct PaymentAttemptNew {
|
|||||||
pub attempt_id: String,
|
pub attempt_id: String,
|
||||||
pub status: storage_enums::AttemptStatus,
|
pub status: storage_enums::AttemptStatus,
|
||||||
pub amount: i64,
|
pub amount: i64,
|
||||||
|
/// amount + surcharge_amount + tax_amount
|
||||||
|
/// This field will always be derived before updating in the Database
|
||||||
|
pub net_amount: i64,
|
||||||
pub currency: Option<storage_enums::Currency>,
|
pub currency: Option<storage_enums::Currency>,
|
||||||
// pub auto_capture: Option<bool>,
|
// pub auto_capture: Option<bool>,
|
||||||
pub save_to_locker: Option<bool>,
|
pub save_to_locker: Option<bool>,
|
||||||
@ -230,6 +234,19 @@ pub struct PaymentAttemptNew {
|
|||||||
pub unified_message: Option<String>,
|
pub unified_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PaymentAttemptNew {
|
||||||
|
/// returns amount + surcharge_amount + tax_amount
|
||||||
|
pub fn calculate_net_amount(&self) -> i64 {
|
||||||
|
self.amount + self.surcharge_amount.unwrap_or(0) + self.tax_amount.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn populate_derived_fields(self) -> Self {
|
||||||
|
let mut payment_attempt_new = self;
|
||||||
|
payment_attempt_new.net_amount = payment_attempt_new.calculate_net_amount();
|
||||||
|
payment_attempt_new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum PaymentAttemptUpdate {
|
pub enum PaymentAttemptUpdate {
|
||||||
Update {
|
Update {
|
||||||
|
|||||||
@ -63,6 +63,15 @@ pub struct PaymentAttempt {
|
|||||||
pub encoded_data: Option<String>,
|
pub encoded_data: Option<String>,
|
||||||
pub unified_code: Option<String>,
|
pub unified_code: Option<String>,
|
||||||
pub unified_message: Option<String>,
|
pub unified_message: Option<String>,
|
||||||
|
pub net_amount: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaymentAttempt {
|
||||||
|
pub fn get_or_calculate_net_amount(&self) -> i64 {
|
||||||
|
self.net_amount.unwrap_or(
|
||||||
|
self.amount + self.surcharge_amount.unwrap_or(0) + self.tax_amount.unwrap_or(0),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Queryable, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Queryable, Serialize, Deserialize)]
|
||||||
@ -128,6 +137,25 @@ pub struct PaymentAttemptNew {
|
|||||||
pub encoded_data: Option<String>,
|
pub encoded_data: Option<String>,
|
||||||
pub unified_code: Option<String>,
|
pub unified_code: Option<String>,
|
||||||
pub unified_message: Option<String>,
|
pub unified_message: Option<String>,
|
||||||
|
pub net_amount: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaymentAttemptNew {
|
||||||
|
/// returns amount + surcharge_amount + tax_amount
|
||||||
|
pub fn calculate_net_amount(&self) -> i64 {
|
||||||
|
self.amount + self.surcharge_amount.unwrap_or(0) + self.tax_amount.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_calculate_net_amount(&self) -> i64 {
|
||||||
|
self.net_amount
|
||||||
|
.unwrap_or_else(|| self.calculate_net_amount())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn populate_derived_fields(self) -> Self {
|
||||||
|
let mut payment_attempt_new = self;
|
||||||
|
payment_attempt_new.net_amount = Some(payment_attempt_new.calculate_net_amount());
|
||||||
|
payment_attempt_new
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -279,6 +307,7 @@ pub enum PaymentAttemptUpdate {
|
|||||||
#[diesel(table_name = payment_attempt)]
|
#[diesel(table_name = payment_attempt)]
|
||||||
pub struct PaymentAttemptUpdateInternal {
|
pub struct PaymentAttemptUpdateInternal {
|
||||||
amount: Option<i64>,
|
amount: Option<i64>,
|
||||||
|
net_amount: Option<i64>,
|
||||||
currency: Option<storage_enums::Currency>,
|
currency: Option<storage_enums::Currency>,
|
||||||
status: Option<storage_enums::AttemptStatus>,
|
status: Option<storage_enums::AttemptStatus>,
|
||||||
connector_transaction_id: Option<String>,
|
connector_transaction_id: Option<String>,
|
||||||
@ -316,10 +345,29 @@ pub struct PaymentAttemptUpdateInternal {
|
|||||||
unified_message: Option<Option<String>>,
|
unified_message: Option<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PaymentAttemptUpdateInternal {
|
||||||
|
pub fn populate_derived_fields(self, source: &PaymentAttempt) -> Self {
|
||||||
|
let mut update_internal = self;
|
||||||
|
update_internal.net_amount = Some(
|
||||||
|
update_internal.amount.unwrap_or(source.amount)
|
||||||
|
+ update_internal
|
||||||
|
.surcharge_amount
|
||||||
|
.or(source.surcharge_amount)
|
||||||
|
.unwrap_or(0)
|
||||||
|
+ update_internal
|
||||||
|
.tax_amount
|
||||||
|
.or(source.tax_amount)
|
||||||
|
.unwrap_or(0),
|
||||||
|
);
|
||||||
|
update_internal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PaymentAttemptUpdate {
|
impl PaymentAttemptUpdate {
|
||||||
pub fn apply_changeset(self, source: PaymentAttempt) -> PaymentAttempt {
|
pub fn apply_changeset(self, source: PaymentAttempt) -> PaymentAttempt {
|
||||||
let PaymentAttemptUpdateInternal {
|
let PaymentAttemptUpdateInternal {
|
||||||
amount,
|
amount,
|
||||||
|
net_amount,
|
||||||
currency,
|
currency,
|
||||||
status,
|
status,
|
||||||
connector_transaction_id,
|
connector_transaction_id,
|
||||||
@ -355,9 +403,10 @@ impl PaymentAttemptUpdate {
|
|||||||
encoded_data,
|
encoded_data,
|
||||||
unified_code,
|
unified_code,
|
||||||
unified_message,
|
unified_message,
|
||||||
} = self.into();
|
} = PaymentAttemptUpdateInternal::from(self).populate_derived_fields(&source);
|
||||||
PaymentAttempt {
|
PaymentAttempt {
|
||||||
amount: amount.unwrap_or(source.amount),
|
amount: amount.unwrap_or(source.amount),
|
||||||
|
net_amount: net_amount.or(source.net_amount),
|
||||||
currency: currency.or(source.currency),
|
currency: currency.or(source.currency),
|
||||||
status: status.unwrap_or(source.status),
|
status: status.unwrap_or(source.status),
|
||||||
connector_transaction_id: connector_transaction_id.or(source.connector_transaction_id),
|
connector_transaction_id: connector_transaction_id.or(source.connector_transaction_id),
|
||||||
|
|||||||
@ -23,7 +23,7 @@ use crate::{
|
|||||||
impl PaymentAttemptNew {
|
impl PaymentAttemptNew {
|
||||||
#[instrument(skip(conn))]
|
#[instrument(skip(conn))]
|
||||||
pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<PaymentAttempt> {
|
pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<PaymentAttempt> {
|
||||||
generics::generic_insert(conn, self).await
|
generics::generic_insert(conn, self.populate_derived_fields()).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ impl PaymentAttempt {
|
|||||||
dsl::attempt_id
|
dsl::attempt_id
|
||||||
.eq(self.attempt_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).populate_derived_fields(&self),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|||||||
@ -643,6 +643,7 @@ diesel::table! {
|
|||||||
unified_code -> Nullable<Varchar>,
|
unified_code -> Nullable<Varchar>,
|
||||||
#[max_length = 1024]
|
#[max_length = 1024]
|
||||||
unified_message -> Nullable<Varchar>,
|
unified_message -> Nullable<Varchar>,
|
||||||
|
net_amount -> Nullable<Int8>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,7 @@ pub struct PaymentAttemptBatchNew {
|
|||||||
pub encoded_data: Option<String>,
|
pub encoded_data: Option<String>,
|
||||||
pub unified_code: Option<String>,
|
pub unified_code: Option<String>,
|
||||||
pub unified_message: Option<String>,
|
pub unified_message: Option<String>,
|
||||||
|
pub net_amount: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -114,6 +115,7 @@ impl PaymentAttemptBatchNew {
|
|||||||
encoded_data: self.encoded_data,
|
encoded_data: self.encoded_data,
|
||||||
unified_code: self.unified_code,
|
unified_code: self.unified_code,
|
||||||
unified_message: self.unified_message,
|
unified_message: self.unified_message,
|
||||||
|
net_amount: self.net_amount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3089,8 +3089,8 @@ impl AttemptType {
|
|||||||
|
|
||||||
error_message: None,
|
error_message: None,
|
||||||
offer_amount: old_payment_attempt.offer_amount,
|
offer_amount: old_payment_attempt.offer_amount,
|
||||||
surcharge_amount: old_payment_attempt.surcharge_amount,
|
surcharge_amount: None,
|
||||||
tax_amount: old_payment_attempt.tax_amount,
|
tax_amount: None,
|
||||||
payment_method_id: None,
|
payment_method_id: None,
|
||||||
payment_method: None,
|
payment_method: None,
|
||||||
capture_method: old_payment_attempt.capture_method,
|
capture_method: old_payment_attempt.capture_method,
|
||||||
@ -3133,6 +3133,7 @@ impl AttemptType {
|
|||||||
merchant_connector_id: None,
|
merchant_connector_id: None,
|
||||||
unified_code: None,
|
unified_code: None,
|
||||||
unified_message: None,
|
unified_message: None,
|
||||||
|
net_amount: old_payment_attempt.amount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -565,6 +565,7 @@ where
|
|||||||
});
|
});
|
||||||
services::ApplicationResponse::JsonWithHeaders((
|
services::ApplicationResponse::JsonWithHeaders((
|
||||||
response
|
response
|
||||||
|
.set_net_amount(payment_attempt.net_amount)
|
||||||
.set_payment_id(Some(payment_attempt.payment_id))
|
.set_payment_id(Some(payment_attempt.payment_id))
|
||||||
.set_merchant_id(Some(payment_attempt.merchant_id))
|
.set_merchant_id(Some(payment_attempt.merchant_id))
|
||||||
.set_status(payment_intent.status)
|
.set_status(payment_intent.status)
|
||||||
@ -714,6 +715,7 @@ where
|
|||||||
}
|
}
|
||||||
None => services::ApplicationResponse::JsonWithHeaders((
|
None => services::ApplicationResponse::JsonWithHeaders((
|
||||||
api::PaymentsResponse {
|
api::PaymentsResponse {
|
||||||
|
net_amount: payment_attempt.net_amount,
|
||||||
payment_id: Some(payment_attempt.payment_id),
|
payment_id: Some(payment_attempt.payment_id),
|
||||||
merchant_id: Some(payment_attempt.merchant_id),
|
merchant_id: Some(payment_attempt.merchant_id),
|
||||||
status: payment_intent.status,
|
status: payment_intent.status,
|
||||||
|
|||||||
@ -97,7 +97,7 @@ impl PaymentAttemptInterface for MockDb {
|
|||||||
#[allow(clippy::as_conversions)]
|
#[allow(clippy::as_conversions)]
|
||||||
let id = payment_attempts.len() as i32;
|
let id = payment_attempts.len() as i32;
|
||||||
let time = common_utils::date_time::now();
|
let time = common_utils::date_time::now();
|
||||||
|
let payment_attempt = payment_attempt.populate_derived_fields();
|
||||||
let payment_attempt = PaymentAttempt {
|
let payment_attempt = PaymentAttempt {
|
||||||
id,
|
id,
|
||||||
payment_id: payment_attempt.payment_id,
|
payment_id: payment_attempt.payment_id,
|
||||||
@ -105,6 +105,7 @@ impl PaymentAttemptInterface for MockDb {
|
|||||||
attempt_id: payment_attempt.attempt_id,
|
attempt_id: payment_attempt.attempt_id,
|
||||||
status: payment_attempt.status,
|
status: payment_attempt.status,
|
||||||
amount: payment_attempt.amount,
|
amount: payment_attempt.amount,
|
||||||
|
net_amount: payment_attempt.net_amount,
|
||||||
currency: payment_attempt.currency,
|
currency: payment_attempt.currency,
|
||||||
save_to_locker: payment_attempt.save_to_locker,
|
save_to_locker: payment_attempt.save_to_locker,
|
||||||
connector: payment_attempt.connector,
|
connector: payment_attempt.connector,
|
||||||
|
|||||||
@ -331,6 +331,7 @@ impl<T: DatabaseStore> PaymentAttemptInterface for KVRouterStore<T> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
MerchantStorageScheme::RedisKv => {
|
MerchantStorageScheme::RedisKv => {
|
||||||
|
let payment_attempt = payment_attempt.populate_derived_fields();
|
||||||
let key = format!(
|
let key = format!(
|
||||||
"mid_{}_pid_{}",
|
"mid_{}_pid_{}",
|
||||||
payment_attempt.merchant_id, payment_attempt.payment_id
|
payment_attempt.merchant_id, payment_attempt.payment_id
|
||||||
@ -343,6 +344,7 @@ impl<T: DatabaseStore> PaymentAttemptInterface for KVRouterStore<T> {
|
|||||||
attempt_id: payment_attempt.attempt_id.clone(),
|
attempt_id: payment_attempt.attempt_id.clone(),
|
||||||
status: payment_attempt.status,
|
status: payment_attempt.status,
|
||||||
amount: payment_attempt.amount,
|
amount: payment_attempt.amount,
|
||||||
|
net_amount: payment_attempt.net_amount,
|
||||||
currency: payment_attempt.currency,
|
currency: payment_attempt.currency,
|
||||||
save_to_locker: payment_attempt.save_to_locker,
|
save_to_locker: payment_attempt.save_to_locker,
|
||||||
connector: payment_attempt.connector.clone(),
|
connector: payment_attempt.connector.clone(),
|
||||||
@ -1035,6 +1037,7 @@ impl DataModelExt for PaymentAttempt {
|
|||||||
attempt_id: self.attempt_id,
|
attempt_id: self.attempt_id,
|
||||||
status: self.status,
|
status: self.status,
|
||||||
amount: self.amount,
|
amount: self.amount,
|
||||||
|
net_amount: Some(self.net_amount),
|
||||||
currency: self.currency,
|
currency: self.currency,
|
||||||
save_to_locker: self.save_to_locker,
|
save_to_locker: self.save_to_locker,
|
||||||
connector: self.connector,
|
connector: self.connector,
|
||||||
@ -1081,6 +1084,7 @@ impl DataModelExt for PaymentAttempt {
|
|||||||
|
|
||||||
fn from_storage_model(storage_model: Self::StorageModel) -> Self {
|
fn from_storage_model(storage_model: Self::StorageModel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
net_amount: storage_model.get_or_calculate_net_amount(),
|
||||||
id: storage_model.id,
|
id: storage_model.id,
|
||||||
payment_id: storage_model.payment_id,
|
payment_id: storage_model.payment_id,
|
||||||
merchant_id: storage_model.merchant_id,
|
merchant_id: storage_model.merchant_id,
|
||||||
@ -1139,6 +1143,7 @@ impl DataModelExt for PaymentAttemptNew {
|
|||||||
|
|
||||||
fn to_storage_model(self) -> Self::StorageModel {
|
fn to_storage_model(self) -> Self::StorageModel {
|
||||||
DieselPaymentAttemptNew {
|
DieselPaymentAttemptNew {
|
||||||
|
net_amount: Some(self.net_amount),
|
||||||
payment_id: self.payment_id,
|
payment_id: self.payment_id,
|
||||||
merchant_id: self.merchant_id,
|
merchant_id: self.merchant_id,
|
||||||
attempt_id: self.attempt_id,
|
attempt_id: self.attempt_id,
|
||||||
@ -1189,6 +1194,7 @@ impl DataModelExt for PaymentAttemptNew {
|
|||||||
|
|
||||||
fn from_storage_model(storage_model: Self::StorageModel) -> Self {
|
fn from_storage_model(storage_model: Self::StorageModel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
net_amount: storage_model.get_or_calculate_net_amount(),
|
||||||
payment_id: storage_model.payment_id,
|
payment_id: storage_model.payment_id,
|
||||||
merchant_id: storage_model.merchant_id,
|
merchant_id: storage_model.merchant_id,
|
||||||
attempt_id: storage_model.attempt_id,
|
attempt_id: storage_model.attempt_id,
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
ALTER TABLE payment_attempt DROP COLUMN IF EXISTS net_amount;
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE payment_attempt
|
||||||
|
ADD COLUMN IF NOT EXISTS net_amount BIGINT;
|
||||||
|
-- Backfill
|
||||||
|
UPDATE payment_attempt pa
|
||||||
|
SET net_amount = pa.amount + COALESCE(pa.surcharge_amount, 0) + COALESCE(pa.tax_amount, 0);
|
||||||
@ -10275,6 +10275,7 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"status",
|
"status",
|
||||||
"amount",
|
"amount",
|
||||||
|
"net_amount",
|
||||||
"currency",
|
"currency",
|
||||||
"payment_method",
|
"payment_method",
|
||||||
"attempt_count"
|
"attempt_count"
|
||||||
@ -10309,6 +10310,12 @@
|
|||||||
"description": "The payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,",
|
"description": "The payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,",
|
||||||
"example": 100
|
"example": 100
|
||||||
},
|
},
|
||||||
|
"net_amount": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"description": "The payment net amount. net_amount = amount + surcharge_details.surcharge_amount + surcharge_details.tax_amount,\nIf no surcharge_details, net_amount = amount",
|
||||||
|
"example": 110
|
||||||
|
},
|
||||||
"amount_capturable": {
|
"amount_capturable": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
|
|||||||
Reference in New Issue
Block a user