Skip to content

Commit

Permalink
feat(core): customer_details storage in payment_intent (#5007)
Browse files Browse the repository at this point in the history
Co-authored-by: Narayan Bhat <[email protected]>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 28, 2024
1 parent a172cba commit bb9a971
Show file tree
Hide file tree
Showing 20 changed files with 380 additions and 110 deletions.
4 changes: 1 addition & 3 deletions api-reference/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -8496,14 +8496,12 @@
},
"CustomerDetailsResponse": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "The identifier for the customer.",
"example": "cus_y3oqhf46pyzuxjbcn2giaqnb44",
"nullable": true,
"maxLength": 64,
"minLength": 1
},
Expand Down
6 changes: 3 additions & 3 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,11 @@ pub struct CustomerDetails {
pub phone_country_code: Option<String>,
}

#[derive(Debug, serde::Serialize, Clone, ToSchema, PartialEq)]
#[derive(Debug, Default, serde::Serialize, Clone, ToSchema, PartialEq, Setter)]
pub struct CustomerDetailsResponse {
/// The identifier for the customer.
#[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
pub id: id_type::CustomerId,
#[schema(value_type = Option<String>, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
pub id: Option<id_type::CustomerId>,

/// The customer's name
#[schema(max_length = 255, value_type = Option<String>, example = "John Doe")]
Expand Down
21 changes: 16 additions & 5 deletions crates/diesel_models/src/payment_intent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{enums as storage_enums, schema::payment_intent};
use crate::{encryption::Encryption, enums as storage_enums, schema::payment_intent};

#[derive(Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Identifiable, Queryable, Serialize, Deserialize)]
#[diesel(table_name = payment_intent, primary_key(payment_id, merchant_id))]
pub struct PaymentIntent {
pub payment_id: String,
Expand Down Expand Up @@ -59,10 +59,11 @@ pub struct PaymentIntent {
pub request_external_three_ds_authentication: Option<bool>,
pub charges: Option<pii::SecretSerdeValue>,
pub frm_metadata: Option<pii::SecretSerdeValue>,
pub customer_details: Option<Encryption>,
}

#[derive(
Clone, Debug, Eq, PartialEq, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize,
Clone, Debug, PartialEq, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize,
)]
#[diesel(table_name = payment_intent)]
pub struct PaymentIntentNew {
Expand Down Expand Up @@ -114,6 +115,7 @@ pub struct PaymentIntentNew {
pub request_external_three_ds_authentication: Option<bool>,
pub charges: Option<pii::SecretSerdeValue>,
pub frm_metadata: Option<pii::SecretSerdeValue>,
pub customer_details: Option<Encryption>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand All @@ -130,12 +132,13 @@ pub enum PaymentIntentUpdate {
metadata: pii::SecretSerdeValue,
updated_by: String,
},
ReturnUrlUpdate {
PaymentCreateUpdate {
return_url: Option<String>,
status: Option<storage_enums::IntentStatus>,
customer_id: Option<id_type::CustomerId>,
shipping_address_id: Option<String>,
billing_address_id: Option<String>,
customer_details: Option<Encryption>,
updated_by: String,
},
MerchantStatusUpdate {
Expand Down Expand Up @@ -171,6 +174,7 @@ pub enum PaymentIntentUpdate {
fingerprint_id: Option<String>,
request_external_three_ds_authentication: Option<bool>,
frm_metadata: Option<pii::SecretSerdeValue>,
customer_details: Option<Encryption>,
},
PaymentAttemptAndAttemptCountUpdate {
active_attempt_id: String,
Expand Down Expand Up @@ -246,6 +250,7 @@ pub struct PaymentIntentUpdateInternal {
pub fingerprint_id: Option<String>,
pub request_external_three_ds_authentication: Option<bool>,
pub frm_metadata: Option<pii::SecretSerdeValue>,
pub customer_details: Option<Encryption>,
}

impl PaymentIntentUpdate {
Expand Down Expand Up @@ -281,6 +286,7 @@ impl PaymentIntentUpdate {
fingerprint_id,
request_external_three_ds_authentication,
frm_metadata,
customer_details,
} = self.into();
PaymentIntent {
amount: amount.unwrap_or(source.amount),
Expand Down Expand Up @@ -318,6 +324,7 @@ impl PaymentIntentUpdate {
request_external_three_ds_authentication: request_external_three_ds_authentication
.or(source.request_external_three_ds_authentication),
frm_metadata: frm_metadata.or(source.frm_metadata),
customer_details: customer_details.or(source.customer_details),
..source
}
}
Expand Down Expand Up @@ -348,6 +355,7 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
fingerprint_id,
request_external_three_ds_authentication,
frm_metadata,
customer_details,
} => Self {
amount: Some(amount),
currency: Some(currency),
Expand All @@ -371,6 +379,7 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
fingerprint_id,
request_external_three_ds_authentication,
frm_metadata,
customer_details,
..Default::default()
},
PaymentIntentUpdate::MetadataUpdate {
Expand All @@ -382,19 +391,21 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
updated_by,
..Default::default()
},
PaymentIntentUpdate::ReturnUrlUpdate {
PaymentIntentUpdate::PaymentCreateUpdate {
return_url,
status,
customer_id,
shipping_address_id,
billing_address_id,
customer_details,
updated_by,
} => Self {
return_url,
status,
customer_id,
shipping_address_id,
billing_address_id,
customer_details,
modified_at: Some(common_utils::date_time::now()),
updated_by,
..Default::default()
Expand Down
1 change: 1 addition & 0 deletions crates/diesel_models/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ diesel::table! {
request_external_three_ds_authentication -> Nullable<Bool>,
charges -> Nullable<Jsonb>,
frm_metadata -> Nullable<Jsonb>,
customer_details -> Nullable<Bytea>,
}
}

Expand Down
6 changes: 4 additions & 2 deletions crates/hyperswitch_domain_models/src/payments.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use common_utils::{self, id_type, pii, types::MinorUnit};
use common_utils::{self, crypto::Encryptable, id_type, pii, types::MinorUnit};
use masking::Secret;
use time::PrimitiveDateTime;

pub mod payment_attempt;
Expand All @@ -9,7 +10,7 @@ use common_enums as storage_enums;
use self::payment_attempt::PaymentAttempt;
use crate::RemoteStorageObject;

#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
pub struct PaymentIntent {
pub payment_id: String,
pub merchant_id: String,
Expand Down Expand Up @@ -61,4 +62,5 @@ pub struct PaymentIntent {
pub request_external_three_ds_authentication: Option<bool>,
pub charges: Option<pii::SecretSerdeValue>,
pub frm_metadata: Option<pii::SecretSerdeValue>,
pub customer_details: Option<Encryptable<Secret<serde_json::Value>>>,
}
111 changes: 64 additions & 47 deletions crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ use common_utils::{
errors::{CustomResult, ValidationError},
types::MinorUnit,
};
use error_stack::ResultExt;
use masking::PeekInterface;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use super::PaymentIntent;
use crate::{
behaviour, errors,
mandates::{MandateDataType, MandateDetails},
type_encryption::{decrypt, AsyncLift},
ForeignIDRef, RemoteStorageObject,
};

Expand Down Expand Up @@ -472,7 +475,8 @@ impl ForeignIDRef for PaymentAttempt {
}

use diesel_models::{
PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew,
encryption::Encryption, PaymentIntent as DieselPaymentIntent,
PaymentIntentNew as DieselPaymentIntentNew,
};

#[async_trait::async_trait]
Expand Down Expand Up @@ -525,61 +529,73 @@ impl behaviour::Conversion for PaymentIntent {
request_external_three_ds_authentication: self.request_external_three_ds_authentication,
charges: self.charges,
frm_metadata: self.frm_metadata,
customer_details: self.customer_details.map(Encryption::from),
})
}

async fn convert_back(
storage_model: Self::DstType,
_key: &masking::Secret<Vec<u8>>,
key: &masking::Secret<Vec<u8>>,
) -> CustomResult<Self, ValidationError>
where
Self: Sized,
{
Ok(Self {
payment_id: storage_model.payment_id,
merchant_id: storage_model.merchant_id,
status: storage_model.status,
amount: storage_model.amount,
currency: storage_model.currency,
amount_captured: storage_model.amount_captured,
customer_id: storage_model.customer_id,
description: storage_model.description,
return_url: storage_model.return_url,
metadata: storage_model.metadata,
connector_id: storage_model.connector_id,
shipping_address_id: storage_model.shipping_address_id,
billing_address_id: storage_model.billing_address_id,
statement_descriptor_name: storage_model.statement_descriptor_name,
statement_descriptor_suffix: storage_model.statement_descriptor_suffix,
created_at: storage_model.created_at,
modified_at: storage_model.modified_at,
last_synced: storage_model.last_synced,
setup_future_usage: storage_model.setup_future_usage,
off_session: storage_model.off_session,
client_secret: storage_model.client_secret,
active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id),
business_country: storage_model.business_country,
business_label: storage_model.business_label,
order_details: storage_model.order_details,
allowed_payment_method_types: storage_model.allowed_payment_method_types,
connector_metadata: storage_model.connector_metadata,
feature_metadata: storage_model.feature_metadata,
attempt_count: storage_model.attempt_count,
profile_id: storage_model.profile_id,
merchant_decision: storage_model.merchant_decision,
payment_link_id: storage_model.payment_link_id,
payment_confirm_source: storage_model.payment_confirm_source,
updated_by: storage_model.updated_by,
surcharge_applicable: storage_model.surcharge_applicable,
request_incremental_authorization: storage_model.request_incremental_authorization,
incremental_authorization_allowed: storage_model.incremental_authorization_allowed,
authorization_count: storage_model.authorization_count,
fingerprint_id: storage_model.fingerprint_id,
session_expiry: storage_model.session_expiry,
request_external_three_ds_authentication: storage_model
.request_external_three_ds_authentication,
charges: storage_model.charges,
frm_metadata: storage_model.frm_metadata,
async {
let inner_decrypt = |inner| decrypt(inner, key.peek());
Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
payment_id: storage_model.payment_id,
merchant_id: storage_model.merchant_id,
status: storage_model.status,
amount: storage_model.amount,
currency: storage_model.currency,
amount_captured: storage_model.amount_captured,
customer_id: storage_model.customer_id,
description: storage_model.description,
return_url: storage_model.return_url,
metadata: storage_model.metadata,
connector_id: storage_model.connector_id,
shipping_address_id: storage_model.shipping_address_id,
billing_address_id: storage_model.billing_address_id,
statement_descriptor_name: storage_model.statement_descriptor_name,
statement_descriptor_suffix: storage_model.statement_descriptor_suffix,
created_at: storage_model.created_at,
modified_at: storage_model.modified_at,
last_synced: storage_model.last_synced,
setup_future_usage: storage_model.setup_future_usage,
off_session: storage_model.off_session,
client_secret: storage_model.client_secret,
active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id),
business_country: storage_model.business_country,
business_label: storage_model.business_label,
order_details: storage_model.order_details,
allowed_payment_method_types: storage_model.allowed_payment_method_types,
connector_metadata: storage_model.connector_metadata,
feature_metadata: storage_model.feature_metadata,
attempt_count: storage_model.attempt_count,
profile_id: storage_model.profile_id,
merchant_decision: storage_model.merchant_decision,
payment_link_id: storage_model.payment_link_id,
payment_confirm_source: storage_model.payment_confirm_source,
updated_by: storage_model.updated_by,
surcharge_applicable: storage_model.surcharge_applicable,
request_incremental_authorization: storage_model.request_incremental_authorization,
incremental_authorization_allowed: storage_model.incremental_authorization_allowed,
authorization_count: storage_model.authorization_count,
fingerprint_id: storage_model.fingerprint_id,
session_expiry: storage_model.session_expiry,
request_external_three_ds_authentication: storage_model
.request_external_three_ds_authentication,
charges: storage_model.charges,
frm_metadata: storage_model.frm_metadata,
customer_details: storage_model
.customer_details
.async_lift(inner_decrypt)
.await?,
})
}
.await
.change_context(ValidationError::InvalidValue {
message: "Failed while decrypting payment intent".to_string(),
})
}

Expand Down Expand Up @@ -628,6 +644,7 @@ impl behaviour::Conversion for PaymentIntent {
request_external_three_ds_authentication: self.request_external_three_ds_authentication,
charges: self.charges,
frm_metadata: self.frm_metadata,
customer_details: self.customer_details.map(Encryption::from),
})
}
}
Loading

0 comments on commit bb9a971

Please sign in to comment.