mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
refactor(payments_v2): use batch encryption for intent create and confirm intent (#6589)
Co-authored-by: Sanchith Hegde <sanchith.hegde@juspay.in> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
159
crates/hyperswitch_domain_models/src/address.rs
Normal file
159
crates/hyperswitch_domain_models/src/address.rs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
use masking::{PeekInterface, Secret};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct Address {
|
||||||
|
pub address: Option<AddressDetails>,
|
||||||
|
pub phone: Option<PhoneDetails>,
|
||||||
|
pub email: Option<common_utils::pii::Email>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl masking::SerializableSecret for Address {}
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
/// Unify the address, giving priority to `self` when details are present in both
|
||||||
|
pub fn unify_address(self, other: Option<&Self>) -> Self {
|
||||||
|
let other_address_details = other.and_then(|address| address.address.as_ref());
|
||||||
|
Self {
|
||||||
|
address: self
|
||||||
|
.address
|
||||||
|
.map(|address| address.unify_address_details(other_address_details))
|
||||||
|
.or(other_address_details.cloned()),
|
||||||
|
email: self.email.or(other.and_then(|other| other.email.clone())),
|
||||||
|
phone: self.phone.or(other.and_then(|other| other.phone.clone())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Eq, serde::Deserialize, serde::Serialize, PartialEq)]
|
||||||
|
pub struct AddressDetails {
|
||||||
|
pub city: Option<String>,
|
||||||
|
pub country: Option<common_enums::CountryAlpha2>,
|
||||||
|
pub line1: Option<Secret<String>>,
|
||||||
|
pub line2: Option<Secret<String>>,
|
||||||
|
pub line3: Option<Secret<String>>,
|
||||||
|
pub zip: Option<Secret<String>>,
|
||||||
|
pub state: Option<Secret<String>>,
|
||||||
|
pub first_name: Option<Secret<String>>,
|
||||||
|
pub last_name: Option<Secret<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressDetails {
|
||||||
|
pub fn get_optional_full_name(&self) -> Option<Secret<String>> {
|
||||||
|
match (self.first_name.as_ref(), self.last_name.as_ref()) {
|
||||||
|
(Some(first_name), Some(last_name)) => Some(Secret::new(format!(
|
||||||
|
"{} {}",
|
||||||
|
first_name.peek(),
|
||||||
|
last_name.peek()
|
||||||
|
))),
|
||||||
|
(Some(name), None) | (None, Some(name)) => Some(name.to_owned()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unify the address details, giving priority to `self` when details are present in both
|
||||||
|
pub fn unify_address_details(self, other: Option<&Self>) -> Self {
|
||||||
|
if let Some(other) = other {
|
||||||
|
let (first_name, last_name) = if self
|
||||||
|
.first_name
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|first_name| !first_name.peek().trim().is_empty())
|
||||||
|
{
|
||||||
|
(self.first_name, self.last_name)
|
||||||
|
} else {
|
||||||
|
(other.first_name.clone(), other.last_name.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
city: self.city.or(other.city.clone()),
|
||||||
|
country: self.country.or(other.country),
|
||||||
|
line1: self.line1.or(other.line1.clone()),
|
||||||
|
line2: self.line2.or(other.line2.clone()),
|
||||||
|
line3: self.line3.or(other.line3.clone()),
|
||||||
|
zip: self.zip.or(other.zip.clone()),
|
||||||
|
state: self.state.or(other.state.clone()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct PhoneDetails {
|
||||||
|
pub number: Option<Secret<String>>,
|
||||||
|
pub country_code: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<api_models::payments::Address> for Address {
|
||||||
|
fn from(address: api_models::payments::Address) -> Self {
|
||||||
|
Self {
|
||||||
|
address: address.address.map(AddressDetails::from),
|
||||||
|
phone: address.phone.map(PhoneDetails::from),
|
||||||
|
email: address.email,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<api_models::payments::AddressDetails> for AddressDetails {
|
||||||
|
fn from(address: api_models::payments::AddressDetails) -> Self {
|
||||||
|
Self {
|
||||||
|
city: address.city,
|
||||||
|
country: address.country,
|
||||||
|
line1: address.line1,
|
||||||
|
line2: address.line2,
|
||||||
|
line3: address.line3,
|
||||||
|
zip: address.zip,
|
||||||
|
state: address.state,
|
||||||
|
first_name: address.first_name,
|
||||||
|
last_name: address.last_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<api_models::payments::PhoneDetails> for PhoneDetails {
|
||||||
|
fn from(phone: api_models::payments::PhoneDetails) -> Self {
|
||||||
|
Self {
|
||||||
|
number: phone.number,
|
||||||
|
country_code: phone.country_code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Address> for api_models::payments::Address {
|
||||||
|
fn from(address: Address) -> Self {
|
||||||
|
Self {
|
||||||
|
address: address
|
||||||
|
.address
|
||||||
|
.map(api_models::payments::AddressDetails::from),
|
||||||
|
phone: address.phone.map(api_models::payments::PhoneDetails::from),
|
||||||
|
email: address.email,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AddressDetails> for api_models::payments::AddressDetails {
|
||||||
|
fn from(address: AddressDetails) -> Self {
|
||||||
|
Self {
|
||||||
|
city: address.city,
|
||||||
|
country: address.country,
|
||||||
|
line1: address.line1,
|
||||||
|
line2: address.line2,
|
||||||
|
line3: address.line3,
|
||||||
|
zip: address.zip,
|
||||||
|
state: address.state,
|
||||||
|
first_name: address.first_name,
|
||||||
|
last_name: address.last_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PhoneDetails> for api_models::payments::PhoneDetails {
|
||||||
|
fn from(phone: PhoneDetails) -> Self {
|
||||||
|
Self {
|
||||||
|
number: phone.number,
|
||||||
|
country_code: phone.country_code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
pub mod address;
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod behaviour;
|
pub mod behaviour;
|
||||||
pub mod business_profile;
|
pub mod business_profile;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
use api_models::payments::Address;
|
use common_utils::ext_traits::ValueExt;
|
||||||
use common_utils::{
|
use common_utils::{
|
||||||
self,
|
self,
|
||||||
crypto::Encryptable,
|
crypto::Encryptable,
|
||||||
@ -28,11 +28,13 @@ use common_enums as storage_enums;
|
|||||||
use diesel_models::types::{FeatureMetadata, OrderDetailsWithAmount};
|
use diesel_models::types::{FeatureMetadata, OrderDetailsWithAmount};
|
||||||
|
|
||||||
use self::payment_attempt::PaymentAttempt;
|
use self::payment_attempt::PaymentAttempt;
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
use crate::RemoteStorageObject;
|
use crate::RemoteStorageObject;
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
use crate::{business_profile, merchant_account};
|
use crate::{
|
||||||
#[cfg(feature = "v2")]
|
address::Address, business_profile, errors, merchant_account, payment_method_data,
|
||||||
use crate::{errors, payment_method_data, ApiModelToDieselModelConvertor};
|
ApiModelToDieselModelConvertor,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "v1")]
|
#[cfg(feature = "v1")]
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, ToEncryption)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, ToEncryption)]
|
||||||
@ -349,10 +351,10 @@ pub struct PaymentIntent {
|
|||||||
pub merchant_reference_id: Option<id_type::PaymentReferenceId>,
|
pub merchant_reference_id: Option<id_type::PaymentReferenceId>,
|
||||||
/// The billing address for the order in a denormalized form.
|
/// The billing address for the order in a denormalized form.
|
||||||
#[encrypt(ty = Value)]
|
#[encrypt(ty = Value)]
|
||||||
pub billing_address: Option<Encryptable<Secret<Address>>>,
|
pub billing_address: Option<Encryptable<Address>>,
|
||||||
/// The shipping address for the order in a denormalized form.
|
/// The shipping address for the order in a denormalized form.
|
||||||
#[encrypt(ty = Value)]
|
#[encrypt(ty = Value)]
|
||||||
pub shipping_address: Option<Encryptable<Secret<Address>>>,
|
pub shipping_address: Option<Encryptable<Address>>,
|
||||||
/// Capture method for the payment
|
/// Capture method for the payment
|
||||||
pub capture_method: storage_enums::CaptureMethod,
|
pub capture_method: storage_enums::CaptureMethod,
|
||||||
/// Authentication type that is requested by the merchant for this payment.
|
/// Authentication type that is requested by the merchant for this payment.
|
||||||
@ -416,8 +418,7 @@ impl PaymentIntent {
|
|||||||
merchant_account: &merchant_account::MerchantAccount,
|
merchant_account: &merchant_account::MerchantAccount,
|
||||||
profile: &business_profile::Profile,
|
profile: &business_profile::Profile,
|
||||||
request: api_models::payments::PaymentsCreateIntentRequest,
|
request: api_models::payments::PaymentsCreateIntentRequest,
|
||||||
billing_address: Option<Encryptable<Secret<Address>>>,
|
decrypted_payment_intent: DecryptedPaymentIntent,
|
||||||
shipping_address: Option<Encryptable<Secret<Address>>>,
|
|
||||||
) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
|
) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
|
||||||
let connector_metadata = request
|
let connector_metadata = request
|
||||||
.get_connector_metadata_as_value()
|
.get_connector_metadata_as_value()
|
||||||
@ -480,8 +481,26 @@ impl PaymentIntent {
|
|||||||
frm_metadata: request.frm_metadata,
|
frm_metadata: request.frm_metadata,
|
||||||
customer_details: None,
|
customer_details: None,
|
||||||
merchant_reference_id: request.merchant_reference_id,
|
merchant_reference_id: request.merchant_reference_id,
|
||||||
billing_address,
|
billing_address: decrypted_payment_intent
|
||||||
shipping_address,
|
.billing_address
|
||||||
|
.as_ref()
|
||||||
|
.map(|data| {
|
||||||
|
data.clone()
|
||||||
|
.deserialize_inner_value(|value| value.parse_value("Address"))
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
.change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Unable to decode billing address")?,
|
||||||
|
shipping_address: decrypted_payment_intent
|
||||||
|
.shipping_address
|
||||||
|
.as_ref()
|
||||||
|
.map(|data| {
|
||||||
|
data.clone()
|
||||||
|
.deserialize_inner_value(|value| value.parse_value("Address"))
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
.change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Unable to decode shipping address")?,
|
||||||
capture_method: request.capture_method.unwrap_or_default(),
|
capture_method: request.capture_method.unwrap_or_default(),
|
||||||
authentication_type: request.authentication_type.unwrap_or_default(),
|
authentication_type: request.authentication_type.unwrap_or_default(),
|
||||||
prerouting_algorithm: None,
|
prerouting_algorithm: None,
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
#[cfg(all(feature = "v1", feature = "olap"))]
|
#[cfg(all(feature = "v1", feature = "olap"))]
|
||||||
use api_models::enums::Connector;
|
use api_models::enums::Connector;
|
||||||
use common_enums as storage_enums;
|
use common_enums as storage_enums;
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
use common_utils::{
|
||||||
|
crypto::Encryptable, encryption::Encryption, ext_traits::ValueExt,
|
||||||
|
types::keymanager::ToEncryptable,
|
||||||
|
};
|
||||||
use common_utils::{
|
use common_utils::{
|
||||||
errors::{CustomResult, ValidationError},
|
errors::{CustomResult, ValidationError},
|
||||||
id_type, pii,
|
id_type, pii,
|
||||||
@ -18,15 +23,19 @@ use error_stack::ResultExt;
|
|||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
use masking::PeekInterface;
|
use masking::PeekInterface;
|
||||||
use masking::Secret;
|
use masking::Secret;
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
use serde_json::Value;
|
||||||
use time::PrimitiveDateTime;
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
#[cfg(all(feature = "v1", feature = "olap"))]
|
#[cfg(all(feature = "v1", feature = "olap"))]
|
||||||
use super::PaymentIntent;
|
use super::PaymentIntent;
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
use crate::merchant_key_store::MerchantKeyStore;
|
use crate::type_encryption::{crypto_operation, CryptoOperation};
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
use crate::router_response_types;
|
use crate::{address::Address, merchant_key_store::MerchantKeyStore, router_response_types};
|
||||||
use crate::{
|
use crate::{
|
||||||
behaviour, errors,
|
behaviour, errors,
|
||||||
mandates::{MandateDataType, MandateDetails},
|
mandates::{MandateDataType, MandateDetails},
|
||||||
@ -222,7 +231,7 @@ pub struct ErrorDetails {
|
|||||||
/// Few fields which are related are grouped together for better readability and understandability.
|
/// Few fields which are related are grouped together for better readability and understandability.
|
||||||
/// These fields will be flattened and stored in the database in individual columns
|
/// These fields will be flattened and stored in the database in individual columns
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, router_derive::ToEncryption)]
|
||||||
pub struct PaymentAttempt {
|
pub struct PaymentAttempt {
|
||||||
/// Payment id for the payment attempt
|
/// Payment id for the payment attempt
|
||||||
pub payment_id: id_type::GlobalPaymentId,
|
pub payment_id: id_type::GlobalPaymentId,
|
||||||
@ -259,12 +268,11 @@ pub struct PaymentAttempt {
|
|||||||
pub connector_metadata: Option<pii::SecretSerdeValue>,
|
pub connector_metadata: Option<pii::SecretSerdeValue>,
|
||||||
pub payment_experience: Option<storage_enums::PaymentExperience>,
|
pub payment_experience: Option<storage_enums::PaymentExperience>,
|
||||||
/// The insensitive data of the payment method data is stored here
|
/// The insensitive data of the payment method data is stored here
|
||||||
// TODO: evaluate what details should be stored here. Use a domain type instead of serde_json::Value
|
|
||||||
pub payment_method_data: Option<pii::SecretSerdeValue>,
|
pub payment_method_data: Option<pii::SecretSerdeValue>,
|
||||||
/// The result of the routing algorithm.
|
/// The result of the routing algorithm.
|
||||||
/// This will store the list of connectors and other related information that was used to route the payment.
|
/// This will store the list of connectors and other related information that was used to route the payment.
|
||||||
// TODO: change this to type instead of serde_json::Value
|
// TODO: change this to type instead of serde_json::Value
|
||||||
pub routing_result: Option<serde_json::Value>,
|
pub routing_result: Option<Value>,
|
||||||
pub preprocessing_step_id: Option<String>,
|
pub preprocessing_step_id: Option<String>,
|
||||||
/// Number of captures that have happened for the payment attempt
|
/// Number of captures that have happened for the payment attempt
|
||||||
pub multiple_capture_count: Option<i16>,
|
pub multiple_capture_count: Option<i16>,
|
||||||
@ -306,8 +314,8 @@ pub struct PaymentAttempt {
|
|||||||
/// A reference to the payment at connector side. This is returned by the connector
|
/// A reference to the payment at connector side. This is returned by the connector
|
||||||
pub external_reference_id: Option<String>,
|
pub external_reference_id: Option<String>,
|
||||||
/// The billing address for the payment method
|
/// The billing address for the payment method
|
||||||
// TODO: use a type here instead of value
|
#[encrypt(ty = Value)]
|
||||||
pub payment_method_billing_address: common_utils::crypto::OptionalEncryptableValue,
|
pub payment_method_billing_address: Option<Encryptable<Address>>,
|
||||||
/// The global identifier for the payment attempt
|
/// The global identifier for the payment attempt
|
||||||
pub id: id_type::GlobalAttemptId,
|
pub id: id_type::GlobalAttemptId,
|
||||||
/// The connector mandate details which are stored temporarily
|
/// The connector mandate details which are stored temporarily
|
||||||
@ -364,6 +372,7 @@ impl PaymentAttempt {
|
|||||||
cell_id: id_type::CellId,
|
cell_id: id_type::CellId,
|
||||||
storage_scheme: storage_enums::MerchantStorageScheme,
|
storage_scheme: storage_enums::MerchantStorageScheme,
|
||||||
request: &api_models::payments::PaymentsConfirmIntentRequest,
|
request: &api_models::payments::PaymentsConfirmIntentRequest,
|
||||||
|
encrypted_data: DecryptedPaymentAttempt,
|
||||||
) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
|
) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
|
||||||
let id = id_type::GlobalAttemptId::generate(&cell_id);
|
let id = id_type::GlobalAttemptId::generate(&cell_id);
|
||||||
let intent_amount_details = payment_intent.amount_details.clone();
|
let intent_amount_details = payment_intent.amount_details.clone();
|
||||||
@ -1755,13 +1764,39 @@ impl behaviour::Conversion for PaymentAttempt {
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
use crate::type_encryption;
|
|
||||||
|
|
||||||
async {
|
async {
|
||||||
let connector_payment_id = storage_model
|
let connector_payment_id = storage_model
|
||||||
.get_optional_connector_transaction_id()
|
.get_optional_connector_transaction_id()
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
|
let decrypted_data = crypto_operation(
|
||||||
|
state,
|
||||||
|
common_utils::type_name!(Self::DstType),
|
||||||
|
CryptoOperation::BatchDecrypt(EncryptedPaymentAttempt::to_encryptable(
|
||||||
|
EncryptedPaymentAttempt {
|
||||||
|
payment_method_billing_address: storage_model
|
||||||
|
.payment_method_billing_address,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
key_manager_identifier,
|
||||||
|
key.peek(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.and_then(|val| val.try_into_batchoperation())?;
|
||||||
|
|
||||||
|
let decrypted_data = EncryptedPaymentAttempt::from_encryptable(decrypted_data)
|
||||||
|
.change_context(common_utils::errors::CryptoError::DecodingFailed)
|
||||||
|
.attach_printable("Invalid batch operation data")?;
|
||||||
|
|
||||||
|
let payment_method_billing_address = decrypted_data
|
||||||
|
.payment_method_billing_address
|
||||||
|
.map(|billing| {
|
||||||
|
billing.deserialize_inner_value(|value| value.parse_value("Address"))
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
.change_context(common_utils::errors::CryptoError::DecodingFailed)
|
||||||
|
.attach_printable("Error while deserializing Address")?;
|
||||||
|
|
||||||
let amount_details = AttemptAmountDetails {
|
let amount_details = AttemptAmountDetails {
|
||||||
net_amount: storage_model.net_amount,
|
net_amount: storage_model.net_amount,
|
||||||
tax_on_surcharge: storage_model.tax_on_surcharge,
|
tax_on_surcharge: storage_model.tax_on_surcharge,
|
||||||
@ -1772,18 +1807,6 @@ impl behaviour::Conversion for PaymentAttempt {
|
|||||||
amount_to_capture: storage_model.amount_to_capture,
|
amount_to_capture: storage_model.amount_to_capture,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner_decrypt = |inner| async {
|
|
||||||
type_encryption::crypto_operation(
|
|
||||||
state,
|
|
||||||
common_utils::type_name!(Self::DstType),
|
|
||||||
type_encryption::CryptoOperation::DecryptOptional(inner),
|
|
||||||
key_manager_identifier.clone(),
|
|
||||||
key.peek(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.and_then(|val| val.try_into_optionaloperation())
|
|
||||||
};
|
|
||||||
|
|
||||||
let error = storage_model
|
let error = storage_model
|
||||||
.error_code
|
.error_code
|
||||||
.zip(storage_model.error_message)
|
.zip(storage_model.error_message)
|
||||||
@ -1838,10 +1861,7 @@ impl behaviour::Conversion for PaymentAttempt {
|
|||||||
authentication_applied: storage_model.authentication_applied,
|
authentication_applied: storage_model.authentication_applied,
|
||||||
external_reference_id: storage_model.external_reference_id,
|
external_reference_id: storage_model.external_reference_id,
|
||||||
connector: storage_model.connector,
|
connector: storage_model.connector,
|
||||||
payment_method_billing_address: inner_decrypt(
|
payment_method_billing_address,
|
||||||
storage_model.payment_method_billing_address,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
connector_mandate_detail: storage_model.connector_mandate_detail,
|
connector_mandate_detail: storage_model.connector_mandate_detail,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,10 @@ use api_models::{
|
|||||||
payments::{ExtendedCardInfo, GetAddressFromPaymentMethodData, PaymentsConfirmIntentRequest},
|
payments::{ExtendedCardInfo, GetAddressFromPaymentMethodData, PaymentsConfirmIntentRequest},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use common_utils::{ext_traits::Encode, types::keymanager::ToEncryptable};
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
use hyperswitch_domain_models::payments::PaymentConfirmData;
|
use hyperswitch_domain_models::payments::PaymentConfirmData;
|
||||||
|
use masking::PeekInterface;
|
||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
use tracing_futures::Instrument;
|
use tracing_futures::Instrument;
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ use crate::{
|
|||||||
types::{
|
types::{
|
||||||
self,
|
self,
|
||||||
api::{self, ConnectorCallType, PaymentIdTypeExt},
|
api::{self, ConnectorCallType, PaymentIdTypeExt},
|
||||||
domain::{self},
|
domain::{self, types as domain_types},
|
||||||
storage::{self, enums as storage_enums},
|
storage::{self, enums as storage_enums},
|
||||||
},
|
},
|
||||||
utils::{self, OptionExt},
|
utils::{self, OptionExt},
|
||||||
@ -176,12 +178,36 @@ impl<F: Send + Clone> GetTracker<F, PaymentConfirmData<F>, PaymentsConfirmIntent
|
|||||||
|
|
||||||
let cell_id = state.conf.cell_information.id.clone();
|
let cell_id = state.conf.cell_information.id.clone();
|
||||||
|
|
||||||
|
let batch_encrypted_data = domain_types::crypto_operation(
|
||||||
|
key_manager_state,
|
||||||
|
common_utils::type_name!(hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt),
|
||||||
|
domain_types::CryptoOperation::BatchEncrypt(
|
||||||
|
hyperswitch_domain_models::payments::payment_attempt::FromRequestEncryptablePaymentAttempt::to_encryptable(
|
||||||
|
hyperswitch_domain_models::payments::payment_attempt::FromRequestEncryptablePaymentAttempt {
|
||||||
|
payment_method_billing_address: request.payment_method_data.billing.as_ref().map(|address| address.clone().encode_to_value()).transpose().change_context(errors::ApiErrorResponse::InternalServerError).attach_printable("Failed to encode payment_method_billing address")?.map(masking::Secret::new),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
common_utils::types::keymanager::Identifier::Merchant(merchant_account.get_id().to_owned()),
|
||||||
|
key_store.key.peek(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.and_then(|val| val.try_into_batchoperation())
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed while encrypting payment intent details".to_string())?;
|
||||||
|
|
||||||
|
let encrypted_data =
|
||||||
|
hyperswitch_domain_models::payments::payment_attempt::FromRequestEncryptablePaymentAttempt::from_encryptable(batch_encrypted_data)
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed while encrypting payment intent details")?;
|
||||||
|
|
||||||
let payment_attempt_domain_model =
|
let payment_attempt_domain_model =
|
||||||
hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt::create_domain_model(
|
hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt::create_domain_model(
|
||||||
&payment_intent,
|
&payment_intent,
|
||||||
cell_id,
|
cell_id,
|
||||||
storage_scheme,
|
storage_scheme,
|
||||||
request
|
request,
|
||||||
|
encrypted_data
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,11 @@ use api_models::{enums::FrmSuggestion, payments::PaymentsCreateIntentRequest};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use common_utils::{
|
use common_utils::{
|
||||||
errors::CustomResult,
|
errors::CustomResult,
|
||||||
ext_traits::{AsyncExt, ValueExt},
|
ext_traits::{AsyncExt, Encode, ValueExt},
|
||||||
|
types::keymanager::ToEncryptable,
|
||||||
};
|
};
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
|
use masking::PeekInterface;
|
||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
|
|
||||||
use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
|
use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
|
||||||
@ -18,7 +20,8 @@ use crate::{
|
|||||||
routes::{app::ReqState, SessionState},
|
routes::{app::ReqState, SessionState},
|
||||||
services,
|
services,
|
||||||
types::{
|
types::{
|
||||||
api, domain,
|
api,
|
||||||
|
domain::{self, types as domain_types},
|
||||||
storage::{self, enums},
|
storage::{self, enums},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -100,51 +103,39 @@ impl<F: Send + Clone> GetTracker<F, payments::PaymentIntentData<F>, PaymentsCrea
|
|||||||
let key_manager_state = &state.into();
|
let key_manager_state = &state.into();
|
||||||
|
|
||||||
let storage_scheme = merchant_account.storage_scheme;
|
let storage_scheme = merchant_account.storage_scheme;
|
||||||
// Derivation of directly supplied Billing Address data in our Payment Create Request
|
|
||||||
// Encrypting our Billing Address Details to be stored in Payment Intent
|
|
||||||
let billing_address = request
|
|
||||||
.billing
|
|
||||||
.clone()
|
|
||||||
.async_map(|billing_details| {
|
|
||||||
create_encrypted_data(key_manager_state, key_store, billing_details)
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.transpose()
|
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
|
||||||
.attach_printable("Unable to encrypt billing details")?
|
|
||||||
.map(|encrypted_value| {
|
|
||||||
encrypted_value.deserialize_inner_value(|value| value.parse_value("Address"))
|
|
||||||
})
|
|
||||||
.transpose()
|
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
|
||||||
.attach_printable("Unable to deserialize decrypted value to Address")?;
|
|
||||||
|
|
||||||
// Derivation of directly supplied Shipping Address data in our Payment Create Request
|
let batch_encrypted_data = domain_types::crypto_operation(
|
||||||
// Encrypting our Shipping Address Details to be stored in Payment Intent
|
key_manager_state,
|
||||||
let shipping_address = request
|
common_utils::type_name!(hyperswitch_domain_models::payments::PaymentIntent),
|
||||||
.shipping
|
domain_types::CryptoOperation::BatchEncrypt(
|
||||||
.clone()
|
hyperswitch_domain_models::payments::FromRequestEncryptablePaymentIntent::to_encryptable(
|
||||||
.async_map(|shipping_details| {
|
hyperswitch_domain_models::payments::FromRequestEncryptablePaymentIntent {
|
||||||
create_encrypted_data(key_manager_state, key_store, shipping_details)
|
shipping_address: request.shipping.clone().map(|address| address.encode_to_value()).transpose().change_context(errors::ApiErrorResponse::InternalServerError).attach_printable("Failed to encode shipping address")?.map(masking::Secret::new),
|
||||||
})
|
billing_address: request.billing.clone().map(|address| address.encode_to_value()).transpose().change_context(errors::ApiErrorResponse::InternalServerError).attach_printable("Failed to encode billing address")?.map(masking::Secret::new),
|
||||||
.await
|
customer_details: None,
|
||||||
.transpose()
|
},
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
),
|
||||||
.attach_printable("Unable to encrypt shipping details")?
|
),
|
||||||
.map(|encrypted_value| {
|
common_utils::types::keymanager::Identifier::Merchant(merchant_account.get_id().to_owned()),
|
||||||
encrypted_value.deserialize_inner_value(|value| value.parse_value("Address"))
|
key_store.key.peek(),
|
||||||
})
|
)
|
||||||
.transpose()
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.and_then(|val| val.try_into_batchoperation())
|
||||||
.attach_printable("Unable to deserialize decrypted value to Address")?;
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed while encrypting payment intent details".to_string())?;
|
||||||
|
|
||||||
|
let encrypted_data =
|
||||||
|
hyperswitch_domain_models::payments::FromRequestEncryptablePaymentIntent::from_encryptable(batch_encrypted_data)
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed while encrypting payment intent details")?;
|
||||||
|
|
||||||
let payment_intent_domain =
|
let payment_intent_domain =
|
||||||
hyperswitch_domain_models::payments::PaymentIntent::create_domain_model_from_request(
|
hyperswitch_domain_models::payments::PaymentIntent::create_domain_model_from_request(
|
||||||
payment_id,
|
payment_id,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
profile,
|
profile,
|
||||||
request.clone(),
|
request.clone(),
|
||||||
billing_address,
|
encrypted_data,
|
||||||
shipping_address,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@ -947,11 +947,13 @@ where
|
|||||||
billing: payment_intent
|
billing: payment_intent
|
||||||
.billing_address
|
.billing_address
|
||||||
.clone()
|
.clone()
|
||||||
.map(|billing| billing.into_inner().expose()),
|
.map(|billing| billing.into_inner())
|
||||||
|
.map(From::from),
|
||||||
shipping: payment_intent
|
shipping: payment_intent
|
||||||
.shipping_address
|
.shipping_address
|
||||||
.clone()
|
.clone()
|
||||||
.map(|shipping| shipping.into_inner().expose()),
|
.map(|shipping| shipping.into_inner())
|
||||||
|
.map(From::from),
|
||||||
customer_id: payment_intent.customer_id.clone(),
|
customer_id: payment_intent.customer_id.clone(),
|
||||||
customer_present: payment_intent.customer_present.clone(),
|
customer_present: payment_intent.customer_present.clone(),
|
||||||
description: payment_intent.description.clone(),
|
description: payment_intent.description.clone(),
|
||||||
|
|||||||
@ -242,13 +242,17 @@ fn generate_to_encryptable(
|
|||||||
|
|
||||||
let inner_types = get_field_and_inner_types(&fields);
|
let inner_types = get_field_and_inner_types(&fields);
|
||||||
|
|
||||||
let inner_type = inner_types.first().map(|(_, ty)| ty).ok_or_else(|| {
|
let inner_type = inner_types.first().ok_or_else(|| {
|
||||||
syn::Error::new(
|
syn::Error::new(
|
||||||
proc_macro2::Span::call_site(),
|
proc_macro2::Span::call_site(),
|
||||||
"Please use the macro with attribute #[encrypt] on the fields you want to encrypt",
|
"Please use the macro with attribute #[encrypt] on the fields you want to encrypt",
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let provided_ty = get_encryption_ty_meta(&inner_type.0)
|
||||||
|
.map(|ty| ty.value.clone())
|
||||||
|
.unwrap_or(inner_type.1.clone());
|
||||||
|
|
||||||
let structs = struct_types.iter().map(|(prefix, struct_type)| {
|
let structs = struct_types.iter().map(|(prefix, struct_type)| {
|
||||||
let name = format_ident!("{}{}", prefix, struct_name);
|
let name = format_ident!("{}{}", prefix, struct_name);
|
||||||
let temp_fields = struct_type.generate_struct_fields(&inner_types);
|
let temp_fields = struct_type.generate_struct_fields(&inner_types);
|
||||||
@ -275,15 +279,15 @@ fn generate_to_encryptable(
|
|||||||
let decrypted_name = format_ident!("Decrypted{}", struct_name);
|
let decrypted_name = format_ident!("Decrypted{}", struct_name);
|
||||||
(
|
(
|
||||||
quote! { #decrypted_name },
|
quote! { #decrypted_name },
|
||||||
quote! { Secret<#inner_type> },
|
quote! { Secret<#provided_ty> },
|
||||||
quote! { Secret<#inner_type> },
|
quote! { Secret<#provided_ty> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StructType::Encrypted => {
|
StructType::Encrypted => {
|
||||||
let decrypted_name = format_ident!("Decrypted{}", struct_name);
|
let decrypted_name = format_ident!("Decrypted{}", struct_name);
|
||||||
(
|
(
|
||||||
quote! { #decrypted_name },
|
quote! { #decrypted_name },
|
||||||
quote! { Secret<#inner_type> },
|
quote! { Secret<#provided_ty> },
|
||||||
quote! { Encryption },
|
quote! { Encryption },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -291,8 +295,8 @@ fn generate_to_encryptable(
|
|||||||
let decrypted_update_name = format_ident!("DecryptedUpdate{}", struct_name);
|
let decrypted_update_name = format_ident!("DecryptedUpdate{}", struct_name);
|
||||||
(
|
(
|
||||||
quote! { #decrypted_update_name },
|
quote! { #decrypted_update_name },
|
||||||
quote! { Secret<#inner_type> },
|
quote! { Secret<#provided_ty> },
|
||||||
quote! { Secret<#inner_type> },
|
quote! { Secret<#provided_ty> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
//Unreachable statement
|
//Unreachable statement
|
||||||
|
|||||||
Reference in New Issue
Block a user