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:
Narayan Bhat
2024-11-26 13:47:12 +05:30
committed by GitHub
parent 03423a1f76
commit 108b1603fa
8 changed files with 307 additions and 85 deletions

View 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,
}
}
}

View File

@ -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;

View File

@ -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,

View File

@ -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,
}) })
} }

View File

@ -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?;

View File

@ -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?;

View File

@ -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(),

View File

@ -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